diff --git a/Makefile b/Makefile index 38ec5c7..1e45056 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) ifndef CPUS -CPUS := 1 +CPUS := 2 endif QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic QEMUOPTS += -initrd fs.img diff --git a/defs.h b/defs.h index 0e8cb52..2353e4a 100644 --- a/defs.h +++ b/defs.h @@ -130,12 +130,11 @@ void swtch(struct context*, struct context*); // spinlock.c void acquire(struct spinlock*); -void getcallerpcs(void*, uint64*); int holding(struct spinlock*); void initlock(struct spinlock*, char*); void release(struct spinlock*); -void pushcli(void); -void popcli(void); +void push_off(void); +void pop_off(void); // sleeplock.c void acquiresleep(struct sleeplock*); @@ -168,6 +167,7 @@ void timerinit(void); // trap.c extern uint ticks; void trapinit(void); +void trapinithart(void); extern struct spinlock tickslock; void usertrapret(void); @@ -179,6 +179,7 @@ int uartgetc(void); // vm.c void kvminit(void); +void kvminithart(void); pagetable_t uvmcreate(void); void uvminit(pagetable_t, char *, uint); uint64 uvmalloc(pagetable_t, uint64, uint64); @@ -194,6 +195,7 @@ int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 // plic.c void plicinit(void); +void plicinithart(void); uint64 plic_pending(void); int plic_claim(void); void plic_complete(int); diff --git a/main.c b/main.c index 24793cd..2168b9f 100644 --- a/main.c +++ b/main.c @@ -4,25 +4,39 @@ #include "riscv.h" #include "defs.h" +volatile static int started = 0; + // Bootstrap processor starts running C code here. // Allocate a real stack and switch to it, first // doing some setup required for memory allocator to work. void -main(int hartid) +main() { - w_tp(hartid); // save hartid where cpuid() can find it - uartinit(); // serial port - consoleinit(); - printf("entering main() on hart %d\n", hartid); - kinit(); // physical page allocator - kvminit(); // kernel page table - procinit(); // process table - trapinit(); // trap vectors - plicinit(); // set up interrupt controller - binit(); // buffer cache - fileinit(); // file table - ramdiskinit(); // disk - userinit(); // first user process + if(cpuid() == 0){ + uartinit(); // serial port + consoleinit(); + printf("hart %d starting\n", cpuid()); + kinit(); // physical page allocator + kvminit(); // create kernel page table + kvminithart(); // turn on paging + procinit(); // process table + trapinit(); // trap vectors + trapinithart(); // install kernel trap vector + plicinit(); // set up interrupt controller + plicinithart(); // ask PLIC for device interrupts + binit(); // buffer cache + fileinit(); // file table + ramdiskinit(); // disk + userinit(); // first user process + started = 1; + } else { + while(started == 0) + ; + printf("hart %d starting\n", cpuid()); + kvminithart(); // turn on paging + trapinithart(); // install kernel trap vector + plicinithart(); // ask PLIC for device interrupts + } scheduler(); } diff --git a/memlayout.h b/memlayout.h index 9c9cfdb..462986c 100644 --- a/memlayout.h +++ b/memlayout.h @@ -28,6 +28,14 @@ // qemu puts programmable interrupt controller here. #define PLIC 0x0c000000L +#define PLIC_PRIORITY (PLIC + 0x0) +#define PLIC_PENDING (PLIC + 0x1000) +#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) +#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) +#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) +#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) +#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) +#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) #define RAMDISK 0x88000000L diff --git a/proc.c b/proc.c index 766fd93..28cac26 100644 --- a/proc.c +++ b/proc.c @@ -360,7 +360,7 @@ scheduler(void) { struct proc *p; struct cpu *c = mycpu(); - + c->proc = 0; for(;;){ // Enable interrupts on this processor. @@ -385,7 +385,6 @@ scheduler(void) c->proc = 0; } release(&ptable.lock); - } } @@ -393,7 +392,7 @@ scheduler(void) // and have changed proc->state. Saves and restores // intena because intena is a property of this // kernel thread, not this CPU. It should -// be proc->intena and proc->ncli, but that would +// be proc->intena and proc->noff, but that would // break in the few places where a lock is held but // there's no process. void diff --git a/proc.h b/proc.h index 00a1cb9..278e4cd 100644 --- a/proc.h +++ b/proc.h @@ -22,9 +22,8 @@ struct context { struct cpu { struct proc *proc; // The process running on this cpu or null struct context scheduler; // swtch() here to enter scheduler - volatile uint started; // Has the CPU started? - int ncli; // Depth of pushcli nesting. - int intena; // Were interrupts enabled before pushcli? + int noff; // Depth of push_off() nesting. + int intena; // Were interrupts enabled before push_off()? }; extern struct cpu cpus[NCPU]; diff --git a/spinlock.c b/spinlock.c index 0377870..8b3c3f0 100644 --- a/spinlock.c +++ b/spinlock.c @@ -5,6 +5,7 @@ #include "memlayout.h" #include "spinlock.h" #include "riscv.h" +#include "proc.h" #include "defs.h" void @@ -15,27 +16,6 @@ initlock(struct spinlock *lk, char *name) lk->cpu = 0; } -void -acquire(struct spinlock *lk) -{ - lk->locked = 1; - lk->cpu = mycpu(); -} - -void -release(struct spinlock *lk) -{ - lk->locked = 0; - lk->cpu = 0; -} - -int -holding(struct spinlock *lk) -{ - return lk->locked && lk->cpu == mycpu(); -} - -#if 0 // Acquire the lock. // Loops (spins) until the lock is acquired. // Holding a lock for a long time may cause @@ -43,12 +23,14 @@ holding(struct spinlock *lk) void acquire(struct spinlock *lk) { - pushcli(); // disable interrupts to avoid deadlock. + push_off(); // disable interrupts to avoid deadlock. if(holding(lk)) panic("acquire"); // The xchg is atomic. - while(xchg(&lk->locked, 1) != 0) + //while(xchg(&lk->locked, 1) != 0) + // ; + while(__sync_lock_test_and_set(&lk->locked, 1) != 0) ; // Tell the C compiler and the processor to not move loads or stores @@ -58,7 +40,6 @@ acquire(struct spinlock *lk) // Record info about lock acquisition for holding() and debugging. lk->cpu = mycpu(); - getcallerpcs(&lk, lk->pcs); } // Release the lock. @@ -68,7 +49,6 @@ release(struct spinlock *lk) if(!holding(lk)) panic("release"); - lk->pcs[0] = 0; lk->cpu = 0; // Tell the C compiler and the processor to not move loads or stores @@ -81,27 +61,10 @@ release(struct spinlock *lk) // Release the lock, equivalent to lk->locked = 0. // This code can't use a C assignment, since it might // not be atomic. A real OS would use C atomics here. - asm volatile("movl $0, %0" : "+m" (lk->locked) : ); + //asm volatile("movl $0, %0" : "+m" (lk->locked) : ); + __sync_lock_release(&lk->locked); - popcli(); -} - -// Record the current call stack in pcs[] by following the %ebp chain. -void -getcallerpcs(void *v, uint64 pcs[]) -{ - uint64 *ebp; - int i; - - asm volatile("mov %%rbp, %0" : "=r" (ebp)); - for(i = 0; i < 10; i++){ - if(ebp == 0 || ebp < (uint64*)KERNBASE || ebp == (uint64*)0xffffffff) - break; - pcs[i] = ebp[1]; // saved %eip - ebp = (uint64*)ebp[0]; // saved %ebp - } - for(; i < 10; i++) - pcs[i] = 0; + pop_off(); } // Check whether this cpu is holding the lock. @@ -109,37 +72,37 @@ int holding(struct spinlock *lk) { int r; - pushcli(); + push_off(); r = lk->locked && lk->cpu == mycpu(); - popcli(); + pop_off(); return r; } - -// Pushcli/popcli are like cli/sti except that they are matched: -// it takes two popcli to undo two pushcli. Also, if interrupts -// are off, then pushcli, popcli leaves them off. +// push_off/pop_off are like intr_off()/intr_on() except that they are matched: +// it takes two pop_off to undo two push_off. Also, if interrupts +// are initially off, then push_off, pop_off leaves them off. void -pushcli(void) +push_off(void) { - int eflags; + struct cpu *c = mycpu(); + int old = intr_get(); - eflags = readeflags(); - cli(); - if(mycpu()->ncli == 0) - mycpu()->intena = eflags & FL_IF; - mycpu()->ncli += 1; + intr_off(); + if(c->noff == 0) + c->intena = old; + c->noff += 1; } void -popcli(void) +pop_off(void) { - if(readeflags()&FL_IF) - panic("popcli - interruptible"); - if(--mycpu()->ncli < 0) - panic("popcli"); - if(mycpu()->ncli == 0 && mycpu()->intena) - sti(); + struct cpu *c = mycpu(); + if(intr_get()) + panic("pop_off - interruptible"); + c->noff -= 1; + if(c->noff < 0) + panic("pop_off"); + if(c->noff == 0 && c->intena) + intr_on(); } -#endif diff --git a/spinlock.h b/spinlock.h index 90bffdb..4392820 100644 --- a/spinlock.h +++ b/spinlock.h @@ -5,7 +5,5 @@ struct spinlock { // For debugging: char *name; // Name of lock. struct cpu *cpu; // The cpu holding the lock. - uint64 pcs[10]; // The call stack (an array of program counters) - // that locked the lock. }; diff --git a/start.c b/start.c index 53edb8e..ce22be8 100644 --- a/start.c +++ b/start.c @@ -9,12 +9,12 @@ void main(); // entry.S needs one stack per CPU. __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; -// assembly code in kernelvec for machine-mode timer interrupt. -extern void machinevec(); - // scratch area for timer interrupt, one per CPU. uint64 mscratch0[NCPU * 32]; +// assembly code in kernelvec for machine-mode timer interrupt. +extern void machinevec(); + // entry.S jumps here in machine mode on stack0. void mstart() @@ -48,7 +48,9 @@ mstart() w_mstatus(r_mstatus() | MSTATUS_MIE); w_mie(r_mie() | MIE_MTIE); - // call main(hartid) in supervisor mode. - asm("csrr a0, mhartid ; \ - mret"); + // keep each CPU's hartid in its tp register, for cpuid(). + w_tp(id); + + // call main() in supervisor mode. + asm("mret"); } diff --git a/trap.c b/trap.c index 693c596..929f761 100644 --- a/trap.c +++ b/trap.c @@ -19,14 +19,16 @@ extern int devintr(); void trapinit(void) { - int i; - - // set up to take exceptions and traps while in the kernel. - w_stvec((uint64)kernelvec); - initlock(&tickslock, "time"); } +// set up to take exceptions and traps while in the kernel. +void +trapinithart(void) +{ + w_stvec((uint64)kernelvec); +} + // // handle an interrupt, exception, or system call from user space. // called from trampoline.S diff --git a/vm.c b/vm.c index b24b271..1d3887a 100644 --- a/vm.c +++ b/vm.c @@ -54,9 +54,13 @@ kvminit() // the highest virtual address in the kernel. mappages(kernel_pagetable, TRAMPOLINE, PGSIZE, (uint64)trampout, PTE_R | PTE_X); +} - // Switch h/w page table register to the kernel's page table, - // and enable paging. +// Switch h/w page table register to the kernel's page table, +// and enable paging. +void +kvminithart() +{ w_satp(MAKE_SATP(kernel_pagetable)); }