diff --git a/bootasm.S b/bootasm.S index 56175ce..f5d1678 100644 --- a/bootasm.S +++ b/bootasm.S @@ -13,7 +13,7 @@ .code16 # Assemble for 16-bit mode .globl start start: - cli # Disable interrupts + cli # BIOS enabled interrupts ; disable # Set up the important data segment registers (DS, ES, SS). xorw %ax,%ax # Segment number zero @@ -45,7 +45,8 @@ seta20.2: # Switch from real to protected mode, using a bootstrap GDT # and segment translation that makes virtual addresses # identical to physical addresses, so that the - # effective memory map does not change during the switch. + # effective memory map does not change after subsequent + # loads of segment registers. lgdt gdtdesc movl %cr0, %eax orl $CR0_PE, %eax @@ -57,7 +58,11 @@ seta20.2: # default to 32 bits after this jump. ljmp $(SEG_KCODE<<3), $start32 -.code32 # Assemble for 32-bit mode +# tell the assembler to generate 0x66 prefixes for 16-bit +# instructions like movw, and to generate 32-bit immediate +# addresses. +.code32 + start32: # Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax # Our data segment selector diff --git a/bootother.S b/bootother.S index 899669a..186873e 100644 --- a/bootother.S +++ b/bootother.S @@ -9,80 +9,69 @@ # Because this code sets DS to zero, it must sit # at an address in the low 2^16 bytes. # -# Bootothers (in main.c) sends the STARTUPs, one at a time. -# It puts this code (start) at 0x7000. -# It puts the correct %esp in start-4, -# and the place to jump to in start-8. +# Bootothers (in main.c) sends the STARTUPs one at a time. +# It copies this code (start) at 0x7000. +# It puts the address of a newly allocated per-core stack in start-4, +# and the address of the place to jump to (mpmain) in start-8. # # This code is identical to bootasm.S except: # - it does not need to enable A20 # - it uses the address at start-4 for the %esp # - it jumps to the address at start-8 instead of calling bootmain -#define SEG_KCODE 1 // kernel code -#define SEG_KDATA 2 // kernel data+stack +#define SEG_KCODE 1 +#define SEG_KDATA 2 -#define CR0_PE 1 // protected mode enable bit +#define CR0_PE 1 -.code16 # Assemble for 16-bit mode +.code16 .globl start start: - cli # Disable interrupts + cli - # Set up the important data segment registers (DS, ES, SS). - xorw %ax,%ax # Segment number zero - movw %ax,%ds # -> Data Segment - movw %ax,%es # -> Extra Segment - movw %ax,%ss # -> Stack Segment + xorw %ax,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss //PAGEBREAK! - # Switch from real to protected mode, using a bootstrap GDT - # and segment translation that makes virtual addresses - # identical to physical addresses, so that the - # effective memory map does not change during the switch. lgdt gdtdesc movl %cr0, %eax orl $CR0_PE, %eax movl %eax, %cr0 - # This ljmp is how you load the CS (Code Segment) register. - # SEG_ASM produces segment descriptors with the 32-bit mode - # flag set (the D flag), so addresses and word operands will - # default to 32 bits after this jump. ljmp $(SEG_KCODE<<3), $start32 -.code32 # Assemble for 32-bit mode +.code32 start32: - # Set up the protected-mode data segment registers - movw $(SEG_KDATA<<3), %ax # Our data segment selector - movw %ax, %ds # -> DS: Data Segment - movw %ax, %es # -> ES: Extra Segment - movw %ax, %ss # -> SS: Stack Segment - movw $0, %ax # Zero segments not ready for use - movw %ax, %fs # -> FS - movw %ax, %gs # -> GS + movw $(SEG_KDATA<<3), %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw $0, %ax + movw %ax, %fs + movw %ax, %gs - # Set up the stack pointer and call into C. + # switch to the stack allocated by bootothers() movl start-4, %esp + + # call mpmain() call *(start-8) - # If the call returns (it shouldn't), trigger a Bochs - # breakpoint if running under Bochs, then loop. - movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 + movw $0x8a00, %ax movw %ax, %dx outw %ax, %dx - movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 + movw $0x8ae0, %ax outw %ax, %dx spin: jmp spin -# Bootstrap GDT -.p2align 2 # force 4 byte alignment +.p2align 2 gdt: - SEG_NULLASM # null seg - SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg - SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg + SEG_NULLASM + SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) + SEG_ASM(STA_W, 0x0, 0xffffffff) gdtdesc: - .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 - .long gdt # address gdt + .word (gdtdesc - gdt - 1) + .long gdt diff --git a/defs.h b/defs.h index 93a8bc6..7408d4d 100644 --- a/defs.h +++ b/defs.h @@ -152,7 +152,7 @@ void uartintr(void); void uartputc(int); // vm.c -void ksegment(void); +void seginit(void); void kvmalloc(void); void vmenable(void); pde_t* setupkvm(void); diff --git a/main.c b/main.c index 8ddf261..beac4da 100644 --- a/main.c +++ b/main.c @@ -11,16 +11,14 @@ void jkstack(void) __attribute__((noreturn)); void mainc(void); // 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. int main(void) { mpinit(); // collect info about this machine lapicinit(mpbcpu()); - ksegment(); // set up segments - picinit(); // interrupt controller - ioapicinit(); // another interrupt controller - consoleinit(); // I/O devices & their interrupts - uartinit(); // serial port + seginit(); // set up segments kinit(); // initialize memory allocator jkstack(); // call mainc() on a properly-allocated stack } @@ -37,10 +35,16 @@ jkstack(void) panic("jkstack"); } +// Set up hardware and software. +// Runs only on the boostrap processor. void mainc(void) { cprintf("\ncpu%d: starting xv6\n\n", cpu->id); + picinit(); // interrupt controller + ioapicinit(); // another interrupt controller + consoleinit(); // I/O devices & their interrupts + uartinit(); // serial port kvmalloc(); // initialize the kernel page table pinit(); // process table tvinit(); // trap vectors @@ -64,16 +68,17 @@ static void mpmain(void) { if(cpunum() != mpbcpu()) { - ksegment(); + seginit(); lapicinit(cpunum()); } vmenable(); // turn on paging cprintf("cpu%d: starting\n", cpu->id); idtinit(); // load idt register - xchg(&cpu->booted, 1); + xchg(&cpu->booted, 1); // tell bootothers() we're up scheduler(); // start running processes } +// Start the non-boot processors. static void bootothers(void) { @@ -91,10 +96,13 @@ bootothers(void) if(c == cpus+cpunum()) // We've started already. continue; - // Fill in %esp, %eip and start code on cpu. + // Tell bootother.S what stack to use and the address of mpmain; + // it expects to find these two addresses stored just before + // its first instruction. stack = kalloc(); *(void**)(code-4) = stack + KSTACKSIZE; *(void**)(code-8) = mpmain; + lapicstartap(c->id, (uint)code); // Wait for cpu to finish mpmain() diff --git a/proc.c b/proc.c index 6f7bdb6..2e8a0a4 100644 --- a/proc.c +++ b/proc.c @@ -65,7 +65,8 @@ procdump(void) //PAGEBREAK: 32 // Look in the process table for an UNUSED proc. -// If found, change state to EMBRYO and return it. +// If found, change state to EMBRYO and initialize +// state required to run in the kernel. // Otherwise return 0. static struct proc* allocproc(void) @@ -97,7 +98,7 @@ found: p->tf = (struct trapframe*)sp; // Set up new context to start executing at forkret, - // which returns to trapret (see below). + // which returns to trapret. sp -= 4; *(uint*)sp = (uint)trapret; @@ -105,6 +106,7 @@ found: p->context = (struct context*)sp; memset(p->context, 0, sizeof *p->context); p->context->eip = (uint)forkret; + return p; } diff --git a/proc.h b/proc.h index 4a80a28..7ffaffb 100644 --- a/proc.h +++ b/proc.h @@ -11,7 +11,7 @@ // Per-CPU state struct cpu { uchar id; // Local APIC ID; index into cpus[] below - struct context *scheduler; // Switch here to enter scheduler + struct context *scheduler; // swtch() here to enter scheduler struct taskstate ts; // Used by x86 to find stack for interrupt struct segdesc gdt[NSEGS]; // x86 global descriptor table volatile uint booted; // Has the CPU started? @@ -20,7 +20,7 @@ struct cpu { // Cpu-local storage variables; see below struct cpu *cpu; - struct proc *proc; + struct proc *proc; // The currently-running process. }; extern struct cpu cpus[NCPU]; @@ -29,13 +29,13 @@ extern int ncpu; // Per-CPU variables, holding pointers to the // current cpu and to the current process. // The asm suffix tells gcc to use "%gs:0" to refer to cpu -// and "%gs:4" to refer to proc. ksegment sets up the +// and "%gs:4" to refer to proc. seginit sets up the // %gs segment register so that %gs refers to the memory // holding those two variables in the local cpu's struct cpu. // This is similar to how thread-local variables are implemented // in thread libraries such as Linux pthreads. -extern struct cpu *cpu asm("%gs:0"); // This cpu. -extern struct proc *proc asm("%gs:4"); // Current proc on this cpu. +extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()] +extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc //PAGEBREAK: 17 // Saved registers for kernel context switches. @@ -61,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; // Per-process state struct proc { uint sz; // Size of process memory (bytes) - pde_t* pgdir; // Linear address of proc's pgdir + pde_t* pgdir; // Page table char *kstack; // Bottom of kernel stack for this process enum procstate state; // Process state volatile int pid; // Process ID struct proc *parent; // Parent process struct trapframe *tf; // Trap frame for current syscall - struct context *context; // Switch here to run process + struct context *context; // swtch() here to run process void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed struct file *ofile[NOFILE]; // Open files diff --git a/spinlock.c b/spinlock.c index 68cfbe9..281748a 100644 --- a/spinlock.c +++ b/spinlock.c @@ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name) void acquire(struct spinlock *lk) { - pushcli(); + pushcli(); // disable interrupts to avoid deadlock. if(holding(lk)) panic("acquire"); diff --git a/vm.c b/vm.c index 5c6f943..c57fa53 100644 --- a/vm.c +++ b/vm.c @@ -13,7 +13,7 @@ static pde_t *kpgdir; // for use in scheduler() // Set up CPU's kernel segment descriptors. // Run once at boot time on each CPU. void -ksegment(void) +seginit(void) { struct cpu *c;