From 0f0456ec53d2739282eccf62b22f7c933d7cbae5 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 15:01:26 -0400 Subject: Infer TOOLPREFIX, just like in JOS --- Makefile | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3ddc1c1..71f28b9 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,27 @@ OBJS = \ vm.o\ # Cross-compiling (e.g., on Mac OS X) -TOOLPREFIX = i386-jos-elf- +#TOOLPREFIX = i386-jos-elf- # Using native tools (e.g., on X86 Linux) #TOOLPREFIX = +# Try to infer the correct TOOLPREFIX if not set +ifndef TOOLPREFIX +TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ + then echo 'i386-jos-elf-'; \ + elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ + then echo ''; \ + else echo "***" 1>&2; \ + echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ + echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ + echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ + echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ + echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ + echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ + echo "***" 1>&2; exit 1; fi) +endif + CC = $(TOOLPREFIX)gcc AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld -- cgit v1.2.3 From 1e8035be535781e2b317881ab421ac33c7de0299 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 15:05:27 -0400 Subject: Infer qemu path, just like in JOS --- Makefile | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 71f28b9..11b78c3 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,23 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d echo "***" 1>&2; exit 1; fi) endif +# If the makefile can't find QEMU, specify its path here +#QEMU = + +# Try to infer the correct QEMU +ifndef QEMU +QEMU = $(shell if which qemu > /dev/null; \ + then echo qemu; exit; \ + else \ + qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ + if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ + echo "***" 1>&2; \ + echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ + echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ + echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ + echo "***" 1>&2; exit 1) +endif + CC = $(TOOLPREFIX)gcc AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld @@ -159,27 +176,27 @@ bochs : fs.img xv6.img # try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) # QEMU's gdb stub command line changed in 0.11 -QEMUGDB = $(shell if qemu -help | grep -q '^-gdb'; \ +QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) QEMUOPTS = -smp 2 -hdb fs.img xv6.img qemu: fs.img xv6.img - qemu -serial mon:stdio $(QEMUOPTS) + $(QEMU) -serial mon:stdio $(QEMUOPTS) qemu-nox: fs.img xv6.img - qemu -nographic $(QEMUOPTS) + $(QEMU) -nographic $(QEMUOPTS) .gdbinit: .gdbinit.tmpl sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ qemu-gdb: fs.img xv6.img .gdbinit @echo "*** Now run 'gdb'." 1>&2 - qemu -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) + $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) qemu-nox-gdb: fs.img xv6.img .gdbinit @echo "*** Now run 'gdb'." 1>&2 - qemu -nographic $(QEMUOPTS) -S $(QEMUGDB) + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) # CUT HERE # prepare dist for students -- cgit v1.2.3 From 880ee18ab14477b669a6b95707721322bfe086c1 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 15:11:10 -0400 Subject: mkfs must be compiled 32-bit. Fix mkfs warnings --- Makefile | 2 +- mkfs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 11b78c3..8f820e1 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,7 @@ _forktest: forktest.o $(ULIB) $(OBJDUMP) -S _forktest > forktest.asm mkfs: mkfs.c fs.h - gcc -Wall -o mkfs mkfs.c + gcc -m32 -Werror -Wall -o mkfs mkfs.c UPROGS=\ _cat\ diff --git a/mkfs.c b/mkfs.c index 3a3c62a..a970b5c 100644 --- a/mkfs.c +++ b/mkfs.c @@ -82,7 +82,7 @@ main(int argc, char *argv[]) usedblocks = ninodes / IPB + 3 + bitblocks; freeblock = usedblocks; - printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, + printf("used %d (bit %d ninode %u) free %u total %d\n", usedblocks, bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); assert(nblocks + usedblocks == size); @@ -230,7 +230,7 @@ balloc(int used) for(i = 0; i < used; i++) { buf[i/8] = buf[i/8] | (0x1 << (i%8)); } - printf("balloc: write bitmap block at sector %lu\n", ninodes/IPB + 3); + printf("balloc: write bitmap block at sector %u\n", ninodes/IPB + 3); wsect(ninodes / IPB + 3, buf); } -- cgit v1.2.3 From 8d774afb2d45592d85725474a704ac99b0624d2c Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 31 Aug 2010 15:39:25 -0400 Subject: no more pminit, or ELF header at 0x10000 kinit() knows about end and PHYSTOP map all of kernel read/write (rather than r/o instructions) thanks, austin --- defs.h | 3 +-- kalloc.c | 8 +++++--- main.c | 2 +- mkfs.c | 4 ++-- vm.c | 45 ++++----------------------------------------- 5 files changed, 13 insertions(+), 49 deletions(-) diff --git a/defs.h b/defs.h index ca7367d..dca91bb 100644 --- a/defs.h +++ b/defs.h @@ -62,7 +62,7 @@ void ioapicinit(void); // kalloc.c char* kalloc(void); void kfree(char*); -void kinit(char*,uint); +void kinit(); // kbd.c void kbdintr(void); @@ -151,7 +151,6 @@ void uartintr(void); void uartputc(int); // vm.c -void pminit(void); void ksegment(void); void kvmalloc(void); void vmenable(void); diff --git a/kalloc.c b/kalloc.c index 65de759..8c9ff93 100644 --- a/kalloc.c +++ b/kalloc.c @@ -19,11 +19,13 @@ struct { // Initialize free list of physical pages. void -kinit(char *p, uint len) +kinit(void) { + extern char end[]; + initlock(&kmem.lock, "kmem"); - char *p1 = (char*)PGROUNDUP((uint)p); - char *p2 = PGROUNDDOWN(p + len); + char *p1 = (char*)PGROUNDUP((uint)end); + char *p2 = PGROUNDDOWN(PHYSTOP); for( ; p1 < p2; p1 += 4096) kfree(p1); } diff --git a/main.c b/main.c index 878ea36..cb6e6ce 100644 --- a/main.c +++ b/main.c @@ -21,7 +21,7 @@ main(void) ioapicinit(); // another interrupt controller consoleinit(); // I/O devices & their interrupts uartinit(); // serial port - pminit(); // discover how much memory there is + kinit(); // initialize memory allocator jkstack(); // call mainc() on a properly-allocated stack } diff --git a/mkfs.c b/mkfs.c index a970b5c..3a3c62a 100644 --- a/mkfs.c +++ b/mkfs.c @@ -82,7 +82,7 @@ main(int argc, char *argv[]) usedblocks = ninodes / IPB + 3 + bitblocks; freeblock = usedblocks; - printf("used %d (bit %d ninode %u) free %u total %d\n", usedblocks, + printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); assert(nblocks + usedblocks == size); @@ -230,7 +230,7 @@ balloc(int used) for(i = 0; i < used; i++) { buf[i/8] = buf[i/8] | (0x1 << (i%8)); } - printf("balloc: write bitmap block at sector %u\n", ninodes/IPB + 3); + printf("balloc: write bitmap block at sector %lu\n", ninodes/IPB + 3); wsect(ninodes / IPB + 3, buf); } diff --git a/vm.c b/vm.c index 46d18fc..8a57ca9 100644 --- a/vm.c +++ b/vm.c @@ -17,8 +17,8 @@ // setupkvm() and exec() set up every page table like this: // 0..640K : user memory (text, data, stack, heap) // 640K..1M : mapped direct (for IO space) -// 1M..kernend : mapped direct (for the kernel's text and data) -// kernend..PHYSTOP : mapped direct (kernel heap and user pages) +// 1M..end : mapped direct (for the kernel's text and data) +// end..PHYSTOP : mapped direct (kernel heap and user pages) // 0xfe000000..0 : mapped direct (devices such as ioapic) // // The kernel allocates memory for its heap and for user memory @@ -31,12 +31,6 @@ #define USERTOP 0xA0000 -static uint kerntext; // Linker starts kernel at 1MB -static uint kerntsz; -static uint kerndata; -static uint kerndsz; -static uint kernend; -static uint freesz; static pde_t *kpgdir; // for use in scheduler() // return the address of the PTE in page table pgdir @@ -161,14 +155,8 @@ setupkvm(void) // Map IO space from 640K to 1Mbyte if (!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) return 0; - // Map kernel text read-only - if (!mappages(pgdir, (void *) kerntext, kerntsz, kerntext, 0)) - return 0; - // Map kernel data read/write - if (!mappages(pgdir, (void *) kerndata, kerndsz, kerndata, PTE_W)) - return 0; - // Map dynamically-allocated memory read/write (kernel stacks, user mem) - if (!mappages(pgdir, (void *) kernend, freesz, PADDR(kernend), PTE_W)) + // Map kernel and free memory pool + if (!mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W)) return 0; // Map devices such as ioapic, lapic, ... if (!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) @@ -333,31 +321,6 @@ copyuvm(pde_t *pgdir, uint sz) return d; } -// Gather information about physical memory layout. -// Called once during boot. -// Really should find out how much physical memory -// there is rather than assuming PHYSTOP. -void -pminit(void) -{ - extern char end[]; - struct proghdr *ph; - struct elfhdr *elf = (struct elfhdr*)0x10000; // scratch space - - if (elf->magic != ELF_MAGIC || elf->phnum != 2) - panic("pminit: need a text and data segment\n"); - - ph = (struct proghdr*)((uchar*)elf + elf->phoff); - kernend = ((uint)end + PGSIZE) & ~(PGSIZE-1); - kerntext = ph[0].va; - kerndata = ph[1].va; - kerntsz = ph[0].memsz; - kerndsz = ph[1].memsz; - freesz = PHYSTOP - kernend; - - kinit((char *)kernend, freesz); -} - // Allocate one page table for the machine for the kernel address // space for scheduler processes. void -- cgit v1.2.3 From 1aee6a6c4f1227dd4737ba5c88306acfba893828 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:01:38 -0400 Subject: Hopefully get the printf size spec right, regardless of compiler. --- mkfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkfs.c b/mkfs.c index 3a3c62a..5f572cf 100644 --- a/mkfs.c +++ b/mkfs.c @@ -82,7 +82,7 @@ main(int argc, char *argv[]) usedblocks = ninodes / IPB + 3 + bitblocks; freeblock = usedblocks; - printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, + printf("used %d (bit %d ninode %zu) free %u total %d\n", usedblocks, bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); assert(nblocks + usedblocks == size); @@ -230,7 +230,7 @@ balloc(int used) for(i = 0; i < used; i++) { buf[i/8] = buf[i/8] | (0x1 << (i%8)); } - printf("balloc: write bitmap block at sector %lu\n", ninodes/IPB + 3); + printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3); wsect(ninodes / IPB + 3, buf); } -- cgit v1.2.3 From 7b814133e42568b2eca3362731cc0968f6e034b0 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:13:20 -0400 Subject: Acknowledge patches --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index 286d336..c46ec2a 100644 --- a/README +++ b/README @@ -20,6 +20,9 @@ The following people made contributions: Cliff Frey (MP) Xiao Yu (MP) +In addition, we are grateful for the patches submitted by Greg Price, +Yandong Mao, and Hitoshi Mitake. + The code in the files that constitute xv6 is Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. -- cgit v1.2.3 From 04be8fb232ef0f1f52444b0e513a9a256c5a031f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:18:44 -0400 Subject: Rebalance TOC --- runoff.list | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runoff.list b/runoff.list index 3258398..01147ea 100644 --- a/runoff.list +++ b/runoff.list @@ -23,7 +23,6 @@ proc.c swtch.S kalloc.c vm.c - # system calls traps.h vectors.pl @@ -46,8 +45,6 @@ file.c sysfile.c exec.c - - # pipes pipe.c @@ -73,3 +70,5 @@ init.c sh.c + + -- cgit v1.2.3 From 7472b2b451f100162fa4542f5bfe260385f861ad Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:26:08 -0400 Subject: Fix too-long lines --- proc.c | 3 ++- trap.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/proc.c b/proc.c index 5ac2780..0dc77b8 100644 --- a/proc.c +++ b/proc.c @@ -120,7 +120,8 @@ userinit(void) panic("userinit: out of memory?"); if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) panic("userinit: out of memory?"); - inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size); + inituvm(p->pgdir, 0x0, _binary_initcode_start, + (int)_binary_initcode_size); p->sz = PGROUNDUP((int)_binary_initcode_size); memset(p->tf, 0, sizeof(*p->tf)); p->tf->cs = (SEG_UCODE << 3) | DPL_USER; diff --git a/trap.c b/trap.c index daee22f..ae26739 100644 --- a/trap.c +++ b/trap.c @@ -83,7 +83,8 @@ trap(struct trapframe *tf) panic("trap"); } // In user space, assume process misbehaved. - cprintf("pid %d %s: trap %d err %d on cpu %d eip 0x%x addr 0x%x--kill proc\n", + cprintf("pid %d %s: trap %d err %d on cpu %d " + "eip 0x%x addr 0x%x--kill proc\n", proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, rcr2()); proc->killed = 1; -- cgit v1.2.3 From 37ee75f42e9d35a96b84fe0c95479178cd41efac Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:42:05 -0400 Subject: Rearrange for better page breaking --- x86.h | 85 ++++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/x86.h b/x86.h index b9fa8b8..71427b3 100644 --- a/x86.h +++ b/x86.h @@ -90,25 +90,36 @@ readeflags(void) return eflags; } -static inline uint -xchg(volatile uint *addr, uint newval) -{ - uint result; - - // The + in "+m" denotes a read-modify-write operand. - asm volatile("lock; xchgl %0, %1" : - "+m" (*addr), "=a" (result) : - "1" (newval) : - "cc"); - return result; -} - static inline void loadgs(ushort v) { asm volatile("movw %0, %%gs" : : "r" (v)); } +static inline void lebp(uint val) +{ + asm volatile("movl %0,%%ebp" : : "r" (val)); +} + +static inline uint rebp(void) +{ + uint val; + asm volatile("movl %%ebp,%0" : "=r" (val)); + return val; +} + +static inline void lesp(uint val) +{ + asm volatile("movl %0,%%esp" : : "r" (val)); +} + +static inline uint resp(void) +{ + uint val; + asm volatile("movl %%esp,%0" : "=r" (val)); + return val; +} + static inline void cli(void) { @@ -121,6 +132,25 @@ sti(void) asm volatile("sti"); } +static inline void nop_pause(void) +{ + asm volatile("pause" : :); +} + +//PAGEBREAK! +static inline uint +xchg(volatile uint *addr, uint newval) +{ + uint result; + + // The + in "+m" denotes a read-modify-write operand. + asm volatile("lock; xchgl %0, %1" : + "+m" (*addr), "=a" (result) : + "1" (newval) : + "cc"); + return result; +} + static inline void lcr0(uint val) { asm volatile("movl %0,%%cr0" : : "r" (val)); @@ -152,35 +182,6 @@ static inline uint rcr3(void) return val; } -static inline void lebp(uint val) -{ - asm volatile("movl %0,%%ebp" : : "r" (val)); -} - -static inline uint rebp(void) -{ - uint val; - asm volatile("movl %%ebp,%0" : "=r" (val)); - return val; -} - -static inline void lesp(uint val) -{ - asm volatile("movl %0,%%esp" : : "r" (val)); -} - -static inline uint resp(void) -{ - uint val; - asm volatile("movl %%esp,%0" : "=r" (val)); - return val; -} - -static inline void nop_pause(void) -{ - asm volatile("pause" : :); -} - //PAGEBREAK: 36 // Layout of the trap frame built on the stack by the // hardware and by trapasm.S, and passed to trap(). -- cgit v1.2.3 From 92639b6ba95d1d960a9e808c7163f6d171b2e4a3 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:43:41 -0400 Subject: Follow xv6 code style. Also fixes indexing for these functions --- x86.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/x86.h b/x86.h index 71427b3..1f903b2 100644 --- a/x86.h +++ b/x86.h @@ -132,7 +132,8 @@ sti(void) asm volatile("sti"); } -static inline void nop_pause(void) +static inline void +nop_pause(void) { asm volatile("pause" : :); } @@ -151,31 +152,36 @@ xchg(volatile uint *addr, uint newval) return result; } -static inline void lcr0(uint val) +static inline void +lcr0(uint val) { asm volatile("movl %0,%%cr0" : : "r" (val)); } -static inline uint rcr0(void) +static inline uint +rcr0(void) { uint val; asm volatile("movl %%cr0,%0" : "=r" (val)); return val; } -static inline uint rcr2(void) +static inline uint +rcr2(void) { uint val; asm volatile("movl %%cr2,%0" : "=r" (val)); return val; } -static inline void lcr3(uint val) +static inline void +lcr3(uint val) { asm volatile("movl %0,%%cr3" : : "r" (val)); } -static inline uint rcr3(void) +static inline uint +rcr3(void) { uint val; asm volatile("movl %%cr3,%0" : "=r" (val)); -- cgit v1.2.3 From b5592b4d2f0ea5f536b206979f829038c55d7ade Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:47:50 -0400 Subject: Start PDE defs on new page --- mmu.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mmu.h b/mmu.h index db40f25..0975efd 100644 --- a/mmu.h +++ b/mmu.h @@ -62,8 +62,6 @@ struct segdesc { #define STA_R 0x2 // Readable (executable segments) #define STA_A 0x1 // Accessed -// - // System segment type bits #define STS_T16A 0x1 // Available 16-bit TSS #define STS_LDT 0x2 // Local Descriptor Table @@ -78,7 +76,7 @@ struct segdesc { #define STS_IG32 0xE // 32-bit Interrupt Gate #define STS_TG32 0xF // 32-bit Trap Gate - +//PAGEBREAK! // A linear address 'la' has a three-part structure as follows: // // +--------10------+-------10-------+---------12----------+ -- cgit v1.2.3 From 7914ab721436d3c21623010dfab2dc326bf49279 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 16:54:50 -0400 Subject: Page breaking of mmu.h --- mmu.h | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/mmu.h b/mmu.h index 0975efd..475eae8 100644 --- a/mmu.h +++ b/mmu.h @@ -24,6 +24,20 @@ #define FL_VIP 0x00100000 // Virtual Interrupt Pending #define FL_ID 0x00200000 // ID flag +// Control Register flags +#define CR0_PE 0x00000001 // Protection Enable +#define CR0_MP 0x00000002 // Monitor coProcessor +#define CR0_EM 0x00000004 // Emulation +#define CR0_TS 0x00000008 // Task Switched +#define CR0_ET 0x00000010 // Extension Type +#define CR0_NE 0x00000020 // Numeric Errror +#define CR0_WP 0x00010000 // Write Protect +#define CR0_AM 0x00040000 // Alignment Mask +#define CR0_NW 0x20000000 // Not Writethrough +#define CR0_CD 0x40000000 // Cache Disable +#define CR0_PG 0x80000000 // Paging + +//PAGEBREAK! // Segment Descriptor struct segdesc { uint lim_15_0 : 16; // Low bits of segment limit @@ -46,7 +60,6 @@ struct segdesc { { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } - #define SEG16(type, base, lim, dpl) (struct segdesc) \ { (lim) & 0xffff, (uint)(base) & 0xffff, \ ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ @@ -76,7 +89,6 @@ struct segdesc { #define STS_IG32 0xE // 32-bit Interrupt Gate #define STS_TG32 0xF // 32-bit Trap Gate -//PAGEBREAK! // A linear address 'la' has a three-part structure as follows: // // +--------10------+-------10-------+---------12----------+ @@ -128,21 +140,6 @@ struct segdesc { typedef uint pte_t; -// Control Register flags -#define CR0_PE 0x00000001 // Protection Enable -#define CR0_MP 0x00000002 // Monitor coProcessor -#define CR0_EM 0x00000004 // Emulation -#define CR0_TS 0x00000008 // Task Switched -#define CR0_ET 0x00000010 // Extension Type -#define CR0_NE 0x00000020 // Numeric Errror -#define CR0_WP 0x00010000 // Write Protect -#define CR0_AM 0x00040000 // Alignment Mask -#define CR0_NW 0x20000000 // Not Writethrough -#define CR0_CD 0x40000000 // Cache Disable -#define CR0_PG 0x80000000 // Paging - - -// PAGEBREAK: 40 // Task state segment format struct taskstate { uint link; // Old ts selector -- cgit v1.2.3 From 29c054df817d55ae6e0fc3bd4c9e2343a2b4ca75 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:07:54 -0400 Subject: We don't use lesp/lebp and using them at all from C would be fraught with peril. Keep resp/rebp, but fix their code style. --- x86.h | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/x86.h b/x86.h index 1f903b2..33e240d 100644 --- a/x86.h +++ b/x86.h @@ -96,24 +96,16 @@ loadgs(ushort v) asm volatile("movw %0, %%gs" : : "r" (v)); } -static inline void lebp(uint val) -{ - asm volatile("movl %0,%%ebp" : : "r" (val)); -} - -static inline uint rebp(void) +static inline uint +rebp(void) { uint val; asm volatile("movl %%ebp,%0" : "=r" (val)); return val; } -static inline void lesp(uint val) -{ - asm volatile("movl %0,%%esp" : : "r" (val)); -} - -static inline uint resp(void) +static inline uint +resp(void) { uint val; asm volatile("movl %%esp,%0" : "=r" (val)); @@ -132,13 +124,6 @@ sti(void) asm volatile("sti"); } -static inline void -nop_pause(void) -{ - asm volatile("pause" : :); -} - -//PAGEBREAK! static inline uint xchg(volatile uint *addr, uint newval) { @@ -152,6 +137,13 @@ xchg(volatile uint *addr, uint newval) return result; } +static inline void +nop_pause(void) +{ + asm volatile("pause" : :); +} + +//PAGEBREAK! static inline void lcr0(uint val) { -- cgit v1.2.3 From 51e2a7b324020a90bf963d514a0959d930120d71 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:30:31 -0400 Subject: print depends on runoff.spec --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8f820e1..37924e2 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ clean: # make a printout FILES = $(shell grep -v '^\#' runoff.list) -PRINT = runoff.list $(FILES) +PRINT = runoff.list runoff.spec $(FILES) xv6.pdf: $(PRINT) ./runoff -- cgit v1.2.3 From c7ceb71d5757db21a2228ec33bde2f4da1677094 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:33:04 -0400 Subject: Sheet 1 is a right page now because of the (much) longer cross-ref --- runoff | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/runoff b/runoff index 21ee8ed..0e0e49e 100755 --- a/runoff +++ b/runoff @@ -89,16 +89,17 @@ perl -e ' print STDERR "Have no toc for $file\n"; next; } - # this assumes that sheet 1 of code is a left page - # double-check the PDF + # this assumes that sheet 1 of code is a right page + # double-check the PDF. swap the two regexps below + # otherwise. if(!$leftwarn++) { - print STDERR "assuming that sheet 1 is a left page. double-check!\n"; + print STDERR "assuming that sheet 1 is a right page. double-check!\n"; } - if($what eq "left" && !($toc{$file} =~ /^\d[13579]0/)){ + if($what eq "left" && !($toc{$file} =~ /^\d[02468]0/)){ print STDERR "$file does not start on a fresh left page [$toc{$file}]\n"; } # why does this not work if I inline $x in the if? - $x = ($toc{$file} =~ /^\d[02468]0/); + $x = ($toc{$file} =~ /^\d[13579]0/); if($what eq "right" && !$x){ print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; } -- cgit v1.2.3 From 6a6bf37c3c3dcab6f464d031bf94c56a770b6df6 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:35:17 -0400 Subject: Swap bootmain.c and bootother.S. This puts the whole boot process on one spread, though it separates the two asm files. --- runoff.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runoff.list b/runoff.list index 01147ea..025bbe2 100644 --- a/runoff.list +++ b/runoff.list @@ -9,8 +9,8 @@ elf.h # startup bootasm.S -bootother.S bootmain.c +bootother.S main.c # locks -- cgit v1.2.3 From fd462b6a01735cc7a14b9cb11c99242c5ed03c03 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:40:21 -0400 Subject: Got the meat of spinlocks on one spread --- runoff.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/runoff.spec b/runoff.spec index e4cfd42..46dcc09 100644 --- a/runoff.spec +++ b/runoff.spec @@ -17,6 +17,7 @@ even: main.c # spinlock.h either # spinlock.c either +right: spinlock.c # mild preference even: proc.h # mild preference # goal is to have two action-packed 2-page spreads, -- cgit v1.2.3 From 13b3f4d2e32eee81ba039ad3c6a6c97bfc9d4638 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:46:30 -0400 Subject: Make it possible to express half-page preferences --- runoff | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/runoff b/runoff index 0e0e49e..14db252 100755 --- a/runoff +++ b/runoff @@ -82,9 +82,10 @@ perl -e ' next; } - if(/(left|right): (.*)/){ + if(/(left|right)(\+?): (.*)/){ $what = $1; - $file = $2; + $tens = ($2 eq "+" ? "5" : "0"); + $file = $3; if(!defined($toc{$file})){ print STDERR "Have no toc for $file\n"; next; @@ -95,11 +96,11 @@ perl -e ' if(!$leftwarn++) { print STDERR "assuming that sheet 1 is a right page. double-check!\n"; } - if($what eq "left" && !($toc{$file} =~ /^\d[02468]0/)){ + if($what eq "left" && !($toc{$file} =~ /^\d[02468]$tens/)){ print STDERR "$file does not start on a fresh left page [$toc{$file}]\n"; } # why does this not work if I inline $x in the if? - $x = ($toc{$file} =~ /^\d[13579]0/); + $x = ($toc{$file} =~ /^\d[13579]$tens/); if($what eq "right" && !$x){ print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; } -- cgit v1.2.3 From 87b2099ae44039b119267d41a710a56b4784dbe0 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:49:47 -0400 Subject: Our proc.c alignment is perfect. Since ksegment/usegment went away, we get a column back, so we get our two action-packed spreads. --- runoff.spec | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runoff.spec b/runoff.spec index 46dcc09..e6f5a95 100644 --- a/runoff.spec +++ b/runoff.spec @@ -16,16 +16,15 @@ even: main.c # odd: init.c # spinlock.h either -# spinlock.c either right: spinlock.c # mild preference even: proc.h # mild preference # goal is to have two action-packed 2-page spreads, # one with -# ksegment usegment allocproc userinit growproc fork +# allocproc userinit growproc fork # and another with # scheduler sched yield forkret sleep wakeup1 wakeup -right: proc.c # VERY important +right+: proc.c # VERY important # setjmp.S either # vm.c either -- cgit v1.2.3 From 5048762c7e27789a014cc1e74e1002e749c924ce Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 17:52:03 -0400 Subject: Page break kalloc.c --- kalloc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kalloc.c b/kalloc.c index 8c9ff93..7653242 100644 --- a/kalloc.c +++ b/kalloc.c @@ -30,6 +30,7 @@ kinit(void) kfree(p1); } +//PAGEBREAK: 21 // Free the page of physical memory pointed at by v, // which normally should have been returned by a // call to kalloc(). (The exception is when -- cgit v1.2.3 From 3597d5dc704c192a85b9902f7264fe9025aad277 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 31 Aug 2010 19:21:33 -0400 Subject: oops. last minute simplicifaction to kalloc(). --- kalloc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kalloc.c b/kalloc.c index 8c9ff93..43e80e8 100644 --- a/kalloc.c +++ b/kalloc.c @@ -24,10 +24,9 @@ kinit(void) extern char end[]; initlock(&kmem.lock, "kmem"); - char *p1 = (char*)PGROUNDUP((uint)end); - char *p2 = PGROUNDDOWN(PHYSTOP); - for( ; p1 < p2; p1 += 4096) - kfree(p1); + char *p = (char*)PGROUNDUP((uint)end); + for( ; p + PGSIZE - 1 < (char*) PHYSTOP; p += PGSIZE) + kfree(p); } // Free the page of physical memory pointed at by v, -- cgit v1.2.3 From dd645ef11911596d73d128c1da53579678c56aee Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 31 Aug 2010 21:49:49 -0400 Subject: Cleanup if allocuvm fails to alloc. Add a test. --- README | 4 ++-- usertests.c | 36 ++++++++++++++++++++++++++++++++++++ vm.c | 3 ++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/README b/README index c46ec2a..3ea05a7 100644 --- a/README +++ b/README @@ -20,8 +20,8 @@ The following people made contributions: Cliff Frey (MP) Xiao Yu (MP) -In addition, we are grateful for the patches submitted by Greg Price, -Yandong Mao, and Hitoshi Mitake. +In addition, we are grateful for the patches contributed by Greg +Price, Yandong Mao, and Hitoshi Mitake. The code in the files that constitute xv6 is Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. diff --git a/usertests.c b/usertests.c index 670a4a8..7ec03fc 100644 --- a/usertests.c +++ b/usertests.c @@ -324,6 +324,7 @@ mem(void) void *m1, *m2; int pid, ppid; + printf(1, "mem test\n"); ppid = getpid(); if((pid = fork()) == 0){ m1 = 0; @@ -1332,6 +1333,41 @@ sbrktest(void) wait(); } + // if we run the system out of memory, does it clean up the last + // failed allocation? + sbrk(-(sbrk(0) - oldbrk)); + int pids[32]; + int fds[2]; + if(pipe(fds) != 0){ + printf(1, "pipe() failed\n"); + exit(); + } + for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if ((pids[i] = fork()) == 0) { + // allocate the full 640K + sbrk((640 * 1024) - (uint)sbrk(0)); + write(fds[1], "x", 1); + // sit around until killed + while (1) sleep(1000); + } + char scratch; + if (pids[i] != -1) + read(fds[0], &scratch, 1); + } + // if those failed allocations freed up the pages they did allocate, + // we'll be able to allocate here + c = sbrk(4096); + for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if (pids[i] == -1) + continue; + kill(pids[i]); + wait(); + } + if (c == (char*)0xffffffff) { + printf(stdout, "failed sbrk leaked memory\n"); + exit(); + } + if(sbrk(0) > oldbrk) sbrk(-(sbrk(0) - oldbrk)); diff --git a/vm.c b/vm.c index 8a57ca9..95eeca8 100644 --- a/vm.c +++ b/vm.c @@ -195,7 +195,8 @@ allocuvm(pde_t *pgdir, char *addr, uint sz) if(pte == 0 || (*pte & PTE_P) == 0){ char *mem = kalloc(); if(mem == 0){ - // XXX clean up? + cprintf("allocuvm out of memory\n"); + deallocuvm(pgdir, addr, sz); return 0; } memset(mem, 0, PGSIZE); -- cgit v1.2.3 From ba04b3e70e4f1916c777d62de53088d96b09579f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 00:27:12 -0400 Subject: Fix similar leak in copyuvm --- vm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vm.c b/vm.c index 95eeca8..a60c915 100644 --- a/vm.c +++ b/vm.c @@ -313,13 +313,17 @@ copyuvm(pde_t *pgdir, uint sz) if(*pte & PTE_P){ pa = PTE_ADDR(*pte); if (!(mem = kalloc())) - return 0; + goto bad; memmove(mem, (char *)pa, PGSIZE); if (!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) - return 0; + goto bad; } } return d; + +bad: + freevm(d); + return 0; } // Allocate one page table for the machine for the kernel address -- cgit v1.2.3 From 5efca9054f1911e206831e16c2ca5ac8c8fc7c12 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 00:32:27 -0400 Subject: Tab police --- console.c | 4 ++-- exec.c | 10 +++++----- trap.c | 2 +- vm.c | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/console.c b/console.c index 16d0e7a..1d51a3c 100644 --- a/console.c +++ b/console.c @@ -18,8 +18,8 @@ static void consputc(int); static int panicked = 0; static struct { - struct spinlock lock; - int locking; + struct spinlock lock; + int locking; } cons; static void diff --git a/exec.c b/exec.c index 4f11695..39530aa 100644 --- a/exec.c +++ b/exec.c @@ -30,7 +30,7 @@ exec(char *path, char **argv) if(elf.magic != ELF_MAGIC) goto bad; - if (!(pgdir = setupkvm())) + if(!(pgdir = setupkvm())) goto bad; // Load program into memory. @@ -41,11 +41,11 @@ exec(char *path, char **argv) continue; if(ph.memsz < ph.filesz) goto bad; - if (!allocuvm(pgdir, (char *)ph.va, ph.memsz)) + if(!allocuvm(pgdir, (char *)ph.va, ph.memsz)) goto bad; if(ph.va + ph.memsz > sz) sz = ph.va + ph.memsz; - if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz)) + if(!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz)) goto bad; } iunlockput(ip); @@ -53,7 +53,7 @@ exec(char *path, char **argv) // Allocate and initialize stack at sz sz = PGROUNDUP(sz); sz += PGSIZE; // leave an invalid page - if (!allocuvm(pgdir, (char *)sz, PGSIZE)) + if(!allocuvm(pgdir, (char *)sz, PGSIZE)) goto bad; mem = uva2ka(pgdir, (char *)sz); spoffset = sz; @@ -105,7 +105,7 @@ exec(char *path, char **argv) return 0; bad: - if (pgdir) freevm(pgdir); + if(pgdir) freevm(pgdir); iunlockput(ip); return -1; } diff --git a/trap.c b/trap.c index ae26739..b2b2ebb 100644 --- a/trap.c +++ b/trap.c @@ -86,7 +86,7 @@ trap(struct trapframe *tf) cprintf("pid %d %s: trap %d err %d on cpu %d " "eip 0x%x addr 0x%x--kill proc\n", proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, - rcr2()); + rcr2()); proc->killed = 1; } diff --git a/vm.c b/vm.c index a60c915..c3601a4 100644 --- a/vm.c +++ b/vm.c @@ -244,13 +244,13 @@ freevm(pde_t *pgdir) if (da != 0) { pte_t *pgtab = (pte_t*) da; for (j = 0; j < NPTENTRIES; j++) { - if (pgtab[j] != 0) { - uint pa = PTE_ADDR(pgtab[j]); - uint va = PGADDR(i, j, 0); - if (va < USERTOP) // user memory + if (pgtab[j] != 0) { + uint pa = PTE_ADDR(pgtab[j]); + uint va = PGADDR(i, j, 0); + if (va < USERTOP) // user memory kfree((void *) pa); - pgtab[j] = 0; - } + pgtab[j] = 0; + } } kfree((void *) da); pgdir[i] = 0; @@ -287,7 +287,7 @@ inituvm(pde_t *pgdir, char *addr, char *init, uint sz) for (i = 0; i < sz; i += PGSIZE) { if (!(pte = walkpgdir(pgdir, (void *)(i+addr), 0))) - panic("inituvm: pte should exist\n"); + panic("inituvm: pte should exist\n"); off = (i+(uint)addr) % PGSIZE; pa = PTE_ADDR(*pte); if (sz - i < PGSIZE) n = sz - i; -- cgit v1.2.3 From b0751a3e9bfce88cb07c1a540ceabf21f2d53b31 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 00:41:25 -0400 Subject: Space police --- console.c | 2 +- main.c | 2 +- proc.c | 10 +++++----- stressfs.c | 6 +++--- usertests.c | 14 +++++++------- vm.c | 56 ++++++++++++++++++++++++++++---------------------------- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/console.c b/console.c index 1d51a3c..a3cdf96 100644 --- a/console.c +++ b/console.c @@ -163,7 +163,7 @@ consputc(int c) ; } - if (c == BACKSPACE) { + if(c == BACKSPACE){ uartputc('\b'); uartputc(' '); uartputc('\b'); diff --git a/main.c b/main.c index cb6e6ce..8ddf261 100644 --- a/main.c +++ b/main.c @@ -29,7 +29,7 @@ void jkstack(void) { char *kstack = kalloc(); - if (!kstack) + if(!kstack) panic("jkstack\n"); char *top = kstack + PGSIZE; asm volatile("movl %0,%%esp" : : "r" (top)); diff --git a/proc.c b/proc.c index 0dc77b8..375b1bb 100644 --- a/proc.c +++ b/proc.c @@ -116,9 +116,9 @@ userinit(void) p = allocproc(); initproc = p; - if (!(p->pgdir = setupkvm())) + if(!(p->pgdir = setupkvm())) panic("userinit: out of memory?"); - if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) + if(!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) panic("userinit: out of memory?"); inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size); @@ -144,10 +144,10 @@ int growproc(int n) { if(n > 0){ - if (!allocuvm(proc->pgdir, (char *)proc->sz, n)) + if(!allocuvm(proc->pgdir, (char *)proc->sz, n)) return -1; } else if(n < 0){ - if (!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) + if(!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) return -1; } proc->sz += n; @@ -169,7 +169,7 @@ fork(void) return -1; // Copy process state from p. - if (!(np->pgdir = copyuvm(proc->pgdir, proc->sz))) { + if(!(np->pgdir = copyuvm(proc->pgdir, proc->sz))){ kfree(np->kstack); np->kstack = 0; np->state = UNUSED; diff --git a/stressfs.c b/stressfs.c index 21a5d16..43f312b 100644 --- a/stressfs.c +++ b/stressfs.c @@ -17,8 +17,8 @@ main(int argc, char *argv[]) int i; printf(1, "stressfs starting\n"); - for (i = 0; i < 4; i++) { - if (fork() > 0) { + for(i = 0; i < 4; i++){ + if(fork() > 0){ break; } } @@ -28,7 +28,7 @@ main(int argc, char *argv[]) char path[] = "stressfs0"; path[8] += i; int fd = open(path, O_CREATE | O_RDWR); - for (i = 0; i < 100; i++) + for(i = 0; i < 100; i++) printf(fd, "%d\n", i); close(fd); diff --git a/usertests.c b/usertests.c index 7ec03fc..946565f 100644 --- a/usertests.c +++ b/usertests.c @@ -1342,28 +1342,28 @@ sbrktest(void) printf(1, "pipe() failed\n"); exit(); } - for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ - if ((pids[i] = fork()) == 0) { + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if((pids[i] = fork()) == 0) { // allocate the full 640K sbrk((640 * 1024) - (uint)sbrk(0)); write(fds[1], "x", 1); // sit around until killed - while (1) sleep(1000); + for(;;) sleep(1000); } char scratch; - if (pids[i] != -1) + if(pids[i] != -1) read(fds[0], &scratch, 1); } // if those failed allocations freed up the pages they did allocate, // we'll be able to allocate here c = sbrk(4096); - for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ - if (pids[i] == -1) + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if(pids[i] == -1) continue; kill(pids[i]); wait(); } - if (c == (char*)0xffffffff) { + if(c == (char*)0xffffffff) { printf(stdout, "failed sbrk leaked memory\n"); exit(); } diff --git a/vm.c b/vm.c index c3601a4..2f2b238 100644 --- a/vm.c +++ b/vm.c @@ -44,9 +44,9 @@ walkpgdir(pde_t *pgdir, const void *va, int create) pte_t *pgtab; pde = &pgdir[PDX(va)]; - if (*pde & PTE_P) { + if(*pde & PTE_P){ pgtab = (pte_t*) PTE_ADDR(*pde); - } else if (!create || !(r = (uint) kalloc())) + } else if(!create || !(r = (uint) kalloc())) return 0; else { pgtab = (pte_t*) r; @@ -127,7 +127,7 @@ switchuvm(struct proc *p) cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; ltr(SEG_TSS << 3); - if (p->pgdir == 0) + if(p->pgdir == 0) panic("switchuvm: no pgdir\n"); lcr3(PADDR(p->pgdir)); // switch to new address space @@ -149,17 +149,17 @@ setupkvm(void) pde_t *pgdir; // Allocate page directory - if (!(pgdir = (pde_t *) kalloc())) + if(!(pgdir = (pde_t *) kalloc())) return 0; memset(pgdir, 0, PGSIZE); // Map IO space from 640K to 1Mbyte - if (!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) + if(!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) return 0; // Map kernel and free memory pool - if (!mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W)) + if(!mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W)) return 0; // Map devices such as ioapic, lapic, ... - if (!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) + if(!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) return 0; return pgdir; } @@ -172,7 +172,7 @@ char* uva2ka(pde_t *pgdir, char *uva) { pte_t *pte = walkpgdir(pgdir, uva, 0); - if (pte == 0) return 0; + if(pte == 0) return 0; uint pa = PTE_ADDR(*pte); return (char *)pa; } @@ -185,7 +185,7 @@ uva2ka(pde_t *pgdir, char *uva) int allocuvm(pde_t *pgdir, char *addr, uint sz) { - if (addr + sz > (char*)USERTOP) + if(addr + sz > (char*)USERTOP) return 0; char *first = PGROUNDDOWN(addr); char *last = PGROUNDDOWN(addr + sz - 1); @@ -212,7 +212,7 @@ allocuvm(pde_t *pgdir, char *addr, uint sz) int deallocuvm(pde_t *pgdir, char *addr, uint sz) { - if (addr + sz > (char*)USERTOP) + if(addr + sz > (char*)USERTOP) return 0; char *first = (char*) PGROUNDUP((uint)addr); char *last = PGROUNDDOWN(addr + sz - 1); @@ -237,17 +237,17 @@ freevm(pde_t *pgdir) { uint i, j, da; - if (!pgdir) + if(!pgdir) panic("freevm: no pgdir\n"); - for (i = 0; i < NPDENTRIES; i++) { + for(i = 0; i < NPDENTRIES; i++){ da = PTE_ADDR(pgdir[i]); - if (da != 0) { + if(da != 0){ pte_t *pgtab = (pte_t*) da; - for (j = 0; j < NPTENTRIES; j++) { - if (pgtab[j] != 0) { + for(j = 0; j < NPTENTRIES; j++){ + if(pgtab[j] != 0){ uint pa = PTE_ADDR(pgtab[j]); uint va = PGADDR(i, j, 0); - if (va < USERTOP) // user memory + if(va < USERTOP) // user memory kfree((void *) pa); pgtab[j] = 0; } @@ -265,13 +265,13 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) uint i, pa, n; pte_t *pte; - if ((uint)addr % PGSIZE != 0) + if((uint)addr % PGSIZE != 0) panic("loaduvm: addr must be page aligned\n"); - for (i = 0; i < sz; i += PGSIZE) { - if (!(pte = walkpgdir(pgdir, addr+i, 0))) + for(i = 0; i < sz; i += PGSIZE){ + if(!(pte = walkpgdir(pgdir, addr+i, 0))) panic("loaduvm: address should exist\n"); pa = PTE_ADDR(*pte); - if (sz - i < PGSIZE) n = sz - i; + if(sz - i < PGSIZE) n = sz - i; else n = PGSIZE; if(readi(ip, (char *)pa, offset+i, n) != n) return 0; @@ -285,12 +285,12 @@ inituvm(pde_t *pgdir, char *addr, char *init, uint sz) uint i, pa, n, off; pte_t *pte; - for (i = 0; i < sz; i += PGSIZE) { - if (!(pte = walkpgdir(pgdir, (void *)(i+addr), 0))) + for(i = 0; i < sz; i += PGSIZE){ + if(!(pte = walkpgdir(pgdir, (void *)(i+addr), 0))) panic("inituvm: pte should exist\n"); off = (i+(uint)addr) % PGSIZE; pa = PTE_ADDR(*pte); - if (sz - i < PGSIZE) n = sz - i; + if(sz - i < PGSIZE) n = sz - i; else n = PGSIZE; memmove((char *)pa+off, init+i, n); } @@ -306,16 +306,16 @@ copyuvm(pde_t *pgdir, uint sz) uint pa, i; char *mem; - if (!d) return 0; - for (i = 0; i < sz; i += PGSIZE) { - if (!(pte = walkpgdir(pgdir, (void *)i, 0))) + if(!d) return 0; + for(i = 0; i < sz; i += PGSIZE){ + if(!(pte = walkpgdir(pgdir, (void *)i, 0))) panic("copyuvm: pte should exist\n"); if(*pte & PTE_P){ pa = PTE_ADDR(*pte); - if (!(mem = kalloc())) + if(!(mem = kalloc())) goto bad; memmove(mem, (char *)pa, PGSIZE); - if (!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) + if(!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) goto bad; } } -- cgit v1.2.3 From 41c4bbb505f4389a5c1660b407cd4073d7a72aab Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 02:14:35 -0400 Subject: Make it easy to change the number of CPUs --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 37924e2..30cec7c 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,10 @@ GDBPORT = $(shell expr `id -u` % 5000 + 25000) QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) -QEMUOPTS = -smp 2 -hdb fs.img xv6.img +ifndef CPUS +CPUS := 2 +endif +QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) qemu: fs.img xv6.img $(QEMU) -serial mon:stdio $(QEMUOPTS) -- cgit v1.2.3 From b1d41d678888fd1a51e4844ab583f7c47f9fb218 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 16:46:37 -0400 Subject: Remove the stack guard page. Processes are now contiguous from 0 to proc->sz, which means our syscall argument validation is correct. Add a pointer validation test and remove the stack test, which tested for the guard page. --- exec.c | 1 - syscall.c | 5 +---- usertests.c | 60 +++++++++++++++++++++++++++++++++++++++++------------------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/exec.c b/exec.c index 39530aa..c518c04 100644 --- a/exec.c +++ b/exec.c @@ -52,7 +52,6 @@ exec(char *path, char **argv) // Allocate and initialize stack at sz sz = PGROUNDUP(sz); - sz += PGSIZE; // leave an invalid page if(!allocuvm(pgdir, (char *)sz, PGSIZE)) goto bad; mem = uva2ka(pgdir, (char *)sz); diff --git a/syscall.c b/syscall.c index 9296cff..f8aeae7 100644 --- a/syscall.c +++ b/syscall.c @@ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip) return 0; } -// XXX should we copy the string? - // Fetch the nul-terminated string at addr from process p. // Doesn't actually copy the string - just sets *pp to point at it. // Returns length of string, not including nul. @@ -62,8 +60,7 @@ argptr(int n, char **pp, int size) return -1; if((uint)i >= proc->sz || (uint)i+size >= proc->sz) return -1; - // *pp = proc->mem + i; // XXXXX - *pp = (char *) i; // XXXXX + *pp = (char *) i; return 0; } diff --git a/usertests.c b/usertests.c index 946565f..77495bf 100644 --- a/usertests.c +++ b/usertests.c @@ -3,6 +3,8 @@ #include "user.h" #include "fs.h" #include "fcntl.h" +#include "syscall.h" +#include "traps.h" char buf[2048]; char name[3]; @@ -1375,26 +1377,46 @@ sbrktest(void) } void -stacktest(void) +validateint(int *p) { - printf(stdout, "stack test\n"); - char dummy = 1; - char *p = &dummy; - int ppid = getpid(); - int pid = fork(); - if(pid < 0){ - printf(stdout, "fork failed\n"); - exit(); - } - if(pid == 0){ - // should cause a trap: - p[-4096] = 'z'; - kill(ppid); - printf(stdout, "stack test failed: page before stack was writeable\n"); - exit(); + int res; + asm("mov %%esp, %%ebx\n\t" + "mov %3, %%esp\n\t" + "int %2\n\t" + "mov %%ebx, %%esp" : + "=a" (res) : + "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) : + "ebx"); +} + +void +validatetest(void) +{ + int hi = 1100*1024; + + printf(stdout, "validate test\n"); + + uint p; + for (p = 0; p <= (uint)hi; p += 4096) { + int pid; + if ((pid = fork()) == 0) { + // try to crash the kernel by passing in a badly placed integer + validateint((int*)p); + exit(); + } + sleep(0); + sleep(0); + kill(pid); + wait(); + + // try to crash the kernel by passing in a bad string pointer + if (link("nosuchfile", (char*)p) != -1) { + printf(stdout, "link should not succeed\n"); + exit(); + } } - wait(); - printf(stdout, "stack test OK\n"); + + printf(stdout, "validate ok\n"); } int @@ -1408,8 +1430,8 @@ main(int argc, char *argv[]) } close(open("usertests.ran", O_CREATE)); - stacktest(); sbrktest(); + validatetest(); opentest(); writetest(); -- cgit v1.2.3 From d3ecf3eb44379f12b47da9d08e4d1b52cf7c2601 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 1 Sep 2010 17:14:58 -0400 Subject: Slight simplification of copyuvm. We could simplify other things now that processes are contiguous, but we'd have to think harder about the error paths. --- vm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vm.c b/vm.c index 2f2b238..8cfac14 100644 --- a/vm.c +++ b/vm.c @@ -310,14 +310,14 @@ copyuvm(pde_t *pgdir, uint sz) for(i = 0; i < sz; i += PGSIZE){ if(!(pte = walkpgdir(pgdir, (void *)i, 0))) panic("copyuvm: pte should exist\n"); - if(*pte & PTE_P){ - pa = PTE_ADDR(*pte); - if(!(mem = kalloc())) - goto bad; - memmove(mem, (char *)pa, PGSIZE); - if(!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) - goto bad; - } + if(!(*pte & PTE_P)) + panic("copyuvm: page not present\n"); + pa = PTE_ADDR(*pte); + if(!(mem = kalloc())) + goto bad; + memmove(mem, (char *)pa, PGSIZE); + if(!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) + goto bad; } return d; -- cgit v1.2.3 From 4d245c79f989aff781a50c855d254b813639ee90 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 03:25:33 -0400 Subject: Add Nickolai and myself and scrunch two lines out of the text elsewhere --- README | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README b/README index 3ea05a7..22f5c4e 100644 --- a/README +++ b/README @@ -19,6 +19,8 @@ The following people made contributions: Russ Cox (context switching, locking) Cliff Frey (MP) Xiao Yu (MP) + Nickolai Zeldovich + Austin Clements In addition, we are grateful for the patches contributed by Greg Price, Yandong Mao, and Hitoshi Mitake. @@ -42,9 +44,7 @@ Then run "make TOOLPREFIX=i386-jos-elf-". To run xv6, you can use Bochs or QEMU, both PC simulators. Bochs makes debugging easier, but QEMU is much faster. To run in Bochs, run "make bochs" and then type "c" at the bochs prompt. -To run in QEMU, run "make qemu". Both log the xv6 screen output to -standard output. +To run in QEMU, run "make qemu". -To create a typeset version of the code, run "make xv6.pdf". -This requires the "mpage" text formatting utility. -See http://www.mesa.nl/pub/mpage/. +To create a typeset version of the code, run "make xv6.pdf". This +requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/. -- cgit v1.2.3 From 1280d2680274cf44aa1a9531f06f7699d1f9051f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 03:36:39 -0400 Subject: Oops, missed a comment about the stack guard --- proc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/proc.h b/proc.h index 7d97dfa..8799668 100644 --- a/proc.h +++ b/proc.h @@ -48,7 +48,6 @@ struct proc { // Process memory is laid out contiguously, low addresses first: // text // original data and bss -// invalid page // fixed-size stack // expandable heap -- cgit v1.2.3 From 0fa1ab632f6e328321cdec779e66ed07cac89d04 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 03:56:36 -0400 Subject: Pack five columns into cross-ref. This saves us an entire page and fixes the single dangling line. --- runoff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runoff b/runoff index 14db252..1435646 100755 --- a/runoff +++ b/runoff @@ -191,7 +191,7 @@ do uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` if [ "x$defs" != "x$uses" ]; then echo $i $defs - echo $uses |fmt -24 | sed 's/^/ /' + echo $uses |fmt -29 | sed 's/^/ /' fi done ) >refs -- cgit v1.2.3 From 9d7a63e0734b4c7abe5ae00eda302e7d7a22a482 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 03:57:20 -0400 Subject: Specify in runoff.spec which side sheet 1 is on, since it keeps changing --- runoff | 17 +++++++++-------- runoff.spec | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/runoff b/runoff index 1435646..3dee1cc 100755 --- a/runoff +++ b/runoff @@ -58,6 +58,13 @@ perl -e ' next; } + if(/sheet1: (left|right)$/){ + print STDERR "assuming that sheet 1 is a $1 page. double-check!\n"; + $left = $1 eq "left" ? "13579" : "02468"; + $right = $1 eq "left" ? "02468" : "13579"; + next; + } + if(/even: (.*)/){ $file = $1; if(!defined($toc{$file})){ @@ -90,17 +97,11 @@ perl -e ' print STDERR "Have no toc for $file\n"; next; } - # this assumes that sheet 1 of code is a right page - # double-check the PDF. swap the two regexps below - # otherwise. - if(!$leftwarn++) { - print STDERR "assuming that sheet 1 is a right page. double-check!\n"; - } - if($what eq "left" && !($toc{$file} =~ /^\d[02468]$tens/)){ + if($what eq "left" && !($toc{$file} =~ /^\d[$left]$tens/)){ print STDERR "$file does not start on a fresh left page [$toc{$file}]\n"; } # why does this not work if I inline $x in the if? - $x = ($toc{$file} =~ /^\d[13579]$tens/); + $x = ($toc{$file} =~ /^\d[$right]$tens/); if($what eq "right" && !$x){ print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; } diff --git a/runoff.spec b/runoff.spec index e6f5a95..17bb821 100644 --- a/runoff.spec +++ b/runoff.spec @@ -1,3 +1,5 @@ +sheet1: left + # types.h either # param.h either # defs.h either @@ -16,7 +18,7 @@ even: main.c # odd: init.c # spinlock.h either -right: spinlock.c # mild preference +left: spinlock.c # mild preference even: proc.h # mild preference # goal is to have two action-packed 2-page spreads, -- cgit v1.2.3 From d2c7f6497810f05b961df11db75def7ea79b8ec2 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 04:03:42 -0400 Subject: I totally misunderstood the even/odd spec. Add some docs, remove my 'left+' hack, and orthogonalize left/right versus even/odd so you can specify them independently. --- runoff | 13 ++++++------- runoff.spec | 11 ++++++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/runoff b/runoff index 3dee1cc..345fb2e 100755 --- a/runoff +++ b/runoff @@ -89,21 +89,20 @@ perl -e ' next; } - if(/(left|right)(\+?): (.*)/){ + if(/(left|right): (.*)/){ $what = $1; - $tens = ($2 eq "+" ? "5" : "0"); - $file = $3; + $file = $2; if(!defined($toc{$file})){ print STDERR "Have no toc for $file\n"; next; } - if($what eq "left" && !($toc{$file} =~ /^\d[$left]$tens/)){ - print STDERR "$file does not start on a fresh left page [$toc{$file}]\n"; + if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){ + print STDERR "$file does not start on a left page [$toc{$file}]\n"; } # why does this not work if I inline $x in the if? - $x = ($toc{$file} =~ /^\d[$right]$tens/); + $x = ($toc{$file} =~ /^\d[$right][05]/); if($what eq "right" && !$x){ - print STDERR "$file does not start on a fresh right page [$toc{$file}] [$x]\n"; + print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n"; } next; } diff --git a/runoff.spec b/runoff.spec index 17bb821..a082193 100644 --- a/runoff.spec +++ b/runoff.spec @@ -1,5 +1,14 @@ sheet1: left +# "left" and "right" specify which page of a two-page spread a file +# must start on. "left" means that a file must start on the first of +# the two page. "right" means it must start on the second of the two +# pages. The file may start in either column. +# +# "even" and "odd" specify which column a file must start on. "even" +# means it must start in the left of the two columns. "odd" means it +# must start in the right of the two columns. + # types.h either # param.h either # defs.h either @@ -26,7 +35,7 @@ even: proc.h # mild preference # allocproc userinit growproc fork # and another with # scheduler sched yield forkret sleep wakeup1 wakeup -right+: proc.c # VERY important +right: proc.c # VERY important # setjmp.S either # vm.c either -- cgit v1.2.3 From dd3ecd42cd6c8dee72e5212848cd8037d47f81dd Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 04:06:35 -0400 Subject: Re-re-arrange bootasm and bootother. We can't get bootasm and bootmain on the same page, but this will make them easier to flip between. --- runoff.list | 2 +- runoff.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runoff.list b/runoff.list index 025bbe2..01147ea 100644 --- a/runoff.list +++ b/runoff.list @@ -9,8 +9,8 @@ elf.h # startup bootasm.S -bootmain.c bootother.S +bootmain.c main.c # locks diff --git a/runoff.spec b/runoff.spec index a082193..fe960ce 100644 --- a/runoff.spec +++ b/runoff.spec @@ -20,7 +20,7 @@ sheet1: left even: bootasm.S # mild preference even: bootother.S # mild preference -# bootmain.c either +even: bootmain.S # mild preference even: main.c # mp.c don't care at all # even: initcode.S -- cgit v1.2.3 From d8828817d72962a6220cb1fca315cab4bbf6d0a3 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 04:15:17 -0400 Subject: Rearrange proc.h and proc.c to get our action-packed spreads back (mostly). They also make sense in this order, so it's not just for page layout. --- proc.c | 172 ++++++++++++++++++++++++++++++------------------------------ proc.h | 59 +++++++++++---------- runoff.spec | 15 ++++-- 3 files changed, 126 insertions(+), 120 deletions(-) diff --git a/proc.c b/proc.c index 375b1bb..8197377 100644 --- a/proc.c +++ b/proc.c @@ -193,6 +193,92 @@ fork(void) return pid; } +// Exit the current process. Does not return. +// An exited process remains in the zombie state +// until its parent calls wait() to find out it exited. +void +exit(void) +{ + struct proc *p; + int fd; + + if(proc == initproc) + panic("init exiting"); + + // Close all open files. + for(fd = 0; fd < NOFILE; fd++){ + if(proc->ofile[fd]){ + fileclose(proc->ofile[fd]); + proc->ofile[fd] = 0; + } + } + + iput(proc->cwd); + proc->cwd = 0; + + acquire(&ptable.lock); + + // Parent might be sleeping in wait(). + wakeup1(proc->parent); + + // Pass abandoned children to init. + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent == proc){ + p->parent = initproc; + if(p->state == ZOMBIE) + wakeup1(initproc); + } + } + + // Jump into the scheduler, never to return. + proc->state = ZOMBIE; + sched(); + panic("zombie exit"); +} + +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +wait(void) +{ + struct proc *p; + int havekids, pid; + + acquire(&ptable.lock); + for(;;){ + // Scan through table looking for zombie children. + havekids = 0; + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent != proc) + continue; + havekids = 1; + if(p->state == ZOMBIE){ + // Found one. + pid = p->pid; + kfree(p->kstack); + p->kstack = 0; + freevm(p->pgdir); + p->state = UNUSED; + p->pid = 0; + p->parent = 0; + p->name[0] = 0; + p->killed = 0; + release(&ptable.lock); + return pid; + } + } + + // No point waiting if we don't have any children. + if(!havekids || proc->killed){ + release(&ptable.lock); + return -1; + } + + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(proc, &ptable.lock); //DOC: wait-sleep + } +} + //PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. @@ -357,89 +443,3 @@ kill(int pid) return -1; } -// Exit the current process. Does not return. -// An exited process remains in the zombie state -// until its parent calls wait() to find out it exited. -void -exit(void) -{ - struct proc *p; - int fd; - - if(proc == initproc) - panic("init exiting"); - - // Close all open files. - for(fd = 0; fd < NOFILE; fd++){ - if(proc->ofile[fd]){ - fileclose(proc->ofile[fd]); - proc->ofile[fd] = 0; - } - } - - iput(proc->cwd); - proc->cwd = 0; - - acquire(&ptable.lock); - - // Parent might be sleeping in wait(). - wakeup1(proc->parent); - - // Pass abandoned children to init. - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent == proc){ - p->parent = initproc; - if(p->state == ZOMBIE) - wakeup1(initproc); - } - } - - // Jump into the scheduler, never to return. - proc->state = ZOMBIE; - sched(); - panic("zombie exit"); -} - -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. -int -wait(void) -{ - struct proc *p; - int havekids, pid; - - acquire(&ptable.lock); - for(;;){ - // Scan through table looking for zombie children. - havekids = 0; - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->parent != proc) - continue; - havekids = 1; - if(p->state == ZOMBIE){ - // Found one. - pid = p->pid; - kfree(p->kstack); - p->kstack = 0; - freevm(p->pgdir); - p->state = UNUSED; - p->pid = 0; - p->parent = 0; - p->name[0] = 0; - p->killed = 0; - release(&ptable.lock); - return pid; - } - } - - // No point waiting if we don't have any children. - if(!havekids || proc->killed){ - release(&ptable.lock); - return -1; - } - - // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(proc, &ptable.lock); //DOC: wait-sleep - } -} - diff --git a/proc.h b/proc.h index 8799668..4a80a28 100644 --- a/proc.h +++ b/proc.h @@ -8,6 +8,36 @@ #define SEG_TSS 6 // this process's task state #define NSEGS 7 +// Per-CPU state +struct cpu { + uchar id; // Local APIC ID; index into cpus[] below + struct context *scheduler; // Switch 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? + int ncli; // Depth of pushcli nesting. + int intena; // Were interrupts enabled before pushcli? + + // Cpu-local storage variables; see below + struct cpu *cpu; + struct proc *proc; +}; + +extern struct cpu cpus[NCPU]; +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 +// %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. + +//PAGEBREAK: 17 // Saved registers for kernel context switches. // Don't need to save all the segment registers (%cs, etc), // because they are constant across kernel contexts. @@ -50,32 +80,3 @@ struct proc { // original data and bss // fixed-size stack // expandable heap - -// Per-CPU state -struct cpu { - uchar id; // Local APIC ID; index into cpus[] below - struct context *scheduler; // Switch 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? - int ncli; // Depth of pushcli nesting. - int intena; // Were interrupts enabled before pushcli? - - // Cpu-local storage variables; see below - struct cpu *cpu; - struct proc *proc; -}; - -extern struct cpu cpus[NCPU]; -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 -// %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. diff --git a/runoff.spec b/runoff.spec index fe960ce..9d87528 100644 --- a/runoff.spec +++ b/runoff.spec @@ -20,22 +20,27 @@ sheet1: left even: bootasm.S # mild preference even: bootother.S # mild preference -even: bootmain.S # mild preference +even: bootmain.c # mild preference even: main.c # mp.c don't care at all # even: initcode.S # odd: init.c # spinlock.h either -left: spinlock.c # mild preference -even: proc.h # mild preference +left: spinlock.h # mild preference +even: spinlock.h # mild preference + +# This gets struct proc and allocproc on the same spread +right: proc.h +odd: proc.h # goal is to have two action-packed 2-page spreads, # one with -# allocproc userinit growproc fork +# userinit growproc fork exit wait # and another with # scheduler sched yield forkret sleep wakeup1 wakeup -right: proc.c # VERY important +left: proc.c # VERY important +odd: proc.c # VERY important # setjmp.S either # vm.c either -- cgit v1.2.3 From d599aa2e40fc116d84c609358a9fdc51824b621d Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 14:08:45 -0400 Subject: Remove unused nop_pause function. --- runoff | 2 ++ x86.h | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/runoff b/runoff index 345fb2e..2b48cd5 100755 --- a/runoff +++ b/runoff @@ -192,6 +192,8 @@ do if [ "x$defs" != "x$uses" ]; then echo $i $defs echo $uses |fmt -29 | sed 's/^/ /' +# else +# echo $i defined but not used >&2 fi done ) >refs diff --git a/x86.h b/x86.h index 33e240d..5a59cc2 100644 --- a/x86.h +++ b/x86.h @@ -137,12 +137,6 @@ xchg(volatile uint *addr, uint newval) return result; } -static inline void -nop_pause(void) -{ - asm volatile("pause" : :); -} - //PAGEBREAK! static inline void lcr0(uint val) -- cgit v1.2.3 From c7c21467c3d3c9f2a04d6fac3b8ef796470b0448 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 14:30:06 -0400 Subject: Oops. Broke the build when I rearranged proc.c --- defs.h | 1 + proc.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/defs.h b/defs.h index dca91bb..f2724ad 100644 --- a/defs.h +++ b/defs.h @@ -101,6 +101,7 @@ int kill(int); void pinit(void); void procdump(void); void scheduler(void) __attribute__((noreturn)); +void sched(void); void sleep(void*, struct spinlock*); void userinit(void); int wait(void); diff --git a/proc.c b/proc.c index 8197377..a72ec6c 100644 --- a/proc.c +++ b/proc.c @@ -17,6 +17,8 @@ int nextpid = 1; extern void forkret(void); extern void trapret(void); +static void wakeup1(void *chan); + void pinit(void) { -- cgit v1.2.3 From b3cfd7fc372424d754f5478c93f6fbe756d9c80f Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 15:18:19 -0400 Subject: Simplify freevm using deallocuvm --- vm.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/vm.c b/vm.c index 8cfac14..12aebe5 100644 --- a/vm.c +++ b/vm.c @@ -206,8 +206,7 @@ allocuvm(pde_t *pgdir, char *addr, uint sz) return 1; } -// deallocate some of the user pages, in response to sbrk() -// with a negative argument. if addr is not page-aligned, +// deallocate some of the user pages. if addr is not page-aligned, // then only deallocates starting at the next page boundary. int deallocuvm(pde_t *pgdir, char *addr, uint sz) @@ -235,26 +234,14 @@ deallocuvm(pde_t *pgdir, char *addr, uint sz) void freevm(pde_t *pgdir) { - uint i, j, da; + uint i; if(!pgdir) - panic("freevm: no pgdir\n"); + panic("freevm: no pgdir"); + deallocuvm(pgdir, 0, USERTOP); for(i = 0; i < NPDENTRIES; i++){ - da = PTE_ADDR(pgdir[i]); - if(da != 0){ - pte_t *pgtab = (pte_t*) da; - for(j = 0; j < NPTENTRIES; j++){ - if(pgtab[j] != 0){ - uint pa = PTE_ADDR(pgtab[j]); - uint va = PGADDR(i, j, 0); - if(va < USERTOP) // user memory - kfree((void *) pa); - pgtab[j] = 0; - } - } - kfree((void *) da); - pgdir[i] = 0; - } + if(pgdir[i] & PTE_P) + kfree((void *) PTE_ADDR(pgdir[i])); } kfree((void *) pgdir); } -- cgit v1.2.3 From f53e6110bed159c8541c6e0d2fc1b1ffac2d141a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 15:37:05 -0400 Subject: Simplify inituvm and userinit by assuming initcode fits on a page --- defs.h | 2 +- proc.c | 7 ++----- vm.c | 20 +++++++------------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/defs.h b/defs.h index f2724ad..cc3a721 100644 --- a/defs.h +++ b/defs.h @@ -160,7 +160,7 @@ char* uva2ka(pde_t*, char*); int allocuvm(pde_t*, char*, uint); int deallocuvm(pde_t *pgdir, char *addr, uint sz); void freevm(pde_t*); -void inituvm(pde_t*, char*, char*, uint); +void inituvm(pde_t*, char*, uint); int loaduvm(pde_t*, char*, struct inode *ip, uint, uint); pde_t* copyuvm(pde_t*,uint); void switchuvm(struct proc*); diff --git a/proc.c b/proc.c index a72ec6c..853eb0a 100644 --- a/proc.c +++ b/proc.c @@ -120,11 +120,8 @@ userinit(void) initproc = p; if(!(p->pgdir = setupkvm())) panic("userinit: out of memory?"); - if(!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) - panic("userinit: out of memory?"); - inituvm(p->pgdir, 0x0, _binary_initcode_start, - (int)_binary_initcode_size); - p->sz = PGROUNDUP((int)_binary_initcode_size); + inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); + p->sz = PGSIZE; memset(p->tf, 0, sizeof(*p->tf)); p->tf->cs = (SEG_UCODE << 3) | DPL_USER; p->tf->ds = (SEG_UDATA << 3) | DPL_USER; diff --git a/vm.c b/vm.c index 12aebe5..9c0783d 100644 --- a/vm.c +++ b/vm.c @@ -267,20 +267,14 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) } void -inituvm(pde_t *pgdir, char *addr, char *init, uint sz) +inituvm(pde_t *pgdir, char *init, uint sz) { - uint i, pa, n, off; - pte_t *pte; - - for(i = 0; i < sz; i += PGSIZE){ - if(!(pte = walkpgdir(pgdir, (void *)(i+addr), 0))) - panic("inituvm: pte should exist\n"); - off = (i+(uint)addr) % PGSIZE; - pa = PTE_ADDR(*pte); - if(sz - i < PGSIZE) n = sz - i; - else n = PGSIZE; - memmove((char *)pa+off, init+i, n); - } + char *mem = kalloc(); + if (sz >= PGSIZE) + panic("inituvm: more than a page"); + memset(mem, 0, PGSIZE); + mappages(pgdir, 0, PGSIZE, PADDR(mem), PTE_W|PTE_U); + memmove(mem, init, sz); } // given a parent process's page table, create a copy -- cgit v1.2.3 From f25a3f9a41de02116c39b0e65243d111e1cb1026 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 16:23:15 -0400 Subject: Rearrange vm.c so it's in logical order and prints nicely. Shorten a few functions in uninteresting ways to make them fit. --- runoff.spec | 10 ++- vm.c | 264 +++++++++++++++++++++++++++++------------------------------- 2 files changed, 137 insertions(+), 137 deletions(-) diff --git a/runoff.spec b/runoff.spec index 9d87528..d5b5334 100644 --- a/runoff.spec +++ b/runoff.spec @@ -42,8 +42,14 @@ odd: proc.h left: proc.c # VERY important odd: proc.c # VERY important -# setjmp.S either -# vm.c either +# A few more action packed spreads +# page table creation and process loading +# walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm +# process memory management +# allocuvm deallocuvm freevm +right: vm.c +odd: vm.c + # kalloc.c either # syscall.h either diff --git a/vm.c b/vm.c index 9c0783d..f3436b7 100644 --- a/vm.c +++ b/vm.c @@ -6,33 +6,38 @@ #include "proc.h" #include "elf.h" -// The mappings from logical to linear are one to one (i.e., -// segmentation doesn't do anything). -// There is one page table per process, plus one that's used -// when a CPU is not running any process (kpgdir). -// A user process uses the same page table as the kernel; the -// page protection bits prevent it from using anything other -// than its memory. -// -// setupkvm() and exec() set up every page table like this: -// 0..640K : user memory (text, data, stack, heap) -// 640K..1M : mapped direct (for IO space) -// 1M..end : mapped direct (for the kernel's text and data) -// end..PHYSTOP : mapped direct (kernel heap and user pages) -// 0xfe000000..0 : mapped direct (devices such as ioapic) -// -// The kernel allocates memory for its heap and for user memory -// between kernend and the end of physical memory (PHYSTOP). -// The virtual address space of each user program includes the kernel -// (which is inaccessible in user mode). The user program addresses -// range from 0 till 640KB (USERTOP), which where the I/O hole starts -// (both in physical memory and in the kernel's virtual address -// space). - #define USERTOP 0xA0000 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) +{ + struct cpu *c; + + // Map virtual addresses to linear addresses using identity map. + // Cannot share a CODE descriptor for both kernel and user + // because it would have to have DPL_USR, but the CPU forbids + // an interrupt from CPL=0 to DPL=3. + c = &cpus[cpunum()]; + c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); + c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); + c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER); + + // map cpu, and curproc + c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0); + + lgdt(c->gdt, sizeof(c->gdt)); + loadgs(SEG_KCPU << 3); + + // Initialize cpu-local storage. + cpu = c; + proc = 0; +} + // return the address of the PTE in page table pgdir // that corresponds to linear address va. if create!=0, // create any required page table pages. @@ -50,10 +55,8 @@ walkpgdir(pde_t *pgdir, const void *va, int create) return 0; else { pgtab = (pte_t*) r; - // Make sure all those PTE_P bits are zero. memset(pgtab, 0, PGSIZE); - // The permissions here are overly generous, but they can // be further restricted by the permissions in the page table // entries, if necessary. @@ -68,9 +71,9 @@ walkpgdir(pde_t *pgdir, const void *va, int create) static int mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) { - char *first = PGROUNDDOWN(la); + char *a = PGROUNDDOWN(la); char *last = PGROUNDDOWN(la + size - 1); - char *a = first; + while(1){ pte_t *pte = walkpgdir(pgdir, a, 1); if(pte == 0) @@ -86,32 +89,75 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) return 1; } -// Set up CPU's kernel segment descriptors. -// Run once at boot time on each CPU. +// The mappings from logical to linear are one to one (i.e., +// segmentation doesn't do anything). +// There is one page table per process, plus one that's used +// when a CPU is not running any process (kpgdir). +// A user process uses the same page table as the kernel; the +// page protection bits prevent it from using anything other +// than its memory. +// +// setupkvm() and exec() set up every page table like this: +// 0..640K : user memory (text, data, stack, heap) +// 640K..1M : mapped direct (for IO space) +// 1M..end : mapped direct (for the kernel's text and data) +// end..PHYSTOP : mapped direct (kernel heap and user pages) +// 0xfe000000..0 : mapped direct (devices such as ioapic) +// +// The kernel allocates memory for its heap and for user memory +// between kernend and the end of physical memory (PHYSTOP). +// The virtual address space of each user program includes the kernel +// (which is inaccessible in user mode). The user program addresses +// range from 0 till 640KB (USERTOP), which where the I/O hole starts +// (both in physical memory and in the kernel's virtual address +// space). + +// Allocate one page table for the machine for the kernel address +// space for scheduler processes. void -ksegment(void) +kvmalloc(void) { - struct cpu *c; + kpgdir = setupkvm(); +} - // Map virtual addresses to linear addresses using identity map. - // Cannot share a CODE descriptor for both kernel and user - // because it would have to have DPL_USR, but the CPU forbids - // an interrupt from CPL=0 to DPL=3. - c = &cpus[cpunum()]; - c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); - c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); - c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); - c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER); +// Set up kernel part of a page table. +pde_t* +setupkvm(void) +{ + pde_t *pgdir; - // map cpu, and curproc - c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0); + // Allocate page directory + if(!(pgdir = (pde_t *) kalloc())) + return 0; + memset(pgdir, 0, PGSIZE); + if(// Map IO space from 640K to 1Mbyte + !mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W) || + // Map kernel and free memory pool + !mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W) || + // Map devices such as ioapic, lapic, ... + !mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) + return 0; + return pgdir; +} - lgdt(c->gdt, sizeof(c->gdt)); - loadgs(SEG_KCPU << 3); - - // Initialize cpu-local storage. - cpu = c; - proc = 0; +// Turn on paging. +void +vmenable(void) +{ + uint cr0; + + switchkvm(); // load kpgdir into cr3 + cr0 = rcr0(); + cr0 |= CR0_PG; + lcr0(cr0); +} + +// Switch h/w page table register to the kernel-only page table, for when +// no process is running. +void +switchkvm() +{ + lcr3(PADDR(kpgdir)); // Switch to the kernel page table } // Switch h/w page table and TSS registers to point to process p. @@ -134,36 +180,6 @@ switchuvm(struct proc *p) popcli(); } -// Switch h/w page table register to the kernel-only page table, for when -// no process is running. -void -switchkvm() -{ - lcr3(PADDR(kpgdir)); // Switch to the kernel page table -} - -// Set up kernel part of a page table. -pde_t* -setupkvm(void) -{ - pde_t *pgdir; - - // Allocate page directory - if(!(pgdir = (pde_t *) kalloc())) - return 0; - memset(pgdir, 0, PGSIZE); - // Map IO space from 640K to 1Mbyte - if(!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) - return 0; - // Map kernel and free memory pool - if(!mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W)) - return 0; - // Map devices such as ioapic, lapic, ... - if(!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) - return 0; - return pgdir; -} - // return the physical address that a given user address // maps to. the result is also a kernel logical address, // since the kernel maps the physical memory allocated to user @@ -177,6 +193,37 @@ uva2ka(pde_t *pgdir, char *uva) return (char *)pa; } +void +inituvm(pde_t *pgdir, char *init, uint sz) +{ + char *mem = kalloc(); + if (sz >= PGSIZE) + panic("inituvm: more than a page"); + memset(mem, 0, PGSIZE); + mappages(pgdir, 0, PGSIZE, PADDR(mem), PTE_W|PTE_U); + memmove(mem, init, sz); +} + +int +loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) +{ + uint i, pa, n; + pte_t *pte; + + if((uint)addr % PGSIZE != 0) + panic("loaduvm: addr must be page aligned\n"); + for(i = 0; i < sz; i += PGSIZE){ + if(!(pte = walkpgdir(pgdir, addr+i, 0))) + panic("loaduvm: address should exist\n"); + pa = PTE_ADDR(*pte); + if(sz - i < PGSIZE) n = sz - i; + else n = PGSIZE; + if(readi(ip, (char *)pa, offset+i, n) != n) + return 0; + } + return 1; +} + // allocate sz bytes more memory for a process starting at the // given user address; allocates physical memory and page // table entries. addr and sz need not be page-aligned. @@ -187,10 +234,9 @@ allocuvm(pde_t *pgdir, char *addr, uint sz) { if(addr + sz > (char*)USERTOP) return 0; - char *first = PGROUNDDOWN(addr); + char *a = PGROUNDDOWN(addr); char *last = PGROUNDDOWN(addr + sz - 1); - char *a; - for(a = first; a <= last; a += PGSIZE){ + for(; a <= last; a += PGSIZE){ pte_t *pte = walkpgdir(pgdir, a, 0); if(pte == 0 || (*pte & PTE_P) == 0){ char *mem = kalloc(); @@ -213,10 +259,9 @@ deallocuvm(pde_t *pgdir, char *addr, uint sz) { if(addr + sz > (char*)USERTOP) return 0; - char *first = (char*) PGROUNDUP((uint)addr); + char *a = (char *)PGROUNDUP((uint)addr); char *last = PGROUNDDOWN(addr + sz - 1); - char *a; - for(a = first; a <= last; a += PGSIZE){ + for(; a <= last; a += PGSIZE){ pte_t *pte = walkpgdir(pgdir, a, 0); if(pte && (*pte & PTE_P) != 0){ uint pa = PTE_ADDR(*pte); @@ -246,37 +291,6 @@ freevm(pde_t *pgdir) kfree((void *) pgdir); } -int -loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) -{ - uint i, pa, n; - pte_t *pte; - - if((uint)addr % PGSIZE != 0) - panic("loaduvm: addr must be page aligned\n"); - for(i = 0; i < sz; i += PGSIZE){ - if(!(pte = walkpgdir(pgdir, addr+i, 0))) - panic("loaduvm: address should exist\n"); - pa = PTE_ADDR(*pte); - if(sz - i < PGSIZE) n = sz - i; - else n = PGSIZE; - if(readi(ip, (char *)pa, offset+i, n) != n) - return 0; - } - return 1; -} - -void -inituvm(pde_t *pgdir, char *init, uint sz) -{ - char *mem = kalloc(); - if (sz >= PGSIZE) - panic("inituvm: more than a page"); - memset(mem, 0, PGSIZE); - mappages(pgdir, 0, PGSIZE, PADDR(mem), PTE_W|PTE_U); - memmove(mem, init, sz); -} - // given a parent process's page table, create a copy // of it for a child. pde_t* @@ -307,23 +321,3 @@ bad: return 0; } -// Allocate one page table for the machine for the kernel address -// space for scheduler processes. -void -kvmalloc(void) -{ - kpgdir = setupkvm(); -} - -// Turn on paging. -void -vmenable(void) -{ - uint cr0; - - switchkvm(); // load kpgdir into cr3 - cr0 = rcr0(); - cr0 |= CR0_PG; - lcr0(cr0); -} - -- cgit v1.2.3 From 93a1e4cb07e329dc9550643f7fe8430da547497a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 16:36:38 -0400 Subject: Fix comment capitalization --- vm.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vm.c b/vm.c index f3436b7..2a57faf 100644 --- a/vm.c +++ b/vm.c @@ -27,7 +27,7 @@ ksegment(void) c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER); - // map cpu, and curproc + // Map cpu, and curproc c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0); lgdt(c->gdt, sizeof(c->gdt)); @@ -38,8 +38,8 @@ ksegment(void) proc = 0; } -// return the address of the PTE in page table pgdir -// that corresponds to linear address va. if create!=0, +// Return the address of the PTE in page table pgdir +// that corresponds to linear address va. If create!=0, // create any required page table pages. static pte_t * walkpgdir(pde_t *pgdir, const void *va, int create) @@ -65,7 +65,7 @@ walkpgdir(pde_t *pgdir, const void *va, int create) return &pgtab[PTX(va)]; } -// create PTEs for linear addresses starting at la that refer to +// Create PTEs for linear addresses starting at la that refer to // physical addresses starting at pa. la and size might not // be page-aligned. static int @@ -152,12 +152,12 @@ vmenable(void) lcr0(cr0); } -// Switch h/w page table register to the kernel-only page table, for when -// no process is running. +// Switch h/w page table register to the kernel-only page table, +// for when no process is running. void switchkvm() { - lcr3(PADDR(kpgdir)); // Switch to the kernel page table + lcr3(PADDR(kpgdir)); // switch to the kernel page table } // Switch h/w page table and TSS registers to point to process p. @@ -180,8 +180,8 @@ switchuvm(struct proc *p) popcli(); } -// return the physical address that a given user address -// maps to. the result is also a kernel logical address, +// Return the physical address that a given user address +// maps to. The result is also a kernel logical address, // since the kernel maps the physical memory allocated to user // processes directly. char* @@ -224,10 +224,10 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) return 1; } -// allocate sz bytes more memory for a process starting at the +// Allocate sz bytes more memory for a process starting at the // given user address; allocates physical memory and page // table entries. addr and sz need not be page-aligned. -// it is a no-op for any parts of the requested memory +// It is a no-op for any parts of the requested memory // that are already allocated. int allocuvm(pde_t *pgdir, char *addr, uint sz) @@ -252,7 +252,7 @@ allocuvm(pde_t *pgdir, char *addr, uint sz) return 1; } -// deallocate some of the user pages. if addr is not page-aligned, +// Deallocate some of the user pages. If addr is not page-aligned, // then only deallocates starting at the next page boundary. int deallocuvm(pde_t *pgdir, char *addr, uint sz) @@ -274,7 +274,7 @@ deallocuvm(pde_t *pgdir, char *addr, uint sz) return 1; } -// free a page table and all the physical memory pages +// Free a page table and all the physical memory pages // in the user part. void freevm(pde_t *pgdir) @@ -291,7 +291,7 @@ freevm(pde_t *pgdir) kfree((void *) pgdir); } -// given a parent process's page table, create a copy +// Given a parent process's page table, create a copy // of it for a child. pde_t* copyuvm(pde_t *pgdir, uint sz) -- cgit v1.2.3 From 9a4670a138f7ab25062195be2f4d42403dddc077 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 16:39:55 -0400 Subject: Comment inituvm and loaduvm --- vm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vm.c b/vm.c index 2a57faf..0104c3e 100644 --- a/vm.c +++ b/vm.c @@ -193,6 +193,8 @@ uva2ka(pde_t *pgdir, char *uva) return (char *)pa; } +// Load the initcode into address 0 of pgdir. +// sz must be less than a page. void inituvm(pde_t *pgdir, char *init, uint sz) { @@ -204,6 +206,8 @@ inituvm(pde_t *pgdir, char *init, uint sz) memmove(mem, init, sz); } +// Load a program segment into pgdir. addr must be page-aligned +// and the pages from addr to addr+sz must already be mapped. int loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) { -- cgit v1.2.3 From d49d31381df93b40c1c4bc18c97ca42c3256e414 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 17:03:19 -0400 Subject: Nits --- runoff.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runoff.spec b/runoff.spec index d5b5334..3306f32 100644 --- a/runoff.spec +++ b/runoff.spec @@ -2,12 +2,14 @@ sheet1: left # "left" and "right" specify which page of a two-page spread a file # must start on. "left" means that a file must start on the first of -# the two page. "right" means it must start on the second of the two +# the two pages. "right" means it must start on the second of the two # pages. The file may start in either column. # # "even" and "odd" specify which column a file must start on. "even" # means it must start in the left of the two columns. "odd" means it # must start in the right of the two columns. +# +# You'd think these would be the other way around. # types.h either # param.h either -- cgit v1.2.3 From 79cd8b3eedeb1f85d3b19fb6119bd5224c4c532a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 18:28:36 -0400 Subject: Simplify allocuvm/deallocuvm to operate in a contiguous memory model. This makes their interface match up better with proc->sz and also simplifies the callers (it even gets the main body of exec on one page). --- defs.h | 4 ++-- exec.c | 26 +++++++++++--------------- proc.c | 7 ++++--- vm.c | 58 +++++++++++++++++++++++++++------------------------------- 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/defs.h b/defs.h index cc3a721..4de95ad 100644 --- a/defs.h +++ b/defs.h @@ -157,8 +157,8 @@ void kvmalloc(void); void vmenable(void); pde_t* setupkvm(void); char* uva2ka(pde_t*, char*); -int allocuvm(pde_t*, char*, uint); -int deallocuvm(pde_t *pgdir, char *addr, uint sz); +int allocuvm(pde_t*, uint, uint); +int deallocuvm(pde_t*, uint, uint); void freevm(pde_t*); void inituvm(pde_t*, char*, uint); int loaduvm(pde_t*, char*, struct inode *ip, uint, uint); diff --git a/exec.c b/exec.c index c518c04..222f64c 100644 --- a/exec.c +++ b/exec.c @@ -11,7 +11,7 @@ exec(char *path, char **argv) { char *mem, *s, *last; int i, argc, arglen, len, off; - uint sz, sp, spoffset, argp; + uint sz, sp, spbottom, argp; struct elfhdr elf; struct inode *ip; struct proghdr ph; @@ -41,22 +41,18 @@ exec(char *path, char **argv) continue; if(ph.memsz < ph.filesz) goto bad; - if(!allocuvm(pgdir, (char *)ph.va, ph.memsz)) + if(!(sz = allocuvm(pgdir, sz, ph.va + ph.memsz))) goto bad; - if(ph.va + ph.memsz > sz) - sz = ph.va + ph.memsz; if(!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz)) goto bad; } iunlockput(ip); // Allocate and initialize stack at sz - sz = PGROUNDUP(sz); - if(!allocuvm(pgdir, (char *)sz, PGSIZE)) + sz = spbottom = PGROUNDUP(sz); + if(!(sz = allocuvm(pgdir, sz, sz + PGSIZE))) goto bad; - mem = uva2ka(pgdir, (char *)sz); - spoffset = sz; - sz += PGSIZE; + mem = uva2ka(pgdir, (char *)spbottom); arglen = 0; for(argc=0; argv[argc]; argc++) @@ -67,22 +63,22 @@ exec(char *path, char **argv) argp = sz - arglen - 4*(argc+1); // Copy argv strings and pointers to stack. - *(uint*)(mem+argp-spoffset + 4*argc) = 0; // argv[argc] + *(uint*)(mem+argp-spbottom + 4*argc) = 0; // argv[argc] for(i=argc-1; i>=0; i--){ len = strlen(argv[i]) + 1; sp -= len; - memmove(mem+sp-spoffset, argv[i], len); - *(uint*)(mem+argp-spoffset + 4*i) = sp; // argv[i] + memmove(mem+sp-spbottom, argv[i], len); + *(uint*)(mem+argp-spbottom + 4*i) = sp; // argv[i] } // Stack frame for main(argc, argv), below arguments. sp = argp; sp -= 4; - *(uint*)(mem+sp-spoffset) = argp; + *(uint*)(mem+sp-spbottom) = argp; sp -= 4; - *(uint*)(mem+sp-spoffset) = argc; + *(uint*)(mem+sp-spbottom) = argc; sp -= 4; - *(uint*)(mem+sp-spoffset) = 0xffffffff; // fake return pc + *(uint*)(mem+sp-spbottom) = 0xffffffff; // fake return pc // Save program name for debugging. for(last=s=path; *s; s++) diff --git a/proc.c b/proc.c index 853eb0a..6f7bdb6 100644 --- a/proc.c +++ b/proc.c @@ -142,14 +142,15 @@ userinit(void) int growproc(int n) { + uint sz = proc->sz; if(n > 0){ - if(!allocuvm(proc->pgdir, (char *)proc->sz, n)) + if(!(sz = allocuvm(proc->pgdir, sz, sz + n))) return -1; } else if(n < 0){ - if(!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) + if(!(sz = deallocuvm(proc->pgdir, sz, sz + n))) return -1; } - proc->sz += n; + proc->sz = sz; switchuvm(proc); return 0; } diff --git a/vm.c b/vm.c index 0104c3e..5c6f943 100644 --- a/vm.c +++ b/vm.c @@ -228,54 +228,50 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) return 1; } -// Allocate sz bytes more memory for a process starting at the -// given user address; allocates physical memory and page -// table entries. addr and sz need not be page-aligned. -// It is a no-op for any parts of the requested memory -// that are already allocated. +// Allocate memory to the process to bring its size from oldsz to +// newsz. Allocates physical memory and page table entries. oldsz and +// newsz need not be page-aligned, nor does newsz have to be larger +// than oldsz. Returns the new process size or 0 on error. int -allocuvm(pde_t *pgdir, char *addr, uint sz) +allocuvm(pde_t *pgdir, uint oldsz, uint newsz) { - if(addr + sz > (char*)USERTOP) + if(newsz > USERTOP) return 0; - char *a = PGROUNDDOWN(addr); - char *last = PGROUNDDOWN(addr + sz - 1); - for(; a <= last; a += PGSIZE){ - pte_t *pte = walkpgdir(pgdir, a, 0); - if(pte == 0 || (*pte & PTE_P) == 0){ - char *mem = kalloc(); - if(mem == 0){ - cprintf("allocuvm out of memory\n"); - deallocuvm(pgdir, addr, sz); - return 0; - } - memset(mem, 0, PGSIZE); - mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U); + char *a = (char *)PGROUNDUP(oldsz); + char *last = PGROUNDDOWN(newsz - 1); + for (; a <= last; a += PGSIZE){ + char *mem = kalloc(); + if(mem == 0){ + cprintf("allocuvm out of memory\n"); + deallocuvm(pgdir, newsz, oldsz); + return 0; } + memset(mem, 0, PGSIZE); + mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U); } - return 1; + return newsz > oldsz ? newsz : oldsz; } -// Deallocate some of the user pages. If addr is not page-aligned, -// then only deallocates starting at the next page boundary. +// Deallocate user pages to bring the process size from oldsz to +// newsz. oldsz and newsz need not be page-aligned, nor does newsz +// need to be less than oldsz. oldsz can be larger than the actual +// process size. Returns the new process size. int -deallocuvm(pde_t *pgdir, char *addr, uint sz) +deallocuvm(pde_t *pgdir, uint oldsz, uint newsz) { - if(addr + sz > (char*)USERTOP) - return 0; - char *a = (char *)PGROUNDUP((uint)addr); - char *last = PGROUNDDOWN(addr + sz - 1); + char *a = (char *)PGROUNDUP(newsz); + char *last = PGROUNDDOWN(oldsz - 1); for(; a <= last; a += PGSIZE){ pte_t *pte = walkpgdir(pgdir, a, 0); if(pte && (*pte & PTE_P) != 0){ uint pa = PTE_ADDR(*pte); if(pa == 0) - panic("deallocuvm"); + panic("kfree"); kfree((void *) pa); *pte = 0; } } - return 1; + return newsz < oldsz ? newsz : oldsz; } // Free a page table and all the physical memory pages @@ -287,7 +283,7 @@ freevm(pde_t *pgdir) if(!pgdir) panic("freevm: no pgdir"); - deallocuvm(pgdir, 0, USERTOP); + deallocuvm(pgdir, USERTOP, 0); for(i = 0; i < NPDENTRIES; i++){ if(pgdir[i] & PTE_P) kfree((void *) PTE_ADDR(pgdir[i])); -- cgit v1.2.3 From 5bf11c50b512f06fd96675ca1ddd9ab1a2cf6fa0 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 18:36:06 -0400 Subject: Get rid of the blank page in picirq.c. It misaligs things now. --- picirq.c | 29 ----------------------------- runoff.spec | 2 ++ 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/picirq.c b/picirq.c index 1230c13..ff86831 100644 --- a/picirq.c +++ b/picirq.c @@ -82,32 +82,3 @@ picinit(void) if(irqmask != 0xFFFF) picsetmask(irqmask); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Blank page. diff --git a/runoff.spec b/runoff.spec index 3306f32..8a2b5c9 100644 --- a/runoff.spec +++ b/runoff.spec @@ -70,6 +70,7 @@ odd: vm.c # fs.h either # fsvar.h either left: ide.c +even: ide.c # odd: bio.c odd: fs.c # VERY important # file.c either @@ -79,5 +80,6 @@ odd: fs.c # VERY important # even: pipe.c # mild preference # string.c either left: kbd.h +even: kbd.h even: console.c odd: sh.c -- cgit v1.2.3 From 08eef9eae5989d3a709588352e228041e62e2c2c Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 18:50:49 -0400 Subject: Get cgaputc and consputc back on the same page --- console.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/console.c b/console.c index a3cdf96..e7ed248 100644 --- a/console.c +++ b/console.c @@ -136,8 +136,7 @@ cgaputc(int c) if(c == '\n') pos += 80 - pos%80; else if(c == BACKSPACE){ - if(pos > 0) - crt[--pos] = ' ' | 0x0700; + if(pos > 0) --pos; } else crt[pos++] = (c&0xff) | 0x0700; // black on white @@ -164,15 +163,12 @@ consputc(int c) } if(c == BACKSPACE){ - uartputc('\b'); - uartputc(' '); - uartputc('\b'); + uartputc('\b'); uartputc(' '); uartputc('\b'); } else uartputc(c); cgaputc(c); } -//PAGEBREAK: 50 #define INPUT_BUF 128 struct { struct spinlock lock; -- cgit v1.2.3 From 8440dbfee13a5c728c5060af515f9273889a4679 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 18:54:50 -0400 Subject: Get consoleintr back on one page --- console.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/console.c b/console.c index e7ed248..19b3296 100644 --- a/console.c +++ b/console.c @@ -198,8 +198,7 @@ consoleintr(int (*getc)(void)) consputc(BACKSPACE); } break; - case C('H'): // Backspace - case '\x7f': + case C('H'): case '\x7f': // Backspace if(input.e != input.w){ input.e--; consputc(BACKSPACE); @@ -207,9 +206,7 @@ consoleintr(int (*getc)(void)) break; default: if(c != 0 && input.e-input.r < INPUT_BUF){ - // The serial port produces 0x13, not 0x10 - if(c == '\r') - c = '\n'; + c = (c == '\r') ? '\n' : c; input.buf[input.e++ % INPUT_BUF] = c; consputc(c); if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ -- cgit v1.2.3 From 588644f4728e2707138628c62469ba4eeebe18e9 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 19:01:25 -0400 Subject: Consistent style in defs.h --- defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/defs.h b/defs.h index 4de95ad..93a8bc6 100644 --- a/defs.h +++ b/defs.h @@ -161,7 +161,7 @@ int allocuvm(pde_t*, uint, uint); int deallocuvm(pde_t*, uint, uint); void freevm(pde_t*); void inituvm(pde_t*, char*, uint); -int loaduvm(pde_t*, char*, struct inode *ip, uint, uint); +int loaduvm(pde_t*, char*, struct inode *, uint, uint); pde_t* copyuvm(pde_t*,uint); void switchuvm(struct proc*); void switchkvm(); -- cgit v1.2.3 From cc21c06e3b610951d4b870b90083f7f502388252 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 2 Sep 2010 19:03:48 -0400 Subject: Update example in TOC footer --- toc.ftr | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/toc.ftr b/toc.ftr index 6ed7fe0..3ed8593 100644 --- a/toc.ftr +++ b/toc.ftr @@ -6,9 +6,8 @@ on the same line as the name, the line number (or, in a few cases, numbers) where the name is defined. Successive lines in an entry list the line numbers where the name is used. For example, this entry: - swtch 2208 - 0318 1928 1967 2207 - 2208 + swtch 2308 + 0317 2128 2166 2307 2308 -indicates that swtch is defined on line 2208 and is mentioned on five lines -on sheets 03, 19, and 22. +indicates that swtch is defined on line 2308 and is mentioned on five lines +on sheets 03, 21, and 23. -- cgit v1.2.3 From 124fe7e457c0dc9b671c7ffccb56004eb61da4f0 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 3 Sep 2010 15:47:28 -0400 Subject: Rev 4 --- Makefile | 2 +- xv6-rev4.tar.gz | Bin 0 -> 77347 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 xv6-rev4.tar.gz diff --git a/Makefile b/Makefile index 30cec7c..95247ff 100644 --- a/Makefile +++ b/Makefile @@ -241,4 +241,4 @@ tar: rm -rf /tmp/xv6 mkdir -p /tmp/xv6 cp dist/* dist/.gdbinit.tmpl /tmp/xv6 - (cd /tmp; tar cf - xv6) | gzip >xv6-rev3.tar.gz + (cd /tmp; tar cf - xv6) | gzip >xv6-rev4.tar.gz diff --git a/xv6-rev4.tar.gz b/xv6-rev4.tar.gz new file mode 100644 index 0000000..adb3acc Binary files /dev/null and b/xv6-rev4.tar.gz differ -- cgit v1.2.3 From faad047ab22cbe989c208bff5ecb42608ecb8d7b Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 13 Sep 2010 15:34:44 -0400 Subject: change some comments, maybe more informative delete most comments from bootother.S (since copy of bootasm.S) ksegment() -> seginit() move more stuff from main() to mainc() --- bootasm.S | 11 ++++++--- bootother.S | 75 ++++++++++++++++++++++++++----------------------------------- defs.h | 2 +- main.c | 24 +++++++++++++------- proc.c | 6 +++-- proc.h | 14 ++++++------ spinlock.c | 2 +- vm.c | 2 +- 8 files changed, 70 insertions(+), 66 deletions(-) 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; -- cgit v1.2.3 From 4c274cef7425614e328eb2127362d36b203543a8 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 15 Sep 2010 16:15:07 -0400 Subject: Simplify boot loader by removing some JOS'isms --- bootmain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootmain.c b/bootmain.c index 14f4ff3..f67f8fa 100644 --- a/bootmain.c +++ b/bootmain.c @@ -34,7 +34,7 @@ bootmain(void) ph = (struct proghdr*)((uchar*)elf + elf->phoff); eph = ph + elf->phnum; for(; ph < eph; ph++) { - va = (uchar*)(ph->va & 0xFFFFFF); + va = (uchar*)ph->va; readseg(va, ph->filesz, ph->offset); if(ph->memsz > ph->filesz) stosb(va + ph->filesz, 0, ph->memsz - ph->filesz); @@ -42,7 +42,7 @@ bootmain(void) // Call the entry point from the ELF header. // Does not return! - entry = (void(*)(void))(elf->entry & 0xFFFFFF); + entry = (void(*)(void))(elf->entry); entry(); } -- cgit v1.2.3 From 4587b35847b116057e3ef1058da914b8837ff343 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 19 Sep 2010 07:18:42 -0400 Subject: exec questions --- exec.c | 6 ++++++ kalloc.c | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index 222f64c..a6de18f 100644 --- a/exec.c +++ b/exec.c @@ -48,6 +48,9 @@ exec(char *path, char **argv) } iunlockput(ip); + // XXX rtm: what about the BSS? shouldn't there be some + // bzero()ing here? + // Allocate and initialize stack at sz sz = spbottom = PGROUNDUP(sz); if(!(sz = allocuvm(pgdir, sz, sz + PGSIZE))) @@ -62,6 +65,9 @@ exec(char *path, char **argv) sp = sz; argp = sz - arglen - 4*(argc+1); + // XXX rtm: does the following code work if the + // arguments &c do not fit in one page? + // Copy argv strings and pointers to stack. *(uint*)(mem+argp-spbottom + 4*argc) = 0; // argv[argc] for(i=argc-1; i>=0; i--){ diff --git a/kalloc.c b/kalloc.c index 5f690f5..72ce58a 100644 --- a/kalloc.c +++ b/kalloc.c @@ -17,12 +17,12 @@ struct { struct run *freelist; } kmem; +extern char end[]; // first address after kernel loaded from ELF file + // Initialize free list of physical pages. void kinit(void) { - extern char end[]; - initlock(&kmem.lock, "kmem"); char *p = (char*)PGROUNDUP((uint)end); for( ; p + PGSIZE - 1 < (char*) PHYSTOP; p += PGSIZE) @@ -39,7 +39,7 @@ kfree(char *v) { struct run *r; - if(((uint) v) % PGSIZE || (uint)v < 1024*1024 || (uint)v >= PHYSTOP) + if(((uint) v) % PGSIZE || v < end || (uint)v >= PHYSTOP) panic("kfree"); // Fill with junk to catch dangling refs. -- cgit v1.2.3 From 05d66b06294df89ba3d5b8f6cf535f7edf00bd1f Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 19 Sep 2010 13:47:52 -0400 Subject: my comment is wrong, exec handles BSS fine --- exec.c | 3 --- usertests.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index a6de18f..0a9ca59 100644 --- a/exec.c +++ b/exec.c @@ -48,9 +48,6 @@ exec(char *path, char **argv) } iunlockput(ip); - // XXX rtm: what about the BSS? shouldn't there be some - // bzero()ing here? - // Allocate and initialize stack at sz sz = spbottom = PGROUNDUP(sz); if(!(sz = allocuvm(pgdir, sz, sz + PGSIZE))) diff --git a/usertests.c b/usertests.c index 77495bf..e62703d 100644 --- a/usertests.c +++ b/usertests.c @@ -1419,6 +1419,21 @@ validatetest(void) printf(stdout, "validate ok\n"); } +char uninit[10000]; +void +bsstest(void) +{ + int i; + printf(stdout, "bss test\n"); + for(i = 0; i < sizeof(uninit); i++){ + if(uninit[i] != '\0'){ + printf(stdout, "bss test failed\n"); + exit(); + } + } + printf(stdout, "bss test ok\n"); +} + int main(int argc, char *argv[]) { @@ -1430,6 +1445,7 @@ main(int argc, char *argv[]) } close(open("usertests.ran", O_CREATE)); + bsstest(); sbrktest(); validatetest(); -- cgit v1.2.3 From a9183883b88bfccd1c6a62e27621b2c892d50b67 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 20 Sep 2010 06:00:22 -0400 Subject: map kernel instructions r/o --- usertests.c | 28 ++++++++++++++++++++++++++++ vm.c | 9 +++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/usertests.c b/usertests.c index e62703d..177ffba 100644 --- a/usertests.c +++ b/usertests.c @@ -1419,6 +1419,7 @@ validatetest(void) printf(stdout, "validate ok\n"); } +// does unintialized data start out zero? char uninit[10000]; void bsstest(void) @@ -1434,6 +1435,32 @@ bsstest(void) printf(stdout, "bss test ok\n"); } +// does exec do something sensible if the arguments +// are larger than a page? +void +bigargtest(void) +{ + int pid, ppid; + + ppid = getpid(); + pid = fork(); + if(pid == 0){ + char *args[100]; + int i; + for(i = 0; i < 99; i++) + args[i] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + args[99] = 0; + printf(stdout, "bigarg test\n"); + exec("echo", args); + printf(stdout, "bigarg test ok\n"); + exit(); + } else if(pid < 0){ + printf(stdout, "bigargtest: fork failed\n"); + exit(); + } + wait(); +} + int main(int argc, char *argv[]) { @@ -1445,6 +1472,7 @@ main(int argc, char *argv[]) } close(open("usertests.ran", O_CREATE)); + // bigargtest(); bsstest(); sbrktest(); validatetest(); diff --git a/vm.c b/vm.c index c57fa53..cd6b255 100644 --- a/vm.c +++ b/vm.c @@ -125,6 +125,9 @@ pde_t* setupkvm(void) { pde_t *pgdir; + extern char etext[]; + char *rwstart = PGROUNDDOWN(etext) - PGSIZE; + uint rwlen = (uint)rwstart - 0x100000; // Allocate page directory if(!(pgdir = (pde_t *) kalloc())) @@ -132,8 +135,10 @@ setupkvm(void) memset(pgdir, 0, PGSIZE); if(// Map IO space from 640K to 1Mbyte !mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W) || - // Map kernel and free memory pool - !mappages(pgdir, (void *)0x100000, PHYSTOP-0x100000, 0x100000, PTE_W) || + // Map kernel instructions + !mappages(pgdir, (void *)0x100000, rwlen, 0x100000, 0) || + // Map kernel data and free memory pool + !mappages(pgdir, rwstart, PHYSTOP-(uint)rwstart, (uint)rwstart, PTE_W) || // Map devices such as ioapic, lapic, ... !mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) return 0; -- cgit v1.2.3 From 4655d42e3b65f906eae8c815fb78331790f6e423 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 27 Sep 2010 16:14:33 -0400 Subject: copyout() copies data to a va in a pagetable, for exec() &c usertest that passes too many arguments, break exec --- defs.h | 1 + exec.c | 93 ++++++++++++++++++++++++++++++++++++++----------------------- param.h | 2 ++ sysfile.c | 2 +- usertests.c | 10 +++---- vm.c | 32 ++++++++++++++++++--- 6 files changed, 95 insertions(+), 45 deletions(-) diff --git a/defs.h b/defs.h index 7408d4d..d08609d 100644 --- a/defs.h +++ b/defs.h @@ -165,6 +165,7 @@ int loaduvm(pde_t*, char*, struct inode *, uint, uint); pde_t* copyuvm(pde_t*,uint); void switchuvm(struct proc*); void switchkvm(); +int copyout(pde_t *pgdir, uint va, void *buf, uint len); // number of elements in fixed-size array #define NELEM(x) (sizeof(x)/sizeof((x)[0])) diff --git a/exec.c b/exec.c index 0a9ca59..2e2ced4 100644 --- a/exec.c +++ b/exec.c @@ -9,16 +9,13 @@ int exec(char *path, char **argv) { - char *mem, *s, *last; - int i, argc, arglen, len, off; - uint sz, sp, spbottom, argp; + char *s, *last; + int i, off; + uint sz = 0; struct elfhdr elf; - struct inode *ip; + struct inode *ip = 0; struct proghdr ph; - pde_t *pgdir, *oldpgdir; - - pgdir = 0; - sz = 0; + pde_t *pgdir = 0, *oldpgdir; if((ip = namei(path)) == 0) return -1; @@ -48,40 +45,65 @@ exec(char *path, char **argv) } iunlockput(ip); - // Allocate and initialize stack at sz - sz = spbottom = PGROUNDUP(sz); + // Allocate a one-page stack at the next page boundary + sz = PGROUNDUP(sz); if(!(sz = allocuvm(pgdir, sz, sz + PGSIZE))) goto bad; - mem = uva2ka(pgdir, (char *)spbottom); - - arglen = 0; - for(argc=0; argv[argc]; argc++) - arglen += strlen(argv[argc]) + 1; - arglen = (arglen+3) & ~3; - - sp = sz; - argp = sz - arglen - 4*(argc+1); - - // XXX rtm: does the following code work if the - // arguments &c do not fit in one page? - - // Copy argv strings and pointers to stack. - *(uint*)(mem+argp-spbottom + 4*argc) = 0; // argv[argc] - for(i=argc-1; i>=0; i--){ - len = strlen(argv[i]) + 1; - sp -= len; - memmove(mem+sp-spbottom, argv[i], len); - *(uint*)(mem+argp-spbottom + 4*i) = sp; // argv[i] + + // initialize stack content: + + // "argumentN" -- nul-terminated string + // ... + // "argument0" + // 0 -- argv[argc] + // address of argumentN + // ... + // address of argument0 -- argv[0] + // address of address of argument0 -- argv argument to main() + // argc -- argc argument to main() + // ffffffff -- return PC for main() call + + uint sp = sz; + + // count arguments + int argc; + for(argc = 0; argv[argc]; argc++) + ; + if(argc >= MAXARG) + goto bad; + + // push strings and remember where they are + uint strings[MAXARG]; + for(i = argc - 1; i >= 0; --i){ + sp -= strlen(argv[i]) + 1; + strings[i] = sp; + copyout(pgdir, sp, argv[i], strlen(argv[i]) + 1); + } + + // push 0 for argv[argc] + sp -= 4; + int zero = 0; + copyout(pgdir, sp, &zero, 4); + + // push argv[] elements + for(i = argc - 1; i >= 0; --i){ + sp -= 4; + copyout(pgdir, sp, &strings[i], 4); } - // Stack frame for main(argc, argv), below arguments. - sp = argp; + // push argv + uint argvaddr = sp; sp -= 4; - *(uint*)(mem+sp-spbottom) = argp; + copyout(pgdir, sp, &argvaddr, 4); + + // push argc sp -= 4; - *(uint*)(mem+sp-spbottom) = argc; + copyout(pgdir, sp, &argc, 4); + + // push 0 in case main returns sp -= 4; - *(uint*)(mem+sp-spbottom) = 0xffffffff; // fake return pc + uint ffffffff = 0xffffffff; + copyout(pgdir, sp, &ffffffff, 4); // Save program name for debugging. for(last=s=path; *s; s++) @@ -103,6 +125,7 @@ exec(char *path, char **argv) return 0; bad: + cprintf("kernel: exec failed\n"); if(pgdir) freevm(pgdir); iunlockput(ip); return -1; diff --git a/param.h b/param.h index 48c3352..70f88e8 100644 --- a/param.h +++ b/param.h @@ -7,4 +7,6 @@ #define NINODE 50 // maximum number of active i-nodes #define NDEV 10 // maximum major device number #define ROOTDEV 1 // device number of file system root disk +#define USERTOP 0xA0000 // end of user address space #define PHYSTOP 0x1000000 // use phys mem up to here as free pool +#define MAXARG 32 // max exec arguments diff --git a/sysfile.c b/sysfile.c index 6b8eef4..0b42920 100644 --- a/sysfile.c +++ b/sysfile.c @@ -344,7 +344,7 @@ sys_chdir(void) int sys_exec(void) { - char *path, *argv[20]; + char *path, *argv[MAXARG]; int i; uint uargv, uarg; diff --git a/usertests.c b/usertests.c index 177ffba..5d1d8ea 100644 --- a/usertests.c +++ b/usertests.c @@ -1445,11 +1445,11 @@ bigargtest(void) ppid = getpid(); pid = fork(); if(pid == 0){ - char *args[100]; + char *args[32]; int i; - for(i = 0; i < 99; i++) - args[i] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; - args[99] = 0; + for(i = 0; i < 32-1; i++) + args[i] = "bigargs test: failed\n "; + args[32-1] = 0; printf(stdout, "bigarg test\n"); exec("echo", args); printf(stdout, "bigarg test ok\n"); @@ -1472,7 +1472,7 @@ main(int argc, char *argv[]) } close(open("usertests.ran", O_CREATE)); - // bigargtest(); + bigargtest(); bsstest(); sbrktest(); validatetest(); diff --git a/vm.c b/vm.c index cd6b255..551efbf 100644 --- a/vm.c +++ b/vm.c @@ -6,8 +6,6 @@ #include "proc.h" #include "elf.h" -#define USERTOP 0xA0000 - static pde_t *kpgdir; // for use in scheduler() // Set up CPU's kernel segment descriptors. @@ -126,7 +124,7 @@ setupkvm(void) { pde_t *pgdir; extern char etext[]; - char *rwstart = PGROUNDDOWN(etext) - PGSIZE; + char *rwstart = PGROUNDDOWN(etext); uint rwlen = (uint)rwstart - 0x100000; // Allocate page directory @@ -193,7 +191,10 @@ char* uva2ka(pde_t *pgdir, char *uva) { pte_t *pte = walkpgdir(pgdir, uva, 0); - if(pte == 0) return 0; + if((*pte & PTE_P) == 0) + return 0; + if((*pte & PTE_U) == 0) + return 0; uint pa = PTE_ADDR(*pte); return (char *)pa; } @@ -326,3 +327,26 @@ bad: return 0; } +// copy some data to user address va in page table pgdir. +// most useful when pgdir is not the current page table. +// returns 1 if everthing OK, 0 on error. +// uva2ka ensures this only works for PTE_U pages. +int +copyout(pde_t *pgdir, uint va, void *xbuf, uint len) +{ + char *buf = (char *) xbuf; + while(len > 0){ + uint va0 = (uint)PGROUNDDOWN(va); + char *pa0 = uva2ka(pgdir, (char*) va0); + if(pa0 == 0) + return 0; + uint n = PGSIZE - (va - va0); + if(n > len) + n = len; + memmove(pa0 + (va - va0), buf, n); + len -= n; + buf += n; + va = va0 + PGSIZE; + } + return 1; +} -- cgit v1.2.3 From 06feabeceeccc8dbd2658e9f10dd139c14f01ba6 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 27 Sep 2010 16:17:57 -0400 Subject: check exec() arg length fix double iunlockput --- exec.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exec.c b/exec.c index 2e2ced4..c0ea515 100644 --- a/exec.c +++ b/exec.c @@ -44,6 +44,7 @@ exec(char *path, char **argv) goto bad; } iunlockput(ip); + ip = 0; // Allocate a one-page stack at the next page boundary sz = PGROUNDUP(sz); @@ -105,6 +106,9 @@ exec(char *path, char **argv) uint ffffffff = 0xffffffff; copyout(pgdir, sp, &ffffffff, 4); + if(sp < sz - PGSIZE) + goto bad; + // Save program name for debugging. for(last=s=path; *s; s++) if(*s == '/') @@ -125,8 +129,9 @@ exec(char *path, char **argv) return 0; bad: - cprintf("kernel: exec failed\n"); - if(pgdir) freevm(pgdir); - iunlockput(ip); + if(pgdir) + freevm(pgdir); + if(ip) + iunlockput(ip); return -1; } -- cgit v1.2.3 From 2ea6c764c34d3025b5f3121a0919fda7d1eb9b01 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 29 Sep 2010 14:12:26 -0400 Subject: even more fabulous exec --- exec.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/exec.c b/exec.c index c0ea515..1673a38 100644 --- a/exec.c +++ b/exec.c @@ -81,30 +81,19 @@ exec(char *path, char **argv) copyout(pgdir, sp, argv[i], strlen(argv[i]) + 1); } - // push 0 for argv[argc] - sp -= 4; - int zero = 0; - copyout(pgdir, sp, &zero, 4); +#define PUSH(x) { int xx = (int)(x); sp -= 4; copyout(pgdir, sp, &xx, 4); } + + PUSH(0); // argv[argc] is zero // push argv[] elements - for(i = argc - 1; i >= 0; --i){ - sp -= 4; - copyout(pgdir, sp, &strings[i], 4); - } + for(i = argc - 1; i >= 0; --i) + PUSH(strings[i]); - // push argv - uint argvaddr = sp; - sp -= 4; - copyout(pgdir, sp, &argvaddr, 4); + PUSH(sp); // argv - // push argc - sp -= 4; - copyout(pgdir, sp, &argc, 4); + PUSH(argc); - // push 0 in case main returns - sp -= 4; - uint ffffffff = 0xffffffff; - copyout(pgdir, sp, &ffffffff, 4); + PUSH(0xffffffff); // in case main tries to return if(sp < sz - PGSIZE) goto bad; -- cgit v1.2.3 From e92fd6142de05627096a1e831140e5bd355e45be Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 2 Jan 2011 17:59:57 -0500 Subject: mkfs: avoid out of bounds access to sb in wsect --- mkfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mkfs.c b/mkfs.c index 5f572cf..f227b5f 100644 --- a/mkfs.c +++ b/mkfs.c @@ -90,7 +90,9 @@ main(int argc, char *argv[]) for(i = 0; i < nblocks + usedblocks; i++) wsect(i, zeroes); - wsect(1, &sb); + memset(buf, 0, sizeof(buf)); + memmove(buf, &sb, sizeof(sb)); + wsect(1, buf); rootino = ialloc(T_DIR); assert(rootino == ROOTINO); -- cgit v1.2.3 From 240679608cd46649d1144408f28f83141f9f3a86 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 10 Jan 2011 10:43:40 -0500 Subject: mkfs: avoid clash with system struct stat --- mkfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mkfs.c b/mkfs.c index f227b5f..ffd6745 100644 --- a/mkfs.c +++ b/mkfs.c @@ -4,6 +4,8 @@ #include #include #include + +#define stat xv6_stat // avoid clash with host struct stat #include "types.h" #include "fs.h" #include "stat.h" -- cgit v1.2.3 From 1a81e38b17144624415d252a521fd5a06079d681 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 11 Jan 2011 13:01:13 -0500 Subject: make new code like old code Variable declarations at top of function, separate from initialization. Use == 0 instead of ! for checking pointers. Consistent spacing around {, *, casts. Declare 0-parameter functions as (void) not (). Integer valued functions return -1 on failure, 0 on success. --- bootmain.c | 2 +- console.c | 5 ++- defs.h | 8 ++-- exec.c | 24 +++++----- ide.c | 4 +- kalloc.c | 14 +++--- main.c | 30 ++++++------- mkfs.c | 24 +++++----- mmu.h | 14 +++--- mp.c | 4 +- pipe.c | 4 +- proc.c | 12 ++--- spinlock.c | 2 +- stressfs.c | 13 +++--- syscall.c | 9 ++-- sysfile.c | 2 +- ulib.c | 2 +- umalloc.c | 6 +-- user.h | 4 +- usertests.c | 100 +++++++++++++++++++++--------------------- vm.c | 143 +++++++++++++++++++++++++++++++++++------------------------- 21 files changed, 227 insertions(+), 199 deletions(-) diff --git a/bootmain.c b/bootmain.c index f67f8fa..7cd469f 100644 --- a/bootmain.c +++ b/bootmain.c @@ -33,7 +33,7 @@ bootmain(void) // Load each program segment (ignores ph flags). ph = (struct proghdr*)((uchar*)elf + elf->phoff); eph = ph + elf->phnum; - for(; ph < eph; ph++) { + for(; ph < eph; ph++){ va = (uchar*)ph->va; readseg(va, ph->filesz, ph->offset); if(ph->memsz > ph->filesz) diff --git a/console.c b/console.c index 19b3296..634e9e8 100644 --- a/console.c +++ b/console.c @@ -27,15 +27,16 @@ printint(int xx, int base, int sgn) { static char digits[] = "0123456789abcdef"; char buf[16]; - int i = 0, neg = 0; + int i, neg; uint x; - if(sgn && xx < 0){ + if(sgn && (neg = xx < 0)){ neg = 1; x = -xx; } else x = xx; + i = 0; do{ buf[i++] = digits[x % base]; }while((x /= base) != 0); diff --git a/defs.h b/defs.h index d08609d..43f35d2 100644 --- a/defs.h +++ b/defs.h @@ -62,7 +62,7 @@ void ioapicinit(void); // kalloc.c char* kalloc(void); void kfree(char*); -void kinit(); +void kinit(void); // kbd.c void kbdintr(void); @@ -117,8 +117,8 @@ void getcallerpcs(void*, uint*); int holding(struct spinlock*); void initlock(struct spinlock*, char*); void release(struct spinlock*); -void pushcli(); -void popcli(); +void pushcli(void); +void popcli(void); // string.c int memcmp(const void*, const void*, uint); @@ -164,7 +164,7 @@ void inituvm(pde_t*, char*, uint); int loaduvm(pde_t*, char*, struct inode *, uint, uint); pde_t* copyuvm(pde_t*,uint); void switchuvm(struct proc*); -void switchkvm(); +void switchkvm(void); int copyout(pde_t *pgdir, uint va, void *buf, uint len); // number of elements in fixed-size array diff --git a/exec.c b/exec.c index 1673a38..209bc79 100644 --- a/exec.c +++ b/exec.c @@ -10,16 +10,17 @@ int exec(char *path, char **argv) { char *s, *last; - int i, off; - uint sz = 0; + int i, off, argc; + uint sz, sp, strings[MAXARG]; struct elfhdr elf; - struct inode *ip = 0; + struct inode *ip; struct proghdr ph; - pde_t *pgdir = 0, *oldpgdir; + pde_t *pgdir, *oldpgdir; if((ip = namei(path)) == 0) return -1; ilock(ip); + pgdir = 0; // Check ELF header if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) @@ -27,10 +28,11 @@ exec(char *path, char **argv) if(elf.magic != ELF_MAGIC) goto bad; - if(!(pgdir = setupkvm())) + if((pgdir = setupkvm()) == 0) goto bad; // Load program into memory. + sz = 0; for(i=0, off=elf.phoff; i= MAXARG) goto bad; // push strings and remember where they are - uint strings[MAXARG]; for(i = argc - 1; i >= 0; --i){ sp -= strlen(argv[i]) + 1; strings[i] = sp; copyout(pgdir, sp, argv[i], strlen(argv[i]) + 1); } -#define PUSH(x) { int xx = (int)(x); sp -= 4; copyout(pgdir, sp, &xx, 4); } +#define PUSH(x){ int xx = (int)(x); sp -= 4; copyout(pgdir, sp, &xx, 4); } PUSH(0); // argv[argc] is zero diff --git a/ide.c b/ide.c index 7b12aa0..f4bd210 100644 --- a/ide.c +++ b/ide.c @@ -131,7 +131,7 @@ iderw(struct buf *b) if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) panic("iderw: nothing to do"); if(b->dev != 0 && !havedisk1) - panic("idrw: ide disk 1 not present"); + panic("iderw: ide disk 1 not present"); acquire(&idelock); @@ -147,7 +147,7 @@ iderw(struct buf *b) // Wait for request to finish. // Assuming will not sleep too long: ignore proc->killed. - while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) { + while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ sleep(b, &idelock); } diff --git a/kalloc.c b/kalloc.c index 72ce58a..e31d71d 100644 --- a/kalloc.c +++ b/kalloc.c @@ -23,9 +23,11 @@ extern char end[]; // first address after kernel loaded from ELF file void kinit(void) { + char *p; + initlock(&kmem.lock, "kmem"); - char *p = (char*)PGROUNDUP((uint)end); - for( ; p + PGSIZE - 1 < (char*) PHYSTOP; p += PGSIZE) + p = (char*)PGROUNDUP((uint)end); + for(; p + PGSIZE - 1 < (char*)PHYSTOP; p += PGSIZE) kfree(p); } @@ -39,14 +41,14 @@ kfree(char *v) { struct run *r; - if(((uint) v) % PGSIZE || v < end || (uint)v >= PHYSTOP) + if((uint)v % PGSIZE || v < end || (uint)v >= PHYSTOP) panic("kfree"); // Fill with junk to catch dangling refs. memset(v, 1, PGSIZE); acquire(&kmem.lock); - r = (struct run *) v; + r = (struct run*)v; r->next = kmem.freelist; kmem.freelist = r; release(&kmem.lock); @@ -56,7 +58,7 @@ kfree(char *v) // Returns a pointer that the kernel can use. // Returns 0 if the memory cannot be allocated. char* -kalloc() +kalloc(void) { struct run *r; @@ -65,6 +67,6 @@ kalloc() if(r) kmem.freelist = r->next; release(&kmem.lock); - return (char*) r; + return (char*)r; } diff --git a/main.c b/main.c index beac4da..8a5f7f1 100644 --- a/main.c +++ b/main.c @@ -7,7 +7,7 @@ static void bootothers(void); static void mpmain(void); -void jkstack(void) __attribute__((noreturn)); +void jmpkstack(void) __attribute__((noreturn)); void mainc(void); // Bootstrap processor starts running C code here. @@ -20,19 +20,20 @@ main(void) lapicinit(mpbcpu()); seginit(); // set up segments kinit(); // initialize memory allocator - jkstack(); // call mainc() on a properly-allocated stack + jmpkstack(); // call mainc() on a properly-allocated stack } void -jkstack(void) +jmpkstack(void) { - char *kstack = kalloc(); - if(!kstack) - panic("jkstack\n"); - char *top = kstack + PGSIZE; - asm volatile("movl %0,%%esp" : : "r" (top)); - asm volatile("call mainc"); - panic("jkstack"); + char *kstack, *top; + + kstack = kalloc(); + if(kstack == 0) + panic("jmpkstack kalloc"); + top = kstack + PGSIZE; + asm volatile("movl %0,%%esp; call mainc" : : "r" (top)); + panic("jmpkstack"); } // Set up hardware and software. @@ -67,7 +68,7 @@ mainc(void) static void mpmain(void) { - if(cpunum() != mpbcpu()) { + if(cpunum() != mpbcpu()){ seginit(); lapicinit(cpunum()); } @@ -87,9 +88,9 @@ bootothers(void) struct cpu *c; char *stack; - // Write bootstrap code to unused memory at 0x7000. The linker has - // placed the start of bootother.S there. - code = (uchar *) 0x7000; + // Write bootstrap code to unused memory at 0x7000. + // The linker has placed the image of bootother.S in _binary_bootother_start. + code = (uchar*)0x7000; memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); for(c = cpus; c < cpus+ncpu; c++){ @@ -110,4 +111,3 @@ bootothers(void) ; } } - diff --git a/mkfs.c b/mkfs.c index ffd6745..77e6791 100644 --- a/mkfs.c +++ b/mkfs.c @@ -35,7 +35,7 @@ ushort xshort(ushort x) { ushort y; - uchar *a = (uchar*) &y; + uchar *a = (uchar*)&y; a[0] = x; a[1] = x >> 8; return y; @@ -45,7 +45,7 @@ uint xint(uint x) { uint y; - uchar *a = (uchar*) &y; + uchar *a = (uchar*)&y; a[0] = x; a[1] = x >> 8; a[2] = x >> 16; @@ -177,7 +177,7 @@ winode(uint inum, struct dinode *ip) bn = i2b(inum); rsect(bn, buf); - dip = ((struct dinode*) buf) + (inum % IPB); + dip = ((struct dinode*)buf) + (inum % IPB); *dip = *ip; wsect(bn, buf); } @@ -191,7 +191,7 @@ rinode(uint inum, struct dinode *ip) bn = i2b(inum); rsect(bn, buf); - dip = ((struct dinode*) buf) + (inum % IPB); + dip = ((struct dinode*)buf) + (inum % IPB); *ip = *dip; } @@ -231,7 +231,7 @@ balloc(int used) printf("balloc: first %d blocks have been allocated\n", used); assert(used < 512); bzero(buf, 512); - for(i = 0; i < used; i++) { + for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); } printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3); @@ -243,7 +243,7 @@ balloc(int used) void iappend(uint inum, void *xp, int n) { - char *p = (char*) xp; + char *p = (char*)xp; uint fbn, off, n1; struct dinode din; char buf[512]; @@ -256,24 +256,24 @@ iappend(uint inum, void *xp, int n) while(n > 0){ fbn = off / 512; assert(fbn < MAXFILE); - if(fbn < NDIRECT) { - if(xint(din.addrs[fbn]) == 0) { + if(fbn < NDIRECT){ + if(xint(din.addrs[fbn]) == 0){ din.addrs[fbn] = xint(freeblock++); usedblocks++; } x = xint(din.addrs[fbn]); } else { - if(xint(din.addrs[NDIRECT]) == 0) { + if(xint(din.addrs[NDIRECT]) == 0){ // printf("allocate indirect block\n"); din.addrs[NDIRECT] = xint(freeblock++); usedblocks++; } // printf("read indirect block\n"); - rsect(xint(din.addrs[NDIRECT]), (char*) indirect); - if(indirect[fbn - NDIRECT] == 0) { + rsect(xint(din.addrs[NDIRECT]), (char*)indirect); + if(indirect[fbn - NDIRECT] == 0){ indirect[fbn - NDIRECT] = xint(freeblock++); usedblocks++; - wsect(xint(din.addrs[NDIRECT]), (char*) indirect); + wsect(xint(din.addrs[NDIRECT]), (char*)indirect); } x = xint(indirect[fbn-NDIRECT]); } diff --git a/mmu.h b/mmu.h index 475eae8..2d88a52 100644 --- a/mmu.h +++ b/mmu.h @@ -98,18 +98,18 @@ struct segdesc { // \--- PDX(la) --/ \--- PTX(la) --/ // page directory index -#define PDX(la) ((((uint) (la)) >> PDXSHIFT) & 0x3FF) +#define PDX(la) (((uint)(la) >> PDXSHIFT) & 0x3FF) // page table index -#define PTX(la) ((((uint) (la)) >> PTXSHIFT) & 0x3FF) +#define PTX(la) (((uint)(la) >> PTXSHIFT) & 0x3FF) // construct linear address from indexes and offset -#define PGADDR(d, t, o) ((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) +#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) // turn a kernel linear address into a physical address. // all of the kernel data structures have linear and // physical addresses that are equal. -#define PADDR(a) ((uint) a) +#define PADDR(a) ((uint)(a)) // Page directory and page table constants. #define NPDENTRIES 1024 // page directory entries per page directory @@ -136,7 +136,7 @@ struct segdesc { #define PTE_MBZ 0x180 // Bits must be zero // Address in page table or page directory entry -#define PTE_ADDR(pte) ((uint) (pte) & ~0xFFF) +#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF) typedef uint pte_t; @@ -205,7 +205,7 @@ struct gatedesc { // this interrupt/trap gate explicitly using an int instruction. #define SETGATE(gate, istrap, sel, off, d) \ { \ - (gate).off_15_0 = (uint) (off) & 0xffff; \ + (gate).off_15_0 = (uint)(off) & 0xffff; \ (gate).cs = (sel); \ (gate).args = 0; \ (gate).rsv1 = 0; \ @@ -213,6 +213,6 @@ struct gatedesc { (gate).s = 0; \ (gate).dpl = (d); \ (gate).p = 1; \ - (gate).off_31_16 = (uint) (off) >> 16; \ + (gate).off_31_16 = (uint)(off) >> 16; \ } diff --git a/mp.c b/mp.c index d2f828a..edabdd6 100644 --- a/mp.c +++ b/mp.c @@ -113,8 +113,8 @@ mpinit(void) switch(*p){ case MPPROC: proc = (struct mpproc*)p; - if(ncpu != proc->apicid) { - cprintf("mpinit: ncpu=%d apicpid=%d", ncpu, proc->apicid); + if(ncpu != proc->apicid){ + cprintf("mpinit: ncpu=%d apicpid=%d\n", ncpu, proc->apicid); panic("mpinit"); } if(proc->flags & MPBOOT) diff --git a/pipe.c b/pipe.c index bc847b9..f76ed5c 100644 --- a/pipe.c +++ b/pipe.c @@ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable) p->readopen = 0; wakeup(&p->nwrite); } - if(p->readopen == 0 && p->writeopen == 0) { + if(p->readopen == 0 && p->writeopen == 0){ release(&p->lock); kfree((char*)p); } else @@ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n) acquire(&p->lock); for(i = 0; i < n; i++){ - while(p->nwrite == p->nread + PIPESIZE) { //DOC: pipewrite-full + while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full if(p->readopen == 0 || proc->killed){ release(&p->lock); return -1; diff --git a/proc.c b/proc.c index 2e8a0a4..e6ccd9d 100644 --- a/proc.c +++ b/proc.c @@ -120,7 +120,7 @@ userinit(void) p = allocproc(); initproc = p; - if(!(p->pgdir = setupkvm())) + if((p->pgdir = setupkvm()) == 0) panic("userinit: out of memory?"); inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); p->sz = PGSIZE; @@ -144,12 +144,14 @@ userinit(void) int growproc(int n) { - uint sz = proc->sz; + uint sz; + + sz = proc->sz; if(n > 0){ - if(!(sz = allocuvm(proc->pgdir, sz, sz + n))) + if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0) return -1; } else if(n < 0){ - if(!(sz = deallocuvm(proc->pgdir, sz, sz + n))) + if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0) return -1; } proc->sz = sz; @@ -171,7 +173,7 @@ fork(void) return -1; // Copy process state from p. - if(!(np->pgdir = copyuvm(proc->pgdir, proc->sz))){ + if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){ kfree(np->kstack); np->kstack = 0; np->state = UNUSED; diff --git a/spinlock.c b/spinlock.c index 281748a..e668598 100644 --- a/spinlock.c +++ b/spinlock.c @@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[]) ebp = (uint*)v - 2; for(i = 0; i < 10; i++){ - if(ebp == 0 || ebp < (uint *) 0x100000 || ebp == (uint*)0xffffffff) + if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff) break; pcs[i] = ebp[1]; // saved %eip ebp = (uint*)ebp[0]; // saved %ebp diff --git a/stressfs.c b/stressfs.c index 43f312b..5d4fee2 100644 --- a/stressfs.c +++ b/stressfs.c @@ -14,20 +14,19 @@ int main(int argc, char *argv[]) { - int i; + int fd, i; + char path[] = "stressfs0"; + printf(1, "stressfs starting\n"); - for(i = 0; i < 4; i++){ - if(fork() > 0){ + for(i = 0; i < 4; i++) + if(fork() > 0) break; - } - } printf(1, "%d\n", i); - char path[] = "stressfs0"; path[8] += i; - int fd = open(path, O_CREATE | O_RDWR); + fd = open(path, O_CREATE | O_RDWR); for(i = 0; i < 100; i++) printf(fd, "%d\n", i); close(fd); diff --git a/syscall.c b/syscall.c index f8aeae7..16c5b47 100644 --- a/syscall.c +++ b/syscall.c @@ -32,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp) if(addr >= p->sz) return -1; - *pp = (char *) addr; - ep = (char *) p->sz; + *pp = (char*)addr; + ep = (char*)p->sz; for(s = *pp; s < ep; s++) if(*s == 0) return s - *pp; @@ -44,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp) int argint(int n, int *ip) { - int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip); - return x; + return fetchint(proc, proc->tf->esp + 4 + 4*n, ip); } // Fetch the nth word-sized system call argument as a pointer @@ -60,7 +59,7 @@ argptr(int n, char **pp, int size) return -1; if((uint)i >= proc->sz || (uint)i+size >= proc->sz) return -1; - *pp = (char *) i; + *pp = (char*)i; return 0; } diff --git a/sysfile.c b/sysfile.c index 0b42920..4235660 100644 --- a/sysfile.c +++ b/sysfile.c @@ -348,7 +348,7 @@ sys_exec(void) int i; uint uargv, uarg; - if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) { + if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ return -1; } memset(argv, 0, sizeof(argv)); diff --git a/ulib.c b/ulib.c index 0268c26..dbbcfcf 100644 --- a/ulib.c +++ b/ulib.c @@ -45,7 +45,7 @@ strchr(const char *s, char c) { for(; *s; s++) if(*s == c) - return (char*) s; + return (char*)s; return 0; } diff --git a/umalloc.c b/umalloc.c index 4984591..a7e7d2c 100644 --- a/umalloc.c +++ b/umalloc.c @@ -26,7 +26,7 @@ free(void *ap) { Header *bp, *p; - bp = (Header*) ap - 1; + bp = (Header*)ap - 1; for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) break; @@ -52,7 +52,7 @@ morecore(uint nu) if(nu < 4096) nu = 4096; p = sbrk(nu * sizeof(Header)); - if(p == (char*) -1) + if(p == (char*)-1) return 0; hp = (Header*)p; hp->s.size = nu; @@ -81,7 +81,7 @@ malloc(uint nbytes) p->s.size = nunits; } freep = prevp; - return (void*) (p + 1); + return (void*)(p + 1); } if(p == freep) if((p = morecore(nunits)) == 0) diff --git a/user.h b/user.h index 431428c..9e26cf1 100644 --- a/user.h +++ b/user.h @@ -18,10 +18,10 @@ int link(char*, char*); int mkdir(char*); int chdir(char*); int dup(int); -int getpid(); +int getpid(void); char* sbrk(int); int sleep(int); -int uptime(); +int uptime(void); // ulib.c int stat(char*, struct stat*); diff --git a/usertests.c b/usertests.c index 5d1d8ea..9a83591 100644 --- a/usertests.c +++ b/usertests.c @@ -47,12 +47,12 @@ writetest(void) printf(stdout, "error: creat small failed!\n"); exit(); } - for(i = 0; i < 100; i++) { - if(write(fd, "aaaaaaaaaa", 10) != 10) { + for(i = 0; i < 100; i++){ + if(write(fd, "aaaaaaaaaa", 10) != 10){ printf(stdout, "error: write aa %d new file failed\n", i); exit(); } - if(write(fd, "bbbbbbbbbb", 10) != 10) { + if(write(fd, "bbbbbbbbbb", 10) != 10){ printf(stdout, "error: write bb %d new file failed\n", i); exit(); } @@ -67,7 +67,7 @@ writetest(void) exit(); } i = read(fd, buf, 2000); - if(i == 2000) { + if(i == 2000){ printf(stdout, "read succeeded ok\n"); } else { printf(stdout, "read failed\n"); @@ -75,7 +75,7 @@ writetest(void) } close(fd); - if(unlink("small") < 0) { + if(unlink("small") < 0){ printf(stdout, "unlink small failed\n"); exit(); } @@ -95,9 +95,9 @@ writetest1(void) exit(); } - for(i = 0; i < MAXFILE; i++) { - ((int*) buf)[0] = i; - if(write(fd, buf, 512) != 512) { + for(i = 0; i < MAXFILE; i++){ + ((int*)buf)[0] = i; + if(write(fd, buf, 512) != 512){ printf(stdout, "error: write big file failed\n", i); exit(); } @@ -112,19 +112,19 @@ writetest1(void) } n = 0; - for(;;) { + for(;;){ i = read(fd, buf, 512); - if(i == 0) { - if(n == MAXFILE - 1) { + if(i == 0){ + if(n == MAXFILE - 1){ printf(stdout, "read only %d blocks from big", n); exit(); } break; - } else if(i != 512) { + } else if(i != 512){ printf(stdout, "read failed %d\n", i); exit(); } - if(((int*)buf)[0] != n) { + if(((int*)buf)[0] != n){ printf(stdout, "read content of block %d is %d\n", n, ((int*)buf)[0]); exit(); @@ -132,7 +132,7 @@ writetest1(void) n++; } close(fd); - if(unlink("big") < 0) { + if(unlink("big") < 0){ printf(stdout, "unlink big failed\n"); exit(); } @@ -148,14 +148,14 @@ createtest(void) name[0] = 'a'; name[2] = '\0'; - for(i = 0; i < 52; i++) { + for(i = 0; i < 52; i++){ name[1] = '0' + i; fd = open(name, O_CREATE|O_RDWR); close(fd); } name[0] = 'a'; name[2] = '\0'; - for(i = 0; i < 52; i++) { + for(i = 0; i < 52; i++){ name[1] = '0' + i; unlink(name); } @@ -166,22 +166,22 @@ void dirtest(void) { printf(stdout, "mkdir test\n"); - if(mkdir("dir0") < 0) { + if(mkdir("dir0") < 0){ printf(stdout, "mkdir failed\n"); exit(); } - if(chdir("dir0") < 0) { + if(chdir("dir0") < 0){ printf(stdout, "chdir dir0 failed\n"); exit(); } - if(chdir("..") < 0) { + if(chdir("..") < 0){ printf(stdout, "chdir .. failed\n"); exit(); } - if(unlink("dir0") < 0) { + if(unlink("dir0") < 0){ printf(stdout, "unlink dir0 failed\n"); exit(); } @@ -192,7 +192,7 @@ void exectest(void) { printf(stdout, "exec test\n"); - if(exec("echo", echoargv) < 0) { + if(exec("echo", echoargv) < 0){ printf(stdout, "exec echo failed\n"); exit(); } @@ -330,17 +330,17 @@ mem(void) ppid = getpid(); if((pid = fork()) == 0){ m1 = 0; - while((m2 = malloc(10001)) != 0) { - *(char**) m2 = m1; + while((m2 = malloc(10001)) != 0){ + *(char**)m2 = m1; m1 = m2; } - while(m1) { + while(m1){ m2 = *(char**)m1; free(m1); m1 = m2; } m1 = malloc(1024*20); - if(m1 == 0) { + if(m1 == 0){ printf(1, "couldn't allocate mem?!!\n"); kill(ppid); exit(); @@ -1237,16 +1237,18 @@ forktest(void) void sbrktest(void) { - int pid; - char *oldbrk = sbrk(0); + int fds[2], pid, pids[32], ppid; + char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch; + uint amt; printf(stdout, "sbrk test\n"); + oldbrk = sbrk(0); // can one sbrk() less than a page? - char *a = sbrk(0); + a = sbrk(0); int i; for(i = 0; i < 5000; i++){ - char *b = sbrk(1); + b = sbrk(1); if(b != a){ printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); exit(); @@ -1259,7 +1261,7 @@ sbrktest(void) printf(stdout, "sbrk test fork failed\n"); exit(); } - char *c = sbrk(1); + c = sbrk(1); c = sbrk(1); if(c != a + 1){ printf(stdout, "sbrk test failed post-fork\n"); @@ -1271,18 +1273,18 @@ sbrktest(void) // can one allocate the full 640K? a = sbrk(0); - uint amt = (640 * 1024) - (uint) a; - char *p = sbrk(amt); + amt = (640 * 1024) - (uint)a; + p = sbrk(amt); if(p != a){ printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a); exit(); } - char *lastaddr = (char *)(640 * 1024 - 1); + lastaddr = (char*)(640 * 1024 - 1); *lastaddr = 99; // is one forbidden from allocating more than 640K? c = sbrk(4096); - if(c != (char *) 0xffffffff){ + if(c != (char*)0xffffffff){ printf(stdout, "sbrk allocated more than 640K, c %x\n", c); exit(); } @@ -1290,7 +1292,7 @@ sbrktest(void) // can one de-allocate? a = sbrk(0); c = sbrk(-4096); - if(c == (char *) 0xffffffff){ + if(c == (char*)0xffffffff){ printf(stdout, "sbrk could not deallocate\n"); exit(); } @@ -1314,15 +1316,15 @@ sbrktest(void) } c = sbrk(4096); - if(c != (char *) 0xffffffff){ + if(c != (char*)0xffffffff){ printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c); exit(); } // can we read the kernel's memory? - for(a = (char*)(640*1024); a < (char *)2000000; a += 50000){ - int ppid = getpid(); - int pid = fork(); + for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){ + ppid = getpid(); + pid = fork(); if(pid < 0){ printf(stdout, "fork failed\n"); exit(); @@ -1338,21 +1340,18 @@ sbrktest(void) // if we run the system out of memory, does it clean up the last // failed allocation? sbrk(-(sbrk(0) - oldbrk)); - int pids[32]; - int fds[2]; if(pipe(fds) != 0){ printf(1, "pipe() failed\n"); exit(); } for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ - if((pids[i] = fork()) == 0) { + if((pids[i] = fork()) == 0){ // allocate the full 640K sbrk((640 * 1024) - (uint)sbrk(0)); write(fds[1], "x", 1); // sit around until killed for(;;) sleep(1000); } - char scratch; if(pids[i] != -1) read(fds[0], &scratch, 1); } @@ -1365,7 +1364,7 @@ sbrktest(void) kill(pids[i]); wait(); } - if(c == (char*)0xffffffff) { + if(c == (char*)0xffffffff){ printf(stdout, "failed sbrk leaked memory\n"); exit(); } @@ -1392,14 +1391,14 @@ validateint(int *p) void validatetest(void) { - int hi = 1100*1024; + int hi, pid; + uint p; printf(stdout, "validate test\n"); + hi = 1100*1024; - uint p; - for (p = 0; p <= (uint)hi; p += 4096) { - int pid; - if ((pid = fork()) == 0) { + for(p = 0; p <= (uint)hi; p += 4096){ + if((pid = fork()) == 0){ // try to crash the kernel by passing in a badly placed integer validateint((int*)p); exit(); @@ -1410,7 +1409,7 @@ validatetest(void) wait(); // try to crash the kernel by passing in a bad string pointer - if (link("nosuchfile", (char*)p) != -1) { + if(link("nosuchfile", (char*)p) != -1){ printf(stdout, "link should not succeed\n"); exit(); } @@ -1425,6 +1424,7 @@ void bsstest(void) { int i; + printf(stdout, "bss test\n"); for(i = 0; i < sizeof(uninit); i++){ if(uninit[i] != '\0'){ diff --git a/vm.c b/vm.c index 551efbf..940cc2f 100644 --- a/vm.c +++ b/vm.c @@ -42,23 +42,21 @@ seginit(void) static pte_t * walkpgdir(pde_t *pgdir, const void *va, int create) { - uint r; pde_t *pde; pte_t *pgtab; pde = &pgdir[PDX(va)]; if(*pde & PTE_P){ - pgtab = (pte_t*) PTE_ADDR(*pde); - } else if(!create || !(r = (uint) kalloc())) - return 0; - else { - pgtab = (pte_t*) r; + pgtab = (pte_t*)PTE_ADDR(*pde); + } else { + if(!create || (pgtab = (pte_t*)kalloc()) == 0) + return 0; // Make sure all those PTE_P bits are zero. memset(pgtab, 0, PGSIZE); // The permissions here are overly generous, but they can // be further restricted by the permissions in the page table // entries, if necessary. - *pde = PADDR(r) | PTE_P | PTE_W | PTE_U; + *pde = PADDR(pgtab) | PTE_P | PTE_W | PTE_U; } return &pgtab[PTX(va)]; } @@ -69,13 +67,16 @@ walkpgdir(pde_t *pgdir, const void *va, int create) static int mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) { - char *a = PGROUNDDOWN(la); - char *last = PGROUNDDOWN(la + size - 1); + char *a, *last; + pte_t *pte; + + a = PGROUNDDOWN(la); + last = PGROUNDDOWN(la + size - 1); - while(1){ - pte_t *pte = walkpgdir(pgdir, a, 1); + for(;;){ + pte = walkpgdir(pgdir, a, 1); if(pte == 0) - return 0; + return -1; if(*pte & PTE_P) panic("remap"); *pte = pa | perm | PTE_P; @@ -84,7 +85,7 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) a += PGSIZE; pa += PGSIZE; } - return 1; + return 0; } // The mappings from logical to linear are one to one (i.e., @@ -122,23 +123,26 @@ kvmalloc(void) pde_t* setupkvm(void) { - pde_t *pgdir; extern char etext[]; - char *rwstart = PGROUNDDOWN(etext); - uint rwlen = (uint)rwstart - 0x100000; + char *rwstart; + pde_t *pgdir; + uint rwlen; + + rwstart = PGROUNDDOWN(etext); + rwlen = (uint)rwstart - 0x100000; // Allocate page directory - if(!(pgdir = (pde_t *) kalloc())) + if((pgdir = (pde_t*)kalloc()) == 0) return 0; memset(pgdir, 0, PGSIZE); if(// Map IO space from 640K to 1Mbyte - !mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W) || + mappages(pgdir, (void*)USERTOP, 0x60000, USERTOP, PTE_W) < 0 || // Map kernel instructions - !mappages(pgdir, (void *)0x100000, rwlen, 0x100000, 0) || + mappages(pgdir, (void*)0x100000, rwlen, 0x100000, 0) < 0 || // Map kernel data and free memory pool - !mappages(pgdir, rwstart, PHYSTOP-(uint)rwstart, (uint)rwstart, PTE_W) || + mappages(pgdir, rwstart, PHYSTOP-(uint)rwstart, (uint)rwstart, PTE_W) < 0 || // Map devices such as ioapic, lapic, ... - !mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) + mappages(pgdir, (void*)0xFE000000, 0x2000000, 0xFE000000, PTE_W) < 0) return 0; return pgdir; } @@ -189,14 +193,15 @@ switchuvm(struct proc *p) // processes directly. char* uva2ka(pde_t *pgdir, char *uva) -{ - pte_t *pte = walkpgdir(pgdir, uva, 0); +{ + pte_t *pte; + + pte = walkpgdir(pgdir, uva, 0); if((*pte & PTE_P) == 0) return 0; if((*pte & PTE_U) == 0) return 0; - uint pa = PTE_ADDR(*pte); - return (char *)pa; + return (char*)PTE_ADDR(*pte); } // Load the initcode into address 0 of pgdir. @@ -204,9 +209,11 @@ uva2ka(pde_t *pgdir, char *uva) void inituvm(pde_t *pgdir, char *init, uint sz) { - char *mem = kalloc(); - if (sz >= PGSIZE) + char *mem; + + if(sz >= PGSIZE) panic("inituvm: more than a page"); + mem = kalloc(); memset(mem, 0, PGSIZE); mappages(pgdir, 0, PGSIZE, PADDR(mem), PTE_W|PTE_U); memmove(mem, init, sz); @@ -223,15 +230,17 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) if((uint)addr % PGSIZE != 0) panic("loaduvm: addr must be page aligned\n"); for(i = 0; i < sz; i += PGSIZE){ - if(!(pte = walkpgdir(pgdir, addr+i, 0))) + if((pte = walkpgdir(pgdir, addr+i, 0)) == 0) panic("loaduvm: address should exist\n"); pa = PTE_ADDR(*pte); - if(sz - i < PGSIZE) n = sz - i; - else n = PGSIZE; - if(readi(ip, (char *)pa, offset+i, n) != n) - return 0; + if(sz - i < PGSIZE) + n = sz - i; + else + n = PGSIZE; + if(readi(ip, (char*)pa, offset+i, n) != n) + return -1; } - return 1; + return 0; } // Allocate memory to the process to bring its size from oldsz to @@ -241,12 +250,17 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) int allocuvm(pde_t *pgdir, uint oldsz, uint newsz) { + char *a, *last, *mem; + if(newsz > USERTOP) return 0; - char *a = (char *)PGROUNDUP(oldsz); - char *last = PGROUNDDOWN(newsz - 1); - for (; a <= last; a += PGSIZE){ - char *mem = kalloc(); + if(newsz < oldsz) + return oldsz; + + a = (char*)PGROUNDUP(oldsz); + last = PGROUNDDOWN(newsz - 1); + for(; a <= last; a += PGSIZE){ + mem = kalloc(); if(mem == 0){ cprintf("allocuvm out of memory\n"); deallocuvm(pgdir, newsz, oldsz); @@ -255,7 +269,7 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) memset(mem, 0, PGSIZE); mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U); } - return newsz > oldsz ? newsz : oldsz; + return newsz; } // Deallocate user pages to bring the process size from oldsz to @@ -265,19 +279,26 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) int deallocuvm(pde_t *pgdir, uint oldsz, uint newsz) { - char *a = (char *)PGROUNDUP(newsz); - char *last = PGROUNDDOWN(oldsz - 1); + char *a, *last; + pte_t *pte; + uint pa; + + if(newsz >= oldsz) + return oldsz; + + a = (char*)PGROUNDUP(newsz); + last = PGROUNDDOWN(oldsz - 1); for(; a <= last; a += PGSIZE){ - pte_t *pte = walkpgdir(pgdir, a, 0); + pte = walkpgdir(pgdir, a, 0); if(pte && (*pte & PTE_P) != 0){ - uint pa = PTE_ADDR(*pte); + pa = PTE_ADDR(*pte); if(pa == 0) panic("kfree"); - kfree((void *) pa); + kfree((char*)pa); *pte = 0; } } - return newsz < oldsz ? newsz : oldsz; + return newsz; } // Free a page table and all the physical memory pages @@ -287,14 +308,14 @@ freevm(pde_t *pgdir) { uint i; - if(!pgdir) + if(pgdir == 0) panic("freevm: no pgdir"); deallocuvm(pgdir, USERTOP, 0); for(i = 0; i < NPDENTRIES; i++){ if(pgdir[i] & PTE_P) - kfree((void *) PTE_ADDR(pgdir[i])); + kfree((char*)PTE_ADDR(pgdir[i])); } - kfree((void *) pgdir); + kfree((char*)pgdir); } // Given a parent process's page table, create a copy @@ -302,22 +323,23 @@ freevm(pde_t *pgdir) pde_t* copyuvm(pde_t *pgdir, uint sz) { - pde_t *d = setupkvm(); + pde_t *d; pte_t *pte; uint pa, i; char *mem; - if(!d) return 0; + if((d = setupkvm()) == 0) + return 0; for(i = 0; i < sz; i += PGSIZE){ - if(!(pte = walkpgdir(pgdir, (void *)i, 0))) + if((pte = walkpgdir(pgdir, (void*)i, 0)) == 0) panic("copyuvm: pte should exist\n"); if(!(*pte & PTE_P)) panic("copyuvm: page not present\n"); pa = PTE_ADDR(*pte); - if(!(mem = kalloc())) + if((mem = kalloc()) == 0) goto bad; - memmove(mem, (char *)pa, PGSIZE); - if(!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) + memmove(mem, (char*)pa, PGSIZE); + if(mappages(d, (void*)i, PGSIZE, PADDR(mem), PTE_W|PTE_U) < 0) goto bad; } return d; @@ -334,13 +356,16 @@ bad: int copyout(pde_t *pgdir, uint va, void *xbuf, uint len) { - char *buf = (char *) xbuf; + char *buf, *pa0; + uint n, va0; + + buf = (char*)xbuf; while(len > 0){ - uint va0 = (uint)PGROUNDDOWN(va); - char *pa0 = uva2ka(pgdir, (char*) va0); + va0 = (uint)PGROUNDDOWN(va); + pa0 = uva2ka(pgdir, (char*)va0); if(pa0 == 0) - return 0; - uint n = PGSIZE - (va - va0); + return -1; + n = PGSIZE - (va - va0); if(n > len) n = len; memmove(pa0 + (va - va0), buf, n); @@ -348,5 +373,5 @@ copyout(pde_t *pgdir, uint va, void *xbuf, uint len) buf += n; va = va0 + PGSIZE; } - return 1; + return 0; } -- cgit v1.2.3 From af6a6a477531aefa6e961c464de495e0c09af673 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 11 Jan 2011 13:16:28 -0500 Subject: mp: do not panic on surprising hw --- mp.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mp.c b/mp.c index edabdd6..44ee020 100644 --- a/mp.c +++ b/mp.c @@ -113,9 +113,10 @@ mpinit(void) switch(*p){ case MPPROC: proc = (struct mpproc*)p; + cprintf("mpproc %d\n", proc->apicid); if(ncpu != proc->apicid){ - cprintf("mpinit: ncpu=%d apicpid=%d\n", ncpu, proc->apicid); - panic("mpinit"); + cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid); + ismp = 0; } if(proc->flags & MPBOOT) bcpu = &cpus[ncpu]; @@ -135,9 +136,17 @@ mpinit(void) continue; default: cprintf("mpinit: unknown config type %x\n", *p); - panic("mpinit"); + ismp = 0; } } + if(!ismp){ + // Didn't like what we found; fall back to no MP. + ncpu = 1; + lapic = 0; + ioapicid = 0; + return; + } + if(mp->imcrp){ // Bochs doesn't support IMCR, so this doesn't run on Bochs. // But it would on real hardware. -- cgit v1.2.3 From 89bfdd4db183cbe75a3a0c2254ca48a50e37276f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 11 Jan 2011 13:27:45 -0500 Subject: multiboot support and memory-only (no disk) kernel --- Makefile | 24 ++++++++++++++++++-- multiboot.S | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 multiboot.S diff --git a/Makefile b/Makefile index 95247ff..980543a 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,11 @@ xv6.img: bootblock kernel fs.img dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc +xv6memfs.img: bootblock kernelmemfs + dd if=/dev/zero of=xv6memfs.img count=10000 + dd if=bootblock of=xv6memfs.img conv=notrunc + dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc + bootblock: bootasm.S bootmain.c $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S @@ -102,11 +107,23 @@ initcode: initcode.S $(OBJCOPY) -S -O binary initcode.out initcode $(OBJDUMP) -S initcode.o > initcode.asm -kernel: $(OBJS) bootother initcode - $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother +kernel: $(OBJS) multiboot.o bootother initcode + $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o $(OBJS) -b binary initcode bootother fs.img $(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym +# kernelmemfs is a copy of kernel that maintains the +# disk image in memory instead of writing to a disk. +# This is not so useful for testing persistent storage or +# exploring disk buffering implementations, but it is +# great for testing the kernel on real hardware without +# needing a scratch disk. +MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o +kernelmemfs: $(MEMFSOBJS) multiboot.o bootother initcode fs.img + $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o $(MEMFSOBJS) -b binary initcode bootother fs.img + $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm + $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym + tags: $(OBJS) bootother.S _init etags *.S *.c @@ -187,6 +204,9 @@ QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) qemu: fs.img xv6.img $(QEMU) -serial mon:stdio $(QEMUOPTS) +qemu-memfs: xv6memfs.img + $(QEMU) xv6memfs.img -smp $(CPUS) + qemu-nox: fs.img xv6.img $(QEMU) -nographic $(QEMUOPTS) diff --git a/multiboot.S b/multiboot.S new file mode 100644 index 0000000..2579b6d --- /dev/null +++ b/multiboot.S @@ -0,0 +1,75 @@ +# Multiboot header, for multiboot boot loaders like GNU Grub. +# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html +# +# Using GRUB 2, you can boot xv6 from a file stored in a +# Linux file system by copying kernel or kernelmemfs to /boot +# and then adding this menu entry: +# +# menuentry "xv6" { +# insmod ext2 +# set root='(hd0,msdos1)' +# set kernel='/boot/kernel' +# echo "Loading ${kernel}..." +# multiboot ${kernel} ${kernel} +# boot +# } + +#include "asm.h" + +#define STACK 4096 + +#define SEG_KCODE 1 // kernel code +#define SEG_KDATA 2 // kernel data+stack + +# Multiboot header. Data to direct multiboot loader. +.p2align 2 +.text +.globl multiboot_header +multiboot_header: + #define magic 0x1badb002 + #define flags (1<<16 | 1<<0) + .long magic + .long flags + .long (-magic-flags) + .long multiboot_header # beginning of image + .long multiboot_header + .long edata + .long end + .long multiboot_entry + +# Multiboot entry point. Machine is mostly set up. +# Configure the GDT to match the environment that our usual +# boot loader - bootasm.S - sets up. +.globl multiboot_entry +multiboot_entry: + lgdt gdtdesc + ljmp $(SEG_KCODE<<3), $mbstart32 + +mbstart32: + # 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 + + # Set up the stack pointer and call into C. + movl $(stack + STACK), %esp + call main +spin: + jmp spin + +# Bootstrap GDT +.p2align 2 # force 4 byte alignment +gdt: + SEG_NULLASM # null seg + SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg + SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg + +gdtdesc: + .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 + .long gdt # address gdt + +.comm stack, STACK -- cgit v1.2.3 From 417c37115e0c7fc3b2a65c3c4d213e566cbc8807 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 11 Jan 2011 13:51:40 -0500 Subject: more trivial cleanup --- defs.h | 6 +++--- kalloc.c | 2 +- vm.c | 21 +++++++++------------ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/defs.h b/defs.h index 43f35d2..8ea46d6 100644 --- a/defs.h +++ b/defs.h @@ -161,11 +161,11 @@ int allocuvm(pde_t*, uint, uint); int deallocuvm(pde_t*, uint, uint); void freevm(pde_t*); void inituvm(pde_t*, char*, uint); -int loaduvm(pde_t*, char*, struct inode *, uint, uint); -pde_t* copyuvm(pde_t*,uint); +int loaduvm(pde_t*, char*, struct inode*, uint, uint); +pde_t* copyuvm(pde_t*, uint); void switchuvm(struct proc*); void switchkvm(void); -int copyout(pde_t *pgdir, uint va, void *buf, uint len); +int copyout(pde_t*, uint, void*, uint); // number of elements in fixed-size array #define NELEM(x) (sizeof(x)/sizeof((x)[0])) diff --git a/kalloc.c b/kalloc.c index e31d71d..bf1616a 100644 --- a/kalloc.c +++ b/kalloc.c @@ -27,7 +27,7 @@ kinit(void) initlock(&kmem.lock, "kmem"); p = (char*)PGROUNDUP((uint)end); - for(; p + PGSIZE - 1 < (char*)PHYSTOP; p += PGSIZE) + for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE) kfree(p); } diff --git a/vm.c b/vm.c index 940cc2f..bfc0845 100644 --- a/vm.c +++ b/vm.c @@ -250,16 +250,16 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) int allocuvm(pde_t *pgdir, uint oldsz, uint newsz) { - char *a, *last, *mem; + char *mem; + uint a; if(newsz > USERTOP) return 0; if(newsz < oldsz) return oldsz; - a = (char*)PGROUNDUP(oldsz); - last = PGROUNDDOWN(newsz - 1); - for(; a <= last; a += PGSIZE){ + a = PGROUNDUP(oldsz); + for(; a < newsz; a += PGSIZE){ mem = kalloc(); if(mem == 0){ cprintf("allocuvm out of memory\n"); @@ -267,7 +267,7 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) return 0; } memset(mem, 0, PGSIZE); - mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U); + mappages(pgdir, (char*)a, PGSIZE, PADDR(mem), PTE_W|PTE_U); } return newsz; } @@ -279,17 +279,15 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) int deallocuvm(pde_t *pgdir, uint oldsz, uint newsz) { - char *a, *last; pte_t *pte; - uint pa; + uint a, pa; if(newsz >= oldsz) return oldsz; - a = (char*)PGROUNDUP(newsz); - last = PGROUNDDOWN(oldsz - 1); - for(; a <= last; a += PGSIZE){ - pte = walkpgdir(pgdir, a, 0); + a = PGROUNDUP(newsz); + for(; a < oldsz; a += PGSIZE){ + pte = walkpgdir(pgdir, (char*)a, 0); if(pte && (*pte & PTE_P) != 0){ pa = PTE_ADDR(*pte); if(pa == 0) @@ -351,7 +349,6 @@ bad: // copy some data to user address va in page table pgdir. // most useful when pgdir is not the current page table. -// returns 1 if everthing OK, 0 on error. // uva2ka ensures this only works for PTE_U pages. int copyout(pde_t *pgdir, uint va, void *xbuf, uint len) -- cgit v1.2.3 From 4fa8614db068e92c2628456df7a7d5483e4afe5b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 11 Jan 2011 13:54:23 -0500 Subject: missing file memide.c --- memide.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 memide.c diff --git a/memide.c b/memide.c new file mode 100644 index 0000000..d2c5bb7 --- /dev/null +++ b/memide.c @@ -0,0 +1,58 @@ +// Fake IDE disk; stores blocks in memory. +// Useful for running kernel without scratch disk. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "mmu.h" +#include "proc.h" +#include "x86.h" +#include "traps.h" +#include "spinlock.h" +#include "buf.h" + +extern uchar _binary_fs_img_start[], _binary_fs_img_size[]; + +static int disksize; +static uchar *memdisk; + +void +ideinit(void) +{ + memdisk = _binary_fs_img_start; + disksize = (uint)_binary_fs_img_size/512; +} + +// Interrupt handler. +void +ideintr(void) +{ + // no-op +} + +// Sync buf with disk. +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. +void +iderw(struct buf *b) +{ + uchar *p; + + if(!(b->flags & B_BUSY)) + panic("iderw: buf not busy"); + if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) + panic("iderw: nothing to do"); + if(b->dev != 1) + panic("iderw: request not for disk 1"); + if(b->sector >= disksize) + panic("iderw: sector out of range"); + + p = memdisk + b->sector*512; + + if(b->flags & B_DIRTY){ + b->flags &= ~B_DIRTY; + memmove(p, b->data, 512); + } else + memmove(b->data, p, 512); + b->flags |= B_VALID; +} -- cgit v1.2.3 From 08864affaab14743b59b83f6985dad03545a6d71 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 10:23:19 -0500 Subject: xv6: link to xv6-rev4 --- web/index.html | 5 ++--- web/index.txt | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/web/index.html b/web/index.html index 5f60112..5e0897d 100644 --- a/web/index.html +++ b/web/index.html @@ -90,11 +90,11 @@ Jos without xv6; we believe other courses could use xv6 without Jos, though we are not aware of any that have.

Xv6 sources

-The latest xv6 is xv6-rev3.tar.gz. +The latest xv6 is xv6-rev4.tar.gz. We distribute the sources in electronic form but also as a printed booklet with line numbers that keep everyone together during lectures. The booklet is available as -xv6-rev3.pdf. +xv6-rev4.pdf. The xv6 source code is licensed under the traditional MIT license; see the LICENSE file in the source distribution.

@@ -353,6 +353,5 @@ Robert Morris (rtm@mit.edu)

You can reach all of us at 6.828-staff@pdos.csail.mit.edu.

-

diff --git a/web/index.txt b/web/index.txt index 6f3e1f6..4707632 100644 --- a/web/index.txt +++ b/web/index.txt @@ -58,11 +58,13 @@ xv6 without Jos, though we are not aware of any that have. * Xv6 sources -The latest xv6 is [xv6-rev2.tar.gz]. +The latest xv6 is [xv6-rev4.tar.gz]. We distribute the sources in electronic form but also as a printed booklet with line numbers that keep everyone together during lectures. The booklet is available as -[xv6-rev2.pdf]. +[xv6-rev4.pdf]. +The xv6 source code is licensed under the traditional MIT license; +see the LICENSE file in the source distribution. xv6 compiles using the GNU C compiler, targeted at the x86 using ELF binaries. @@ -335,5 +337,3 @@ Robert Morris (rtm@mit.edu) You can reach all of us at 6.828-staff@pdos.csail.mit.edu. -xv6 and lecture notes are copyright © 2006-present by Russ Cox, -Frans Kaashoek, and Robert Morris. -- cgit v1.2.3 From 97b21b5838759e71144d02ff554f594c56fe54ae Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 10:36:14 -0500 Subject: xv6: refresh from 2010 page --- web/Makefile | 3 - web/boot.pdf | Bin 0 -> 167234 bytes web/disk.pdf | Bin 0 -> 116662 bytes web/exec.pdf | Bin 0 -> 86398 bytes web/fscall.pdf | Bin 0 -> 105465 bytes web/fsdata.pdf | Bin 0 -> 131569 bytes web/index.html | 437 ++++++++++++++++++++++----------------------------------- web/index.txt | 339 -------------------------------------------- web/lock.pdf | Bin 0 -> 143133 bytes web/mem.pdf | Bin 0 -> 175343 bytes web/mkhtml | 70 --------- web/sched.pdf | Bin 0 -> 177687 bytes web/trap.pdf | Bin 0 -> 159268 bytes web/unix.pdf | Bin 0 -> 169150 bytes 14 files changed, 166 insertions(+), 683 deletions(-) delete mode 100644 web/Makefile create mode 100644 web/boot.pdf create mode 100644 web/disk.pdf create mode 100644 web/exec.pdf create mode 100644 web/fscall.pdf create mode 100644 web/fsdata.pdf delete mode 100644 web/index.txt create mode 100644 web/lock.pdf create mode 100644 web/mem.pdf delete mode 100755 web/mkhtml create mode 100644 web/sched.pdf create mode 100644 web/trap.pdf create mode 100644 web/unix.pdf diff --git a/web/Makefile b/web/Makefile deleted file mode 100644 index 107d683..0000000 --- a/web/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -index.html: index.txt mkhtml - ./mkhtml index.txt >_$@ && mv _$@ $@ - diff --git a/web/boot.pdf b/web/boot.pdf new file mode 100644 index 0000000..8390b29 Binary files /dev/null and b/web/boot.pdf differ diff --git a/web/disk.pdf b/web/disk.pdf new file mode 100644 index 0000000..3119b7a Binary files /dev/null and b/web/disk.pdf differ diff --git a/web/exec.pdf b/web/exec.pdf new file mode 100644 index 0000000..88499cd Binary files /dev/null and b/web/exec.pdf differ diff --git a/web/fscall.pdf b/web/fscall.pdf new file mode 100644 index 0000000..9a597f4 Binary files /dev/null and b/web/fscall.pdf differ diff --git a/web/fsdata.pdf b/web/fsdata.pdf new file mode 100644 index 0000000..70fc10d Binary files /dev/null and b/web/fsdata.pdf differ diff --git a/web/index.html b/web/index.html index 5e0897d..a8b0ca6 100644 --- a/web/index.html +++ b/web/index.html @@ -1,4 +1,3 @@ - Xv6, a simple Unix-like teaching operating system @@ -32,31 +31,36 @@ h2 { --> +

Xv6, a simple Unix-like teaching operating system

-

-Xv6 is a teaching operating system developed -in the summer of 2006 for MIT's operating systems course, -“6.828: Operating Systems Engineering.” -We used it for 6.828 in Fall 2006 and Fall 2007 -and are using it this semester (Fall 2008). -We hope that xv6 will be useful in other courses too. -This page collects resources to aid the use of xv6 -in other courses. + +

Introduction

+ +Xv6 is a teaching operating system developed in the summer of 2006 for +MIT's operating systems +course, 6.828: operating +systems Engineering. We hope that xv6 will be useful in other +courses too. This page collects resources to aid the use of xv6 in +other courses, including a commentary on the source code itself. + +

Status: The xv6 code is in pretty good shape, but +the commentary is rough.

History and Background

-For many years, MIT had no operating systems course. -In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit -created a new, experimental course (6.097) -to teach operating systems engineering. -In the course lectures, the class worked through Sixth Edition Unix (aka V6) -using John Lions's famous commentary. -In the lab assignments, students wrote most of an exokernel operating -system, eventually named Jos, for the Intel x86. -Exposing students to multiple systems–V6 and Jos–helped -develop a sense of the spectrum of operating system designs. -In the fall of 2003, the experimental 6.097 became the -official course 6.828; the course has been offered each fall since then. -

+ +

For many years, MIT had no operating systems course. In the fall +of 2002, Frans Kaashoek, Josh Cates, and Emil Sit created a new, +experimental course (6.097) to teach operating systems engineering. +In the course lectures, the class worked through Sixth +Edition Unix (aka V6) using John Lions's famous commentary. In +the lab assignments, students wrote most of an exokernel operating +system, eventually named Jos, for the Intel x86. Exposing students to +multiple systems–V6 and Jos–helped develop a sense of the +spectrum of operating system designs. In the fall of 2003, the +experimental 6.097 became the official course 6.828; the course has +been offered each fall since then. + +

V6 presented pedagogic challenges from the start. Students doubted the relevance of an obsolete 30-year-old operating system written in an obsolete programming language (pre-K&R C) @@ -76,13 +80,12 @@ uniprocessors such as enabling/disabling interrupts) and helps relevance. Finally, writing a new system allowed us to write cleaner versions of the rougher parts of V6, like the scheduler and file system. -

-6.828 substituted xv6 for V6 in the fall of 2006. -Based on that experience, we cleaned up rough patches -of xv6 for the course in the fall of 2007. -Since then, xv6 has stabilized, so we are making it -available in the hopes that others will find it useful too. -

+

6.828 substituted xv6 for V6 in the fall of 2006. Based on +that experience, we cleaned up rough patches of xv6. Since then, xv6 +has stabilized, so we are making it available in the hopes that others +will find it useful too. + +

6.828 uses both xv6 and Jos. Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua, and University Texas (Austin) have used @@ -90,14 +93,16 @@ Jos without xv6; we believe other courses could use xv6 without Jos, though we are not aware of any that have.

Xv6 sources

-The latest xv6 is xv6-rev4.tar.gz. + +The latest xv6 is xv6-rev4.tar.gz. We distribute the sources in electronic form but also as a printed booklet with line numbers that keep everyone together during lectures. The booklet is available as -xv6-rev4.pdf. +xv6-rev4.pdf. The xv6 source code is licensed under the traditional MIT license; see the LICENSE file in the source distribution. -

+ +

xv6 compiles using the GNU C compiler, targeted at the x86 using ELF binaries. On BSD and Linux systems, you can use the native compilers; @@ -106,239 +111,131 @@ you must use a cross-compiler. Xv6 does boot on real hardware, but typically we run it using the Bochs emulator. Both the GCC cross compiler and Bochs -can be found on the 6.828 tools page. - -

Lectures

-In 6.828, the lectures in the first half of the course -introduce the PC hardware, the Intel x86, and then xv6. -The lectures in the second half consider advanced topics -using research papers; for some, xv6 serves as a useful -base for making discussions concrete. -This section describe a typical 6.828 lecture schedule, -linking to lecture notes and homework. -A course using only xv6 (not Jos) will need to adapt -a few of the lectures, but we hope these are a useful -starting point. - -

Lecture 1. Operating systems -

-The first lecture introduces both the general topic of -operating systems and the specific approach of 6.828. -After defining “operating system,” the lecture -examines the implementation of a Unix shell -to look at the details the traditional Unix system call interface. -This is relevant to both xv6 and Jos: in the final -Jos labs, students implement a Unix-like interface -and culminating in a Unix shell. -

-lecture notes -OS abstractions slides - -

Lecture 2. PC hardware and x86 programming -

-This lecture introduces the PC architecture, the 16- and 32-bit x86, -the stack, and the GCC x86 calling conventions. -It also introduces the pieces of a typical C tool chain–compiler, -assembler, linker, loader–and the Bochs emulator. -

-Reading: PC Assembly Language -

-Homework: familiarize with Bochs -

-lecture notes -x86 intro slides -homework - -

Lecture 3. Operating system organization -

-This lecture continues Lecture 1's discussion of what -an operating system does. -An operating system provides a “virtual computer” -interface to user space programs. -At a high level, the main job of the operating system -is to implement that interface -using the physical computer it runs on. -

-The lecture discusses four approaches to that job: -monolithic operating systems, microkernels, -virtual machines, and exokernels. -Exokernels might not be worth mentioning -except that the Jos labs are built around one. -

-Reading: Engler et al., Exokernel: An Operating System Architecture -for Application-Level Resource Management -

-lecture notes - -

Lecture 4. Address spaces using segmentation -

-This is the first lecture that uses xv6. -It introduces the idea of address spaces and the -details of the x86 segmentation hardware. -It makes the discussion concrete by reading the xv6 -source code and watching xv6 execute using the Bochs simulator. -

-Reading: x86 MMU handout, -xv6: bootasm.S, bootother.S, bootmain.c, main.c, init.c, and setupsegs in proc.c. -

-Homework: Bochs stack introduction -

-lecture notes -x86 virtual memory slides -homework - -

Lecture 5. Address spaces using page tables -

-This lecture continues the discussion of address spaces, -examining the other x86 virtual memory mechanism: page tables. -Xv6 does not use page tables, so there is no xv6 here. -Instead, the lecture uses Jos as a concrete example. -An xv6-only course might skip or shorten this discussion. -

-Reading: x86 manual excerpts -

-Homework: stuff about gdt -XXX not appropriate; should be in Lecture 4 -

-lecture notes - -

Lecture 6. Interrupts and exceptions -

-How does a user program invoke the operating system kernel? -How does the kernel return to the user program? -What happens when a hardware device needs attention? -This lecture explains the answer to these questions: -interrupt and exception handling. -

-It explains the x86 trap setup mechanisms and then -examines their use in xv6's SETGATE (mmu.h), -tvinit (trap.c), idtinit (trap.c), vectors.pl, and vectors.S. -

-It then traces through a call to the system call open: -init.c, usys.S, vector48 and alltraps (vectors.S), trap (trap.c), -syscall (syscall.c), -sys_open (sysfile.c), fetcharg, fetchint, argint, argptr, argstr (syscall.c), -

-The interrupt controller, briefly: -pic_init and pic_enable (picirq.c). -The timer and keyboard, briefly: -timer_init (timer.c), console_init (console.c). -Enabling and disabling of interrupts. -

-Reading: x86 manual excerpts, -xv6: trapasm.S, trap.c, syscall.c, and usys.S. -Skim lapic.c, ioapic.c, picirq.c. -

-Homework: Explain the 35 words on the top of the -stack at first invocation of syscall. -

-lecture notes -homework - -

Lecture 7. Multiprocessors and locking -

-This lecture introduces the problems of -coordination and synchronization on a -multiprocessor -and then the solution of mutual exclusion locks. -Atomic instructions, test-and-set locks, -lock granularity, (the mistake of) recursive locks. -

-Although xv6 user programs cannot share memory, -the xv6 kernel itself is a program with multiple threads -executing concurrently and sharing memory. -Illustration: the xv6 scheduler's proc_table_lock (proc.c) -and the spin lock implementation (spinlock.c). -

-Reading: xv6: spinlock.c. Skim mp.c. -

-Homework: Interaction between locking and interrupts. -Try not disabling interrupts in the disk driver and watch xv6 break. -

-lecture notes -homework - -

Lecture 8. Threads, processes and context switching -

-The last lecture introduced some of the issues -in writing threaded programs, using xv6's processes -as an example. -This lecture introduces the issues in implementing -threads, continuing to use xv6 as the example. -

-The lecture defines a thread of computation as a register -set and a stack. A process is an address space plus one -or more threads of computation sharing that address space. -Thus the xv6 kernel can be viewed as a single process -with many threads (each user process) executing concurrently. -

-Illustrations: thread switching (swtch.S), scheduler (proc.c), sys_fork (sysproc.c) -

-Reading: proc.c, swtch.S, sys_fork (sysproc.c) -

-Homework: trace through stack switching. -

-lecture notes (need to be updated to use swtch) -homework - -

Lecture 9. Processes and coordination -

-This lecture introduces the idea of sequence coordination -and then examines the particular solution illustrated by -sleep and wakeup (proc.c). -It introduces and refines a simple -producer/consumer queue to illustrate the -need for sleep and wakeup -and then the sleep and wakeup -implementations themselves. -

-Reading: proc.c, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (sysproc.c). -

-Homework: Explain how sleep and wakeup would break -without proc_table_lock. Explain how devices would break -without second lock argument to sleep. -

-lecture notes -homework - -

Lecture 10. Files and disk I/O -

-This is the first of three file system lectures. -This lecture introduces the basic file system interface -and then considers the on-disk layout of individual files -and the free block bitmap. -

-Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and - code related to these calls in fs.c, bio.c, ide.c, and file.c. -

-Homework: Add print to bwrite to trace every disk write. -Explain the disk writes caused by some simple shell commands. -

-lecture notes -homework - -

Lecture 11. Naming -

-The last lecture discussed on-disk file system representation. -This lecture covers the implementation of -file system paths (namei in fs.c) -and also discusses the security problems of a shared /tmp -and symbolic links. -

-Understanding exec (exec.c) is left as an exercise. -

-Reading: namei in fs.c, sysfile.c, file.c. -

-Homework: Explain how to implement symbolic links in xv6. -

-lecture notes -homework - -

Lecture 12. High-performance file systems -

-This lecture is the first of the research paper-based lectures. -It discusses the “soft updates” paper, -using xv6 as a concrete example. +can be found on the 6.828 tools page. + +

Xv6 lecture material

+ +In 6.828, the lectures in the first half of the course introduce the +PC hardware, the Intel x86, and then xv6. The lectures in the second +half consider advanced topics using research papers; for some, xv6 +serves as a useful base for making discussions concrete. The lecture +notes are available from the 6.828 schedule page, and the chapters of +the commentary are below. + +

Xv6 commentary (rough)

+ +

The chapters are rough drafts. + +

Introduction yet to be written.
+

    +
  • read with the code side by side +
  • code references look like (xxxx) or (xxxx-yyyy) in small text. +
  • this pdf is the one with matching line numbers. +
  • each chapter starts with an introduction to the topic, +spends most of the text on code, +and then wraps up talking about how xv6 +compares to real-world operating systems. +
+ +Chapter 0: Operating system interfaces +
+The Unix system call interface. (rev 4) +
+ +Chapter 1: Bootstrap +
+From power on to kernel start. (rev 4) +
+ +Chapter 2: Processes +
+Memory and process allocation, segments, the first user process. (rev 4) +
+ +Chapter 3: Traps +
+Low-level trap mechanism, trap handler, system call arguments, sbrk, fork. +
+ +Chapter 4: Locks +
+Locks and interrupts. +
+ +Chapter 5: Scheduling and coordination +
+Scheduling, sleep and wakeup, pipes, wait and exit. +
+ +Chapter 6: Buffer cache +
+Buffer cache and IDE disk driver. +
+ +Chapter 7: File system data +
+Block in use bitmap, block allocation, inode structure, inode contents, +directories, path names. +
+ +Chapter 8: File system calls +
+FIle descriptors, open, close, dup, read, write. +
+ +Chapter 9: Exec +
+Exec +
+ +Appendix A: Low-level C and inline assembly +
+Intro to C and inline assembly for people who only know Java (say). +Examples drawn entirely from xv6 source. +
+ +Appendix B: Additional drivers. +
+Keyboard, screen, probably MP hardware. +
+ + +

Unix Version 6

+ +

6.828's xv6 is inspired by Unix V6 and by: + +

+ +The following are useful to read the original code: +
    +
  • +The PDP11/40 Processor Handbook, Digital Equipment Corporation, 1972. +
      +
    • A PDF (made from scanned images, +and not text-searchable) +
    • A web-based +version that is indexed by instruction name. +
    + +

Feedback

If you are interested in using xv6 or have used xv6 in a course, @@ -346,12 +243,10 @@ we would love to hear from you. If there's anything that we can do to make xv6 easier to adopt, we'd like to hear about it. We'd also be interested to hear what worked well and what didn't. -

+

Russ Cox (rsc@swtch.com)
Frans Kaashoek (kaashoek@mit.edu)
Robert Morris (rtm@mit.edu) -

+

You can reach all of us at 6.828-staff@pdos.csail.mit.edu. -

- - + diff --git a/web/index.txt b/web/index.txt deleted file mode 100644 index 4707632..0000000 --- a/web/index.txt +++ /dev/null @@ -1,339 +0,0 @@ -** Xv6, a simple Unix-like teaching operating system -Xv6 is a teaching operating system developed -in the summer of 2006 for MIT's operating systems course, -``6.828: Operating Systems Engineering.'' -We used it for 6.828 in Fall 2006 and Fall 2007 -and are using it this semester (Fall 2008). -We hope that xv6 will be useful in other courses too. -This page collects resources to aid the use of xv6 -in other courses. - -* History and Background - -For many years, MIT had no operating systems course. -In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit -created a new, experimental course (6.097) -to teach operating systems engineering. -In the course lectures, the class worked through Sixth Edition Unix (aka V6) -using John Lions's famous commentary. -In the lab assignments, students wrote most of an exokernel operating -system, eventually named Jos, for the Intel x86. -Exposing students to multiple systems--V6 and Jos--helped -develop a sense of the spectrum of operating system designs. -In the fall of 2003, the experimental 6.097 became the -official course 6.828; the course has been offered each fall since then. - -V6 presented pedagogic challenges from the start. -Students doubted the relevance of an obsolete 30-year-old operating system -written in an obsolete programming language (pre-K&R C) -running on obsolete hardware (the PDP-11). -Students also struggled to learn the low-level details of two different -architectures (the PDP-11 and the Intel x86) at the same time. -By the summer of 2006, we had decided to replace V6 -with a new operating system, xv6, modeled on V6 -but written in ANSI C and running on multiprocessor -Intel x86 machines. -Xv6's use of the x86 makes it more relevant to -students' experience than V6 was -and unifies the course around a single architecture. -Adding multiprocessor support requires handling concurrency head on with -locks and threads (instead of using special-case solutions for -uniprocessors such as -enabling/disabling interrupts) and helps relevance. -Finally, writing a new system allowed us to write cleaner versions -of the rougher parts of V6, like the scheduler and file system. - -6.828 substituted xv6 for V6 in the fall of 2006. -Based on that experience, we cleaned up rough patches -of xv6 for the course in the fall of 2007. -Since then, xv6 has stabilized, so we are making it -available in the hopes that others will find it useful too. - -6.828 uses both xv6 and Jos. -Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua, -and University Texas (Austin) have used -Jos without xv6; we believe other courses could use -xv6 without Jos, though we are not aware of any that have. - - -* Xv6 sources - -The latest xv6 is [xv6-rev4.tar.gz]. -We distribute the sources in electronic form but also as -a printed booklet with line numbers that keep everyone -together during lectures. The booklet is available as -[xv6-rev4.pdf]. -The xv6 source code is licensed under the traditional MIT license; -see the LICENSE file in the source distribution. - -xv6 compiles using the GNU C compiler, -targeted at the x86 using ELF binaries. -On BSD and Linux systems, you can use the native compilers; -On OS X, which doesn't use ELF binaries, -you must use a cross-compiler. -Xv6 does boot on real hardware, but typically -we run it using the Bochs emulator. -Both the GCC cross compiler and Bochs -can be found on the [../../2007/tools.html | 6.828 tools page]. - - -* Lectures - -In 6.828, the lectures in the first half of the course -introduce the PC hardware, the Intel x86, and then xv6. -The lectures in the second half consider advanced topics -using research papers; for some, xv6 serves as a useful -base for making discussions concrete. -This section describe a typical 6.828 lecture schedule, -linking to lecture notes and homework. -A course using only xv6 (not Jos) will need to adapt -a few of the lectures, but we hope these are a useful -starting point. - - -Lecture 1. Operating systems - -The first lecture introduces both the general topic of -operating systems and the specific approach of 6.828. -After defining ``operating system,'' the lecture -examines the implementation of a Unix shell -to look at the details the traditional Unix system call interface. -This is relevant to both xv6 and Jos: in the final -Jos labs, students implement a Unix-like interface -and culminating in a Unix shell. - -[l1.html | lecture notes] -[os-lab-1.pdf | OS abstractions slides] - - -Lecture 2. PC hardware and x86 programming - -This lecture introduces the PC architecture, the 16- and 32-bit x86, -the stack, and the GCC x86 calling conventions. -It also introduces the pieces of a typical C tool chain--compiler, -assembler, linker, loader--and the Bochs emulator. - -Reading: PC Assembly Language - -Homework: familiarize with Bochs - -[l2.html | lecture notes] -[os-lab-2.pdf | x86 intro slides] -[x86-intro.html | homework] - - -Lecture 3. Operating system organization - -This lecture continues Lecture 1's discussion of what -an operating system does. -An operating system provides a ``virtual computer'' -interface to user space programs. -At a high level, the main job of the operating system -is to implement that interface -using the physical computer it runs on. - -The lecture discusses four approaches to that job: -monolithic operating systems, microkernels, -virtual machines, and exokernels. -Exokernels might not be worth mentioning -except that the Jos labs are built around one. - -Reading: Engler et al., Exokernel: An Operating System Architecture -for Application-Level Resource Management - -[l3.html | lecture notes] - - -Lecture 4. Address spaces using segmentation - -This is the first lecture that uses xv6. -It introduces the idea of address spaces and the -details of the x86 segmentation hardware. -It makes the discussion concrete by reading the xv6 -source code and watching xv6 execute using the Bochs simulator. - -Reading: x86 MMU handout, -xv6: bootasm.S, bootother.S, bootmain.c, main.c, init.c, and setupsegs in proc.c. - -Homework: Bochs stack introduction - -[l4.html | lecture notes] -[os-lab-3.pdf | x86 virtual memory slides] -[xv6-intro.html | homework] - - -Lecture 5. Address spaces using page tables - -This lecture continues the discussion of address spaces, -examining the other x86 virtual memory mechanism: page tables. -Xv6 does not use page tables, so there is no xv6 here. -Instead, the lecture uses Jos as a concrete example. -An xv6-only course might skip or shorten this discussion. - -Reading: x86 manual excerpts - -Homework: stuff about gdt -XXX not appropriate; should be in Lecture 4 - -[l5.html | lecture notes] - - -Lecture 6. Interrupts and exceptions - -How does a user program invoke the operating system kernel? -How does the kernel return to the user program? -What happens when a hardware device needs attention? -This lecture explains the answer to these questions: -interrupt and exception handling. - -It explains the x86 trap setup mechanisms and then -examines their use in xv6's SETGATE (mmu.h), -tvinit (trap.c), idtinit (trap.c), vectors.pl, and vectors.S. - -It then traces through a call to the system call open: -init.c, usys.S, vector48 and alltraps (vectors.S), trap (trap.c), -syscall (syscall.c), -sys_open (sysfile.c), fetcharg, fetchint, argint, argptr, argstr (syscall.c), - -The interrupt controller, briefly: -pic_init and pic_enable (picirq.c). -The timer and keyboard, briefly: -timer_init (timer.c), console_init (console.c). -Enabling and disabling of interrupts. - -Reading: x86 manual excerpts, -xv6: trapasm.S, trap.c, syscall.c, and usys.S. -Skim lapic.c, ioapic.c, picirq.c. - -Homework: Explain the 35 words on the top of the -stack at first invocation of syscall. - -[l-interrupt.html | lecture notes] -[x86-intr.html | homework] - - -Lecture 7. Multiprocessors and locking - -This lecture introduces the problems of -coordination and synchronization on a -multiprocessor -and then the solution of mutual exclusion locks. -Atomic instructions, test-and-set locks, -lock granularity, (the mistake of) recursive locks. - -Although xv6 user programs cannot share memory, -the xv6 kernel itself is a program with multiple threads -executing concurrently and sharing memory. -Illustration: the xv6 scheduler's proc_table_lock (proc.c) -and the spin lock implementation (spinlock.c). - -Reading: xv6: spinlock.c. Skim mp.c. - -Homework: Interaction between locking and interrupts. -Try not disabling interrupts in the disk driver and watch xv6 break. - -[l-lock.html | lecture notes] -[xv6-lock.html | homework] - - -Lecture 8. Threads, processes and context switching - -The last lecture introduced some of the issues -in writing threaded programs, using xv6's processes -as an example. -This lecture introduces the issues in implementing -threads, continuing to use xv6 as the example. - -The lecture defines a thread of computation as a register -set and a stack. A process is an address space plus one -or more threads of computation sharing that address space. -Thus the xv6 kernel can be viewed as a single process -with many threads (each user process) executing concurrently. - -Illustrations: thread switching (swtch.S), scheduler (proc.c), sys_fork (sysproc.c) - -Reading: proc.c, swtch.S, sys_fork (sysproc.c) - -Homework: trace through stack switching. - -[l-threads.html | lecture notes (need to be updated to use swtch)] -[xv6-sched.html | homework] - - -Lecture 9. Processes and coordination - -This lecture introduces the idea of sequence coordination -and then examines the particular solution illustrated by -sleep and wakeup (proc.c). -It introduces and refines a simple -producer/consumer queue to illustrate the -need for sleep and wakeup -and then the sleep and wakeup -implementations themselves. - -Reading: proc.c, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (sysproc.c). - -Homework: Explain how sleep and wakeup would break -without proc_table_lock. Explain how devices would break -without second lock argument to sleep. - -[l-coordination.html | lecture notes] -[xv6-sleep.html | homework] - - -Lecture 10. Files and disk I/O - -This is the first of three file system lectures. -This lecture introduces the basic file system interface -and then considers the on-disk layout of individual files -and the free block bitmap. - -Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and - code related to these calls in fs.c, bio.c, ide.c, and file.c. - -Homework: Add print to bwrite to trace every disk write. -Explain the disk writes caused by some simple shell commands. - -[l-fs.html | lecture notes] -[xv6-disk.html | homework] - - -Lecture 11. Naming - -The last lecture discussed on-disk file system representation. -This lecture covers the implementation of -file system paths (namei in fs.c) -and also discusses the security problems of a shared /tmp -and symbolic links. - -Understanding exec (exec.c) is left as an exercise. - -Reading: namei in fs.c, sysfile.c, file.c. - -Homework: Explain how to implement symbolic links in xv6. - -[l-name.html | lecture notes] -[xv6-names.html | homework] - - -Lecture 12. High-performance file systems - -This lecture is the first of the research paper-based lectures. -It discusses the ``soft updates'' paper, -using xv6 as a concrete example. - - -* Feedback - -If you are interested in using xv6 or have used xv6 in a course, -we would love to hear from you. -If there's anything that we can do to make xv6 easier -to adopt, we'd like to hear about it. -We'd also be interested to hear what worked well and what didn't. - -Russ Cox (rsc@swtch.com)
-Frans Kaashoek (kaashoek@mit.edu)
-Robert Morris (rtm@mit.edu) - -You can reach all of us at 6.828-staff@pdos.csail.mit.edu. - diff --git a/web/lock.pdf b/web/lock.pdf new file mode 100644 index 0000000..e71c744 Binary files /dev/null and b/web/lock.pdf differ diff --git a/web/mem.pdf b/web/mem.pdf new file mode 100644 index 0000000..73d67d0 Binary files /dev/null and b/web/mem.pdf differ diff --git a/web/mkhtml b/web/mkhtml deleted file mode 100755 index 74987e6..0000000 --- a/web/mkhtml +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl - -my @lines = <>; -my $text = join('', @lines); -my $title; -if($text =~ /^\*\* (.*?)\n/m){ - $title = $1; - $text = $` . $'; -}else{ - $title = "Untitled"; -} - -$text =~ s/[ \t]+$//mg; -$text =~ s/^$/

/mg; -$text =~ s!\b([a-z0-9]+\.(c|s|pl|h))\b!$1!g; -$text =~ s!^(Lecture [0-9]+\. .*?)$!$1!mg; -$text =~ s!^\* (.*?)$!

$1

!mg; -$text =~ s!((
)+\n)+

!\n

!g; -$text =~ s!

\n?((
)+\n)+!\n!g; -$text =~ s!((
)+\n)+!\n

!g; -$text =~ s!\b\s*--\s*\b!\–!g; -$text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!$2!g; -$text =~ s!\[([^ \t]+)\]!$1!g; - -$text =~ s!``!\“!g; -$text =~ s!''!\”!g; - -print < - - -$title - - - -

$title

-

-EOF -print $text; -print < - -EOF diff --git a/web/sched.pdf b/web/sched.pdf new file mode 100644 index 0000000..0419d80 Binary files /dev/null and b/web/sched.pdf differ diff --git a/web/trap.pdf b/web/trap.pdf new file mode 100644 index 0000000..b2aea5b Binary files /dev/null and b/web/trap.pdf differ diff --git a/web/unix.pdf b/web/unix.pdf new file mode 100644 index 0000000..a629f9a Binary files /dev/null and b/web/unix.pdf differ -- cgit v1.2.3 From 9c4fe7ba105c0430c90179fd1e93c3d439a8cbd5 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 21:16:54 -0500 Subject: xv6: formatting, cleanup, rev5 --- data.S | 7 +++++++ xv6-rev5.pdf | Bin 0 -> 162291 bytes xv6-rev5.tar.gz | Bin 0 -> 79693 bytes 3 files changed, 7 insertions(+) create mode 100644 data.S create mode 100644 xv6-rev5.pdf create mode 100644 xv6-rev5.tar.gz diff --git a/data.S b/data.S new file mode 100644 index 0000000..47f05b3 --- /dev/null +++ b/data.S @@ -0,0 +1,7 @@ +# Define "data" symbol to mark beginning of data segment. +# Must be linked before any other data on ld command line. +.data +.globl data +data: + .word 1 + diff --git a/xv6-rev5.pdf b/xv6-rev5.pdf new file mode 100644 index 0000000..3ea9077 Binary files /dev/null and b/xv6-rev5.pdf differ diff --git a/xv6-rev5.tar.gz b/xv6-rev5.tar.gz new file mode 100644 index 0000000..cfdff29 Binary files /dev/null and b/xv6-rev5.tar.gz differ -- cgit v1.2.3 From cf4b1ad90bcaeeb0c8458098c87948f61d408f94 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 21:17:55 -0500 Subject: xv6: formatting, cleanup, rev5 (take 2) --- Makefile | 20 ++++++----- bootasm.S | 33 +++++++---------- bootother.S | 2 +- data.S | 23 ++++++++++-- exec.c | 62 ++++++++++---------------------- fs.h | 1 - ide.c | 2 +- main.c | 7 +++- mp.c | 2 -- proc.c | 76 +++++++++++++++++++-------------------- runoff.list | 4 +++ runoff.spec | 26 +++++++++----- runoff1 | 2 +- toc.ftr | 6 ++-- trap.c | 3 ++ usertests.c | 6 ++-- vm.c | 115 ++++++++++++++++++++++++++++-------------------------------- 17 files changed, 193 insertions(+), 197 deletions(-) diff --git a/Makefile b/Makefile index 980543a..7000fac 100644 --- a/Makefile +++ b/Makefile @@ -107,8 +107,8 @@ initcode: initcode.S $(OBJCOPY) -S -O binary initcode.out initcode $(OBJDUMP) -S initcode.o > initcode.asm -kernel: $(OBJS) multiboot.o bootother initcode - $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o $(OBJS) -b binary initcode bootother fs.img +kernel: $(OBJS) multiboot.o data.o bootother initcode + $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother $(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym @@ -119,8 +119,8 @@ kernel: $(OBJS) multiboot.o bootother initcode # great for testing the kernel on real hardware without # needing a scratch disk. MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o -kernelmemfs: $(MEMFSOBJS) multiboot.o bootother initcode fs.img - $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o $(MEMFSOBJS) -b binary initcode bootother fs.img +kernelmemfs: $(MEMFSOBJS) multiboot.o data.o bootother initcode fs.img + $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o data.o $(MEMFSOBJS) -b binary initcode bootother fs.img $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym @@ -251,14 +251,16 @@ dist-test: rm -rf dist-test mkdir dist-test cp dist/* dist-test - cd dist-test; ../m print - cd dist-test; ../m bochs || true - cd dist-test; ../m qemu + cd dist-test; $(MAKE) print + cd dist-test; $(MAKE) bochs || true + cd dist-test; $(MAKE) qemu -# update this rule (change rev1) when it is time to +# update this rule (change rev#) when it is time to # make a new revision. tar: rm -rf /tmp/xv6 mkdir -p /tmp/xv6 cp dist/* dist/.gdbinit.tmpl /tmp/xv6 - (cd /tmp; tar cf - xv6) | gzip >xv6-rev4.tar.gz + (cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz + +.PHONY: dist-test dist diff --git a/bootasm.S b/bootasm.S index f5d1678..3cc23e7 100644 --- a/bootasm.S +++ b/bootasm.S @@ -13,7 +13,7 @@ .code16 # Assemble for 16-bit mode .globl start start: - cli # BIOS enabled interrupts ; disable + cli # BIOS enabled interrupts; disable # Set up the important data segment registers (DS, ES, SS). xorw %ax,%ax # Segment number zero @@ -21,10 +21,8 @@ start: movw %ax,%es # -> Extra Segment movw %ax,%ss # -> Stack Segment - # Enable A20: - # For backwards compatibility with the earliest PCs, physical - # address line 20 is tied low, so that addresses higher than - # 1MB wrap around to zero by default. This code undoes this. + # Physical address line A20 is tied to zero so that the first PCs + # with 2 MB would run software that assumed 1 MB. Undo that. seta20.1: inb $0x64,%al # Wait for not busy testb $0x2,%al @@ -41,28 +39,21 @@ seta20.2: movb $0xdf,%al # 0xdf -> port 0x60 outb %al,$0x60 -//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 after subsequent - # loads of segment registers. + # Switch from real to protected mode. Use a bootstrap GDT that makes + # virtual addresses map dierctly to physical addresses so that the + # effective memory map doesn't change during the transition. 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 -# tell the assembler to generate 0x66 prefixes for 16-bit -# instructions like movw, and to generate 32-bit immediate -# addresses. -.code32 +//PAGEBREAK! + # Complete transition to 32-bit protected mode by using long jmp + # to reload %cs and %eip. The segment registers are set up with no + # translation, so that the mapping is still the identity mapping. + ljmp $(SEG_KCODE<<3), $start32 +.code32 # Tell assembler to generate 32-bit code now. 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 186873e..37b899b 100644 --- a/bootother.S +++ b/bootother.S @@ -34,12 +34,12 @@ start: movw %ax,%es movw %ax,%ss -//PAGEBREAK! lgdt gdtdesc movl %cr0, %eax orl $CR0_PE, %eax movl %eax, %cr0 +//PAGEBREAK! ljmp $(SEG_KCODE<<3), $start32 .code32 diff --git a/data.S b/data.S index 47f05b3..c0eb55b 100644 --- a/data.S +++ b/data.S @@ -1,5 +1,24 @@ -# Define "data" symbol to mark beginning of data segment. -# Must be linked before any other data on ld command line. +// The kernel layout is: +// +// text +// rodata +// data +// bss +// +// Conventionally, Unix linkers provide pseudo-symbols +// etext, edata, and end, at the end of the text, data, and bss. +// For the kernel mapping, we need the address at the beginning +// of the data section, but that's not one of the conventional +// symbols, because the convention started before there was a +// read-only rodata section between text and data. +// +// To get the address of the data section, we define a symbol +// named data and make sure this is the first object passed to +// the linker, so that it will be the first symbol in the data section. +// +// Alternative approaches would be to parse our own ELF header +// or to write a linker script, but this is simplest. + .data .globl data data: diff --git a/exec.c b/exec.c index 209bc79..05f80f8 100644 --- a/exec.c +++ b/exec.c @@ -10,8 +10,8 @@ int exec(char *path, char **argv) { char *s, *last; - int i, off, argc; - uint sz, sp, strings[MAXARG]; + int i, off; + uint argc, sz, sp, ustack[3+MAXARG+1]; struct elfhdr elf; struct inode *ip; struct proghdr ph; @@ -53,49 +53,25 @@ exec(char *path, char **argv) if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0) goto bad; - // initialize stack content: - - // "argumentN" -- nul-terminated string - // ... - // "argument0" - // 0 -- argv[argc] - // address of argumentN - // ... - // address of argument0 -- argv[0] - // address of address of argument0 -- argv argument to main() - // argc -- argc argument to main() - // ffffffff -- return PC for main() call - + // Push argument strings, prepare rest of stack in ustack. sp = sz; - - // count arguments - for(argc = 0; argv[argc]; argc++) - ; - if(argc >= MAXARG) - goto bad; - - // push strings and remember where they are - for(i = argc - 1; i >= 0; --i){ - sp -= strlen(argv[i]) + 1; - strings[i] = sp; - copyout(pgdir, sp, argv[i], strlen(argv[i]) + 1); + for(argc = 0; argv[argc]; argc++) { + if(argc >= MAXARG) + goto bad; + sp -= strlen(argv[argc]) + 1; + sp &= ~3; + if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) + goto bad; + ustack[3+argc] = sp; } + ustack[3+argc] = 0; -#define PUSH(x){ int xx = (int)(x); sp -= 4; copyout(pgdir, sp, &xx, 4); } + ustack[0] = 0xffffffff; // fake return PC + ustack[1] = argc; + ustack[2] = sp - (argc+1)*4; // argv pointer - PUSH(0); // argv[argc] is zero - - // push argv[] elements - for(i = argc - 1; i >= 0; --i) - PUSH(strings[i]); - - PUSH(sp); // argv - - PUSH(argc); - - PUSH(0xffffffff); // in case main tries to return - - if(sp < sz - PGSIZE) + sp -= (3+argc+1) * 4; + if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) goto bad; // Save program name for debugging. @@ -110,9 +86,7 @@ exec(char *path, char **argv) proc->sz = sz; proc->tf->eip = elf.entry; // main proc->tf->esp = sp; - - switchuvm(proc); - + switchuvm(proc); freevm(oldpgdir); return 0; diff --git a/fs.h b/fs.h index 6f92592..1e6137b 100644 --- a/fs.h +++ b/fs.h @@ -41,7 +41,6 @@ struct dinode { // Block containing bit for block b #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) -// PAGEBREAK: 10 // Directory is a file containing a sequence of dirent structures. #define DIRSIZ 14 diff --git a/ide.c b/ide.c index f4bd210..53293a7 100644 --- a/ide.c +++ b/ide.c @@ -96,7 +96,7 @@ ideintr(void) acquire(&idelock); if((b = idequeue) == 0){ release(&idelock); - cprintf("Spurious IDE interrupt.\n"); + // cprintf("spurious IDE interrupt\n"); return; } idequeue = b->qnext; diff --git a/main.c b/main.c index 8a5f7f1..e6d81f3 100644 --- a/main.c +++ b/main.c @@ -89,7 +89,8 @@ bootothers(void) char *stack; // Write bootstrap code to unused memory at 0x7000. - // The linker has placed the image of bootother.S in _binary_bootother_start. + // The linker has placed the image of bootother.S in + // _binary_bootother_start. code = (uchar*)0x7000; memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); @@ -111,3 +112,7 @@ bootothers(void) ; } } + +//PAGEBREAK! +// Blank page. + diff --git a/mp.c b/mp.c index 44ee020..5ab348e 100644 --- a/mp.c +++ b/mp.c @@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len) { uchar *e, *p; - cprintf("mpsearch1 0x%x %d\n", addr, len); e = addr+len; for(p = addr; p < e; p += sizeof(struct mp)) if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) @@ -113,7 +112,6 @@ mpinit(void) switch(*p){ case MPPROC: proc = (struct mpproc*)p; - cprintf("mpproc %d\n", proc->apicid); if(ncpu != proc->apicid){ cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid); ismp = 0; diff --git a/proc.c b/proc.c index e6ccd9d..eb334d0 100644 --- a/proc.c +++ b/proc.c @@ -25,44 +25,6 @@ pinit(void) initlock(&ptable.lock, "ptable"); } -//PAGEBREAK: 36 -// Print a process listing to console. For debugging. -// Runs when user types ^P on console. -// No lock to avoid wedging a stuck machine further. -void -procdump(void) -{ - static char *states[] = { - [UNUSED] "unused", - [EMBRYO] "embryo", - [SLEEPING] "sleep ", - [RUNNABLE] "runble", - [RUNNING] "run ", - [ZOMBIE] "zombie" - }; - int i; - struct proc *p; - char *state; - uint pc[10]; - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state == UNUSED) - continue; - if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) - state = states[p->state]; - else - state = "???"; - cprintf("%d %s %s", p->pid, state, p->name); - if(p->state == SLEEPING){ - getcallerpcs((uint*)p->context->ebp+2, pc); - for(i=0; i<10 && pc[i] != 0; i++) - cprintf(" %p", pc[i]); - } - cprintf("\n"); - } -} - - //PAGEBREAK: 32 // Look in the process table for an UNUSED proc. // If found, change state to EMBRYO and initialize @@ -447,3 +409,41 @@ kill(int pid) return -1; } +//PAGEBREAK: 36 +// Print a process listing to console. For debugging. +// Runs when user types ^P on console. +// No lock to avoid wedging a stuck machine further. +void +procdump(void) +{ + static char *states[] = { + [UNUSED] "unused", + [EMBRYO] "embryo", + [SLEEPING] "sleep ", + [RUNNABLE] "runble", + [RUNNING] "run ", + [ZOMBIE] "zombie" + }; + int i; + struct proc *p; + char *state; + uint pc[10]; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state == UNUSED) + continue; + if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) + state = states[p->state]; + else + state = "???"; + cprintf("%d %s %s", p->pid, state, p->name); + if(p->state == SLEEPING){ + getcallerpcs((uint*)p->context->ebp+2, pc); + for(i=0; i<10 && pc[i] != 0; i++) + cprintf(" %p", pc[i]); + } + cprintf("\n"); + } +} + + diff --git a/runoff.list b/runoff.list index 01147ea..f0edaf0 100644 --- a/runoff.list +++ b/runoff.list @@ -22,6 +22,7 @@ proc.h proc.c swtch.S kalloc.c +data.S vm.c # system calls traps.h @@ -48,6 +49,7 @@ exec.c # pipes pipe.c + # string operations string.c @@ -62,6 +64,7 @@ kbd.c console.c timer.c uart.c +multiboot.S # user-level initcode.S @@ -72,3 +75,4 @@ sh.c + diff --git a/runoff.spec b/runoff.spec index 8a2b5c9..4d00038 100644 --- a/runoff.spec +++ b/runoff.spec @@ -6,8 +6,8 @@ sheet1: left # pages. The file may start in either column. # # "even" and "odd" specify which column a file must start on. "even" -# means it must start in the left of the two columns. "odd" means it -# must start in the right of the two columns. +# means it must start in the left of the two columns (00). "odd" means it +# must start in the right of the two columns (50). # # You'd think these would be the other way around. @@ -33,23 +33,23 @@ left: spinlock.h # mild preference even: spinlock.h # mild preference # This gets struct proc and allocproc on the same spread -right: proc.h -odd: proc.h +left: proc.h +even: proc.h # goal is to have two action-packed 2-page spreads, # one with # userinit growproc fork exit wait # and another with # scheduler sched yield forkret sleep wakeup1 wakeup -left: proc.c # VERY important -odd: proc.c # VERY important +right: proc.c # VERY important +even: proc.c # VERY important # A few more action packed spreads # page table creation and process loading # walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm # process memory management # allocuvm deallocuvm freevm -right: vm.c +left: vm.c odd: vm.c # kalloc.c either @@ -69,17 +69,25 @@ odd: vm.c # file.h either # fs.h either # fsvar.h either -left: ide.c +# left: ide.c # mild preference even: ide.c # odd: bio.c + +# with fs.c starting on 2nd column of a left page, we get these 2-page spreads: +# ialloc iupdate iget idup ilock iunlock iput iunlockput +# bmap itrunc stati readi writei +# namecmp dirlookup dirlink skipelem namex namei +# fielinit filealloc filedup fileclose filestat fileread filewrite +# starting on 2nd column of a right page is not terrible either odd: fs.c # VERY important +left: fs.c # mild preference # file.c either # exec.c either # sysfile.c either # even: pipe.c # mild preference # string.c either -left: kbd.h +# left: kbd.h # mild preference even: kbd.h even: console.c odd: sh.c diff --git a/runoff1 b/runoff1 index ba42e8f..532f844 100755 --- a/runoff1 +++ b/runoff1 @@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){ last if $i>=@lines; # If the rest of the file fits, use the whole thing. - if(@lines <= $i+50){ + if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){ $breakbefore = @lines; }else{ # Find a good next page break; diff --git a/toc.ftr b/toc.ftr index 3ed8593..5e15911 100644 --- a/toc.ftr +++ b/toc.ftr @@ -6,8 +6,8 @@ on the same line as the name, the line number (or, in a few cases, numbers) where the name is defined. Successive lines in an entry list the line numbers where the name is used. For example, this entry: - swtch 2308 - 0317 2128 2166 2307 2308 + swtch 2358 + 0317 2128 2166 2357 2358 -indicates that swtch is defined on line 2308 and is mentioned on five lines +indicates that swtch is defined on line 2358 and is mentioned on five lines on sheets 03, 21, and 23. diff --git a/trap.c b/trap.c index b2b2ebb..6651f8e 100644 --- a/trap.c +++ b/trap.c @@ -59,6 +59,9 @@ trap(struct trapframe *tf) ideintr(); lapiceoi(); break; + case T_IRQ0 + IRQ_IDE+1: + // Bochs generates spurious IDE1 interrupts. + break; case T_IRQ0 + IRQ_KBD: kbdintr(); lapiceoi(); diff --git a/usertests.c b/usertests.c index 9a83591..296731a 100644 --- a/usertests.c +++ b/usertests.c @@ -1445,11 +1445,11 @@ bigargtest(void) ppid = getpid(); pid = fork(); if(pid == 0){ - char *args[32]; + char *args[32+1]; int i; - for(i = 0; i < 32-1; i++) + for(i = 0; i < 32; i++) args[i] = "bigargs test: failed\n "; - args[32-1] = 0; + args[32] = 0; printf(stdout, "bigarg test\n"); exec("echo", args); printf(stdout, "bigarg test ok\n"); diff --git a/vm.c b/vm.c index bfc0845..1fe64d2 100644 --- a/vm.c +++ b/vm.c @@ -6,8 +6,18 @@ #include "proc.h" #include "elf.h" +extern char data[]; // defined in data.S + static pde_t *kpgdir; // for use in scheduler() +// Allocate one page table for the machine for the kernel address +// space for scheduler processes. +void +kvmalloc(void) +{ + kpgdir = setupkvm(); +} + // Set up CPU's kernel segment descriptors. // Run once at boot time on each CPU. void @@ -72,7 +82,6 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) a = PGROUNDDOWN(la); last = PGROUNDDOWN(la + size - 1); - for(;;){ pte = walkpgdir(pgdir, a, 1); if(pte == 0) @@ -110,40 +119,32 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) // range from 0 till 640KB (USERTOP), which where the I/O hole starts // (both in physical memory and in the kernel's virtual address // space). - -// Allocate one page table for the machine for the kernel address -// space for scheduler processes. -void -kvmalloc(void) -{ - kpgdir = setupkvm(); -} +static struct kmap { + void *p; + void *e; + int perm; +} kmap[] = { + {(void*)USERTOP, (void*)0x100000, PTE_W}, // I/O space + {(void*)0x100000, data, 0 }, // kernel text, rodata + {data, (void*)PHYSTOP, PTE_W}, // kernel data, memory + {(void*)0xFE000000, 0, PTE_W}, // device mappings +}; // Set up kernel part of a page table. pde_t* setupkvm(void) { - extern char etext[]; - char *rwstart; pde_t *pgdir; - uint rwlen; - - rwstart = PGROUNDDOWN(etext); - rwlen = (uint)rwstart - 0x100000; + struct kmap *k; - // Allocate page directory if((pgdir = (pde_t*)kalloc()) == 0) return 0; memset(pgdir, 0, PGSIZE); - if(// Map IO space from 640K to 1Mbyte - mappages(pgdir, (void*)USERTOP, 0x60000, USERTOP, PTE_W) < 0 || - // Map kernel instructions - mappages(pgdir, (void*)0x100000, rwlen, 0x100000, 0) < 0 || - // Map kernel data and free memory pool - mappages(pgdir, rwstart, PHYSTOP-(uint)rwstart, (uint)rwstart, PTE_W) < 0 || - // Map devices such as ioapic, lapic, ... - mappages(pgdir, (void*)0xFE000000, 0x2000000, 0xFE000000, PTE_W) < 0) - return 0; + k = kmap; + for(k = kmap; k < &kmap[NELEM(kmap)]; k++) + if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 0) + return 0; + return pgdir; } @@ -162,48 +163,27 @@ vmenable(void) // Switch h/w page table register to the kernel-only page table, // for when no process is running. void -switchkvm() +switchkvm(void) { lcr3(PADDR(kpgdir)); // switch to the kernel page table } -// Switch h/w page table and TSS registers to point to process p. +// Switch TSS and h/w page table to correspond to process p. void switchuvm(struct proc *p) { pushcli(); - - // Setup TSS cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0); cpu->gdt[SEG_TSS].s = 0; cpu->ts.ss0 = SEG_KDATA << 3; cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; ltr(SEG_TSS << 3); - if(p->pgdir == 0) - panic("switchuvm: no pgdir\n"); - + panic("switchuvm: no pgdir"); lcr3(PADDR(p->pgdir)); // switch to new address space popcli(); } -// Return the physical address that a given user address -// maps to. The result is also a kernel logical address, -// since the kernel maps the physical memory allocated to user -// processes directly. -char* -uva2ka(pde_t *pgdir, char *uva) -{ - pte_t *pte; - - pte = walkpgdir(pgdir, uva, 0); - if((*pte & PTE_P) == 0) - return 0; - if((*pte & PTE_U) == 0) - return 0; - return (char*)PTE_ADDR(*pte); -} - // Load the initcode into address 0 of pgdir. // sz must be less than a page. void @@ -228,10 +208,10 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) pte_t *pte; if((uint)addr % PGSIZE != 0) - panic("loaduvm: addr must be page aligned\n"); + panic("loaduvm: addr must be page aligned"); for(i = 0; i < sz; i += PGSIZE){ if((pte = walkpgdir(pgdir, addr+i, 0)) == 0) - panic("loaduvm: address should exist\n"); + panic("loaduvm: address should exist"); pa = PTE_ADDR(*pte); if(sz - i < PGSIZE) n = sz - i; @@ -243,10 +223,8 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) return 0; } -// Allocate memory to the process to bring its size from oldsz to -// newsz. Allocates physical memory and page table entries. oldsz and -// newsz need not be page-aligned, nor does newsz have to be larger -// than oldsz. Returns the new process size or 0 on error. +// Allocate page tables and physical memory to grow process from oldsz to +// newsz, which need not be page aligned. Returns new size or 0 on error. int allocuvm(pde_t *pgdir, uint oldsz, uint newsz) { @@ -330,9 +308,9 @@ copyuvm(pde_t *pgdir, uint sz) return 0; for(i = 0; i < sz; i += PGSIZE){ if((pte = walkpgdir(pgdir, (void*)i, 0)) == 0) - panic("copyuvm: pte should exist\n"); + panic("copyuvm: pte should exist"); if(!(*pte & PTE_P)) - panic("copyuvm: page not present\n"); + panic("copyuvm: page not present"); pa = PTE_ADDR(*pte); if((mem = kalloc()) == 0) goto bad; @@ -347,16 +325,31 @@ bad: return 0; } -// copy some data to user address va in page table pgdir. -// most useful when pgdir is not the current page table. +//PAGEBREAK! +// Map user virtual address to kernel physical address. +char* +uva2ka(pde_t *pgdir, char *uva) +{ + pte_t *pte; + + pte = walkpgdir(pgdir, uva, 0); + if((*pte & PTE_P) == 0) + return 0; + if((*pte & PTE_U) == 0) + return 0; + return (char*)PTE_ADDR(*pte); +} + +// Copy len bytes from p to user address va in page table pgdir. +// Most useful when pgdir is not the current page table. // uva2ka ensures this only works for PTE_U pages. int -copyout(pde_t *pgdir, uint va, void *xbuf, uint len) +copyout(pde_t *pgdir, uint va, void *p, uint len) { char *buf, *pa0; uint n, va0; - buf = (char*)xbuf; + buf = (char*)p; while(len > 0){ va0 = (uint)PGROUNDDOWN(va); pa0 = uva2ka(pgdir, (char*)va0); -- cgit v1.2.3 From 688b5c03dd14677d3a74d0dbce199c3d6ad31d63 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 21:19:58 -0500 Subject: updated book pdfs --- web/boot.pdf | Bin 167234 -> 167102 bytes web/disk.pdf | Bin 116662 -> 116527 bytes web/exec.pdf | Bin 86398 -> 85922 bytes web/fscall.pdf | Bin 105465 -> 105323 bytes web/fsdata.pdf | Bin 131569 -> 131471 bytes web/lock.pdf | Bin 143133 -> 143040 bytes web/mem.pdf | Bin 175343 -> 175214 bytes web/sched.pdf | Bin 177687 -> 177589 bytes web/trap.pdf | Bin 159268 -> 159167 bytes web/unix.pdf | Bin 169150 -> 169008 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/web/boot.pdf b/web/boot.pdf index 8390b29..9cb84f5 100644 Binary files a/web/boot.pdf and b/web/boot.pdf differ diff --git a/web/disk.pdf b/web/disk.pdf index 3119b7a..4df20b7 100644 Binary files a/web/disk.pdf and b/web/disk.pdf differ diff --git a/web/exec.pdf b/web/exec.pdf index 88499cd..1652a14 100644 Binary files a/web/exec.pdf and b/web/exec.pdf differ diff --git a/web/fscall.pdf b/web/fscall.pdf index 9a597f4..4b44eac 100644 Binary files a/web/fscall.pdf and b/web/fscall.pdf differ diff --git a/web/fsdata.pdf b/web/fsdata.pdf index 70fc10d..2115b64 100644 Binary files a/web/fsdata.pdf and b/web/fsdata.pdf differ diff --git a/web/lock.pdf b/web/lock.pdf index e71c744..b6574a4 100644 Binary files a/web/lock.pdf and b/web/lock.pdf differ diff --git a/web/mem.pdf b/web/mem.pdf index 73d67d0..1fe14f0 100644 Binary files a/web/mem.pdf and b/web/mem.pdf differ diff --git a/web/sched.pdf b/web/sched.pdf index 0419d80..4255d2f 100644 Binary files a/web/sched.pdf and b/web/sched.pdf differ diff --git a/web/trap.pdf b/web/trap.pdf index b2aea5b..d7910c6 100644 Binary files a/web/trap.pdf and b/web/trap.pdf differ diff --git a/web/unix.pdf b/web/unix.pdf index a629f9a..4fd7972 100644 Binary files a/web/unix.pdf and b/web/unix.pdf differ -- cgit v1.2.3 From a6cd7509c4cb30a7d58aa2797dc979daaa795fd0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 19 Feb 2011 21:21:49 -0500 Subject: web: rev5 --- web/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/index.html b/web/index.html index a8b0ca6..d7bba91 100644 --- a/web/index.html +++ b/web/index.html @@ -94,11 +94,11 @@ xv6 without Jos, though we are not aware of any that have.

Xv6 sources

-The latest xv6 is xv6-rev4.tar.gz. +The latest xv6 is xv6-rev5.tar.gz. We distribute the sources in electronic form but also as a printed booklet with line numbers that keep everyone together during lectures. The booklet is available as -xv6-rev4.pdf. +xv6-rev5.pdf. The xv6 source code is licensed under the traditional MIT license; see the LICENSE file in the source distribution. @@ -130,7 +130,7 @@ the commentary are below.
  • read with the code side by side
  • code references look like (xxxx) or (xxxx-yyyy) in small text. -
  • this pdf is the one with matching line numbers. +
  • this pdf is the one with matching line numbers.
  • each chapter starts with an introduction to the topic, spends most of the text on code, and then wraps up talking about how xv6 -- cgit v1.2.3 From c5f538735191330cc112485548e6dc46317eea83 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 28 Feb 2011 09:32:20 -0500 Subject: fixes from Peter Froehlich --- console.c | 12 ++++++------ mkfs.c | 2 +- syscall.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/console.c b/console.c index 634e9e8..27649e6 100644 --- a/console.c +++ b/console.c @@ -23,24 +23,24 @@ static struct { } cons; static void -printint(int xx, int base, int sgn) +printint(int xx, int base, int sign) { static char digits[] = "0123456789abcdef"; char buf[16]; - int i, neg; + int i; uint x; - if(sgn && (neg = xx < 0)){ - neg = 1; + if(sign && (sign = xx < 0)) x = -xx; - } else + else x = xx; i = 0; do{ buf[i++] = digits[x % base]; }while((x /= base) != 0); - if(neg) + + if(sign) buf[i++] = '-'; while(--i >= 0) diff --git a/mkfs.c b/mkfs.c index 77e6791..20b9649 100644 --- a/mkfs.c +++ b/mkfs.c @@ -229,7 +229,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < 512); + assert(used < 512*8); bzero(buf, 512); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/syscall.c b/syscall.c index 16c5b47..f6550a1 100644 --- a/syscall.c +++ b/syscall.c @@ -57,7 +57,7 @@ argptr(int n, char **pp, int size) if(argint(n, &i) < 0) return -1; - if((uint)i >= proc->sz || (uint)i+size >= proc->sz) + if((uint)i >= proc->sz || (uint)i+size > proc->sz) return -1; *pp = (char*)i; return 0; -- cgit v1.2.3 From ae603ff5e79bb7b5eea4147425752f33d29fe558 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 28 Feb 2011 09:32:54 -0500 Subject: rebuild xv6-rev5.tar.gz does not affect line numbers --- xv6-rev5.tar.gz | Bin 79693 -> 79694 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/xv6-rev5.tar.gz b/xv6-rev5.tar.gz index cfdff29..c73fc21 100644 Binary files a/xv6-rev5.tar.gz and b/xv6-rev5.tar.gz differ -- cgit v1.2.3 From f70ef994dce8f6948edec8c534ae21742141dd50 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 28 Apr 2011 08:01:36 -0400 Subject: Russ's fix for panic remap --- .gitignore | 1 + data.S | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d304828..1c182c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ _* *.o *.d diff --git a/data.S b/data.S index c0eb55b..8b651b4 100644 --- a/data.S +++ b/data.S @@ -20,7 +20,7 @@ // or to write a linker script, but this is simplest. .data +.align 4096 .globl data data: .word 1 - -- cgit v1.2.3 From 96b7da5f03e8fd1722cb66ef3cb7fcc02d703860 Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Fri, 24 Jun 2011 05:26:57 -0400 Subject: do not run past the end of the disk! [ cherry-picked de6075ff0978108daea1d658425bcb37663a98a0 ] Conflicts: fs.c --- fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs.c b/fs.c index 15a4d35..7c6d904 100644 --- a/fs.c +++ b/fs.c @@ -61,7 +61,7 @@ balloc(uint dev) readsb(dev, &sb); for(b = 0; b < sb.size; b += BPB){ bp = bread(dev, BBLOCK(b, sb.ninodes)); - for(bi = 0; bi < BPB; bi++){ + for(bi = 0; bi < BPB && bi < (sb.size - b); bi++){ m = 1 << (bi % 8); if((bp->data[bi/8] & m) == 0){ // Is block free? bp->data[bi/8] |= m; // Mark block in use on disk. -- cgit v1.2.3 From 97657d703f7a92a088b400980c17249f34640a5e Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 24 Jun 2011 14:20:51 -0400 Subject: Add -fno-omit-frame-pointer to compile flags. At some point between gcc 4.4 and 4.6, omitting the frame pointer became default if any -O is specified. getcallerpcs relies on the frame pointer, so bring it back. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7000fac..3487aa4 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror +CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ASFLAGS = -m32 -gdwarf-2 # FreeBSD ld wants ``elf_i386_fbsd'' -- cgit v1.2.3 From 13a96baefc0ff5d8262c4bc8c797bee4b157443c Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Wed, 27 Jul 2011 20:35:46 -0400 Subject: Dirt simple logging Passes usertests and stressfs Seems to recover correctly in a number of simple cases --- Makefile | 1 + defs.h | 8 +++ fs.c | 12 ++--- fs.h | 1 + initcode.S | 3 ++ log.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 2 +- mkfs.c | 13 +++-- param.h | 2 + syscall.c | 49 +++++++++++------- syscall.h | 36 +++++++------- 11 files changed, 244 insertions(+), 47 deletions(-) create mode 100644 log.c diff --git a/Makefile b/Makefile index 3487aa4..f67c88c 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ OBJS = \ uart.o\ vectors.o\ vm.o\ + log.o\ # Cross-compiling (e.g., on Mac OS X) #TOOLPREFIX = i386-jos-elf- diff --git a/defs.h b/defs.h index 8ea46d6..bbe4ae4 100644 --- a/defs.h +++ b/defs.h @@ -6,6 +6,7 @@ struct pipe; struct proc; struct spinlock; struct stat; +struct superblock; // bio.c void binit(void); @@ -32,6 +33,7 @@ int filestat(struct file*, struct stat*); int filewrite(struct file*, char*, int n); // fs.c +void readsb(int dev, struct superblock *sb); int dirlink(struct inode*, char*, uint); struct inode* dirlookup(struct inode*, char*, uint*); struct inode* ialloc(uint, short); @@ -75,6 +77,12 @@ void lapicinit(int); void lapicstartap(uchar, uint); void microdelay(int); +// log.c +void initlog(void); +void log_write(struct buf*); +void begin_trans(); +void commit_trans(); + // mp.c extern int ismp; int mpbcpu(void); diff --git a/fs.c b/fs.c index 7c6d904..a414b65 100644 --- a/fs.c +++ b/fs.c @@ -25,7 +25,7 @@ static void itrunc(struct inode*); // Read the super block. -static void +void readsb(int dev, struct superblock *sb) { struct buf *bp; @@ -65,7 +65,7 @@ balloc(uint dev) m = 1 << (bi % 8); if((bp->data[bi/8] & m) == 0){ // Is block free? bp->data[bi/8] |= m; // Mark block in use on disk. - bwrite(bp); + log_write(bp); brelse(bp); return b + bi; } @@ -92,7 +92,7 @@ bfree(int dev, uint b) if((bp->data[bi/8] & m) == 0) panic("freeing free block"); bp->data[bi/8] &= ~m; // Mark block free on disk. - bwrite(bp); + log_write(bp); brelse(bp); } @@ -159,7 +159,7 @@ ialloc(uint dev, short type) if(dip->type == 0){ // a free inode memset(dip, 0, sizeof(*dip)); dip->type = type; - bwrite(bp); // mark it allocated on the disk + log_write(bp); // mark it allocated on the disk brelse(bp); return iget(dev, inum); } @@ -183,7 +183,7 @@ iupdate(struct inode *ip) dip->nlink = ip->nlink; dip->size = ip->size; memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); - bwrite(bp); + log_write(bp); brelse(bp); } @@ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn) a = (uint*)bp->data; if((addr = a[bn]) == 0){ a[bn] = addr = balloc(ip->dev); - bwrite(bp); + log_write(bp); } brelse(bp); return addr; diff --git a/fs.h b/fs.h index 1e6137b..c9e34bf 100644 --- a/fs.h +++ b/fs.h @@ -13,6 +13,7 @@ struct superblock { uint size; // Size of file system image (blocks) uint nblocks; // Number of data blocks uint ninodes; // Number of inodes. + uint nlog; // Number of log blocks }; #define NDIRECT 12 diff --git a/initcode.S b/initcode.S index 41e84f4..d86660a 100644 --- a/initcode.S +++ b/initcode.S @@ -3,9 +3,12 @@ #include "syscall.h" #include "traps.h" + # exec(init, argv) .globl start start: + movl $SYS_init, %eax + int $T_SYSCALL pushl $argv pushl $init pushl $0 // where caller pc would be diff --git a/log.c b/log.c new file mode 100644 index 0000000..72a0367 --- /dev/null +++ b/log.c @@ -0,0 +1,164 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "mmu.h" +#include "proc.h" +#include "x86.h" +#include "spinlock.h" +#include "fs.h" +#include "buf.h" + +// Dirt simple "logging" supporting only one transaction. All file system calls +// that potentially write a block should be wrapped in begin_trans and commit_trans, +// so that there is never more than one transaction. This serializes all file system +// operations that potentially write, but simplifies recovery (only the last +// one transaction to recover) and concurrency (don't have to worry about reading a modified +// block from a transaction that hasn't committed yet). + +// The header of the log. If head == 0, there are no log entries. All entries till head +// are committed. sector[] records the home sector for each block in the log +// (i.e., physical logging). +struct logheader { + int head; + int sector[LOGSIZE]; +}; + +struct { + struct spinlock lock; + int start; + int size; + int intrans; + int dev; + struct logheader lh; +} log; + +static void recover_from_log(void); + +void +initlog(void) +{ + if (sizeof(struct logheader) >= BSIZE) + panic("initlog: too big logheader"); + + struct superblock sb; + initlock(&log.lock, "log"); + readsb(ROOTDEV, &sb); + log.start = sb.size - sb.nlog; + log.size = sb.nlog; + log.dev = ROOTDEV; + recover_from_log(); +} + +// Copy committed blocks from log to their home location +static void +install_trans(void) +{ + int tail; + + if (log.lh.head > 0) + cprintf("install_trans %d\n", log.lh.head); + for (tail = 0; tail < log.lh.head; tail++) { + cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]); + struct buf *lbuf = bread(log.dev, log.start+tail+1); // read i'th block from log + struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst block + memmove(dbuf->data, lbuf->data, BSIZE); + bwrite(dbuf); + brelse(lbuf); + brelse(dbuf); + } +} + +// Read the log header from disk into the in-memory log header +static void +read_head(void) +{ + struct buf *buf = bread(log.dev, log.start); + struct logheader *lh = (struct logheader *) (buf->data); + int i; + log.lh.head = lh->head; + for (i = 0; i < log.lh.head; i++) { + log.lh.sector[i] = lh->sector[i]; + } + brelse(buf); + if (log.lh.head > 0) + cprintf("read_head: %d\n", log.lh.head); +} + +// Write the in-memory log header to disk, committing log entries till head +static void +write_head(void) +{ + if (log.lh.head > 0) + cprintf("write_head: %d\n", log.lh.head); + + struct buf *buf = bread(log.dev, log.start); + struct logheader *hb = (struct logheader *) (buf->data); + int i; + hb->head = log.lh.head; + for (i = 0; i < log.lh.head; i++) { + hb->sector[i] = log.lh.sector[i]; + } + bwrite(buf); + brelse(buf); +} + +static void +recover_from_log(void) +{ + read_head(); + install_trans(); // Install all transactions till head + log.lh.head = 0; + write_head(); // Reclaim log +} + +void +begin_trans(void) +{ + acquire(&log.lock); + while (log.intrans) { + sleep(&log, &log.lock); + } + log.intrans = 1; + release(&log.lock); +} + +void +commit_trans(void) +{ + write_head(); // This causes all blocks till log.head to be commited + install_trans(); // Install all the transactions till head + log.lh.head = 0; + write_head(); // Reclaim log + + acquire(&log.lock); + log.intrans = 0; + wakeup(&log); + release(&log.lock); +} + +// Write buffer into the log at log.head and record the block number log.lh.entry, but +// don't write the log header (which would commit the write). +void +log_write(struct buf *b) +{ + int i; + + if (log.lh.head >= LOGSIZE) + panic("too big a transaction"); + if (!log.intrans) + panic("write outside of trans"); + + cprintf("log_write: %d %d\n", b->sector, log.lh.head); + + for (i = 0; i < log.lh.head; i++) { + if (log.lh.sector[i] == b->sector) // log absorbtion? + break; + } + log.lh.sector[i] = b->sector; + struct buf *lbuf = bread(b->dev, log.start+i+1); + memmove(lbuf->data, b->data, BSIZE); + bwrite(lbuf); + brelse(lbuf); + if (i == log.lh.head) + log.lh.head++; +} diff --git a/main.c b/main.c index e6d81f3..a27c4ff 100644 --- a/main.c +++ b/main.c @@ -20,7 +20,7 @@ main(void) lapicinit(mpbcpu()); seginit(); // set up segments kinit(); // initialize memory allocator - jmpkstack(); // call mainc() on a properly-allocated stack + jmpkstack(); // call mainc() on a properly-allocated stack } void diff --git a/mkfs.c b/mkfs.c index 20b9649..f015edd 100644 --- a/mkfs.c +++ b/mkfs.c @@ -9,8 +9,10 @@ #include "types.h" #include "fs.h" #include "stat.h" +#include "param.h" -int nblocks = 995; +int nblocks = 985; +int nlog = LOGSIZE; int ninodes = 200; int size = 1024; @@ -79,17 +81,18 @@ main(int argc, char *argv[]) sb.size = xint(size); sb.nblocks = xint(nblocks); // so whole disk is size sectors sb.ninodes = xint(ninodes); + sb.nlog = xint(nlog); bitblocks = size/(512*8) + 1; usedblocks = ninodes / IPB + 3 + bitblocks; freeblock = usedblocks; - printf("used %d (bit %d ninode %zu) free %u total %d\n", usedblocks, - bitblocks, ninodes/IPB + 1, freeblock, nblocks+usedblocks); + printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks, + bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog); - assert(nblocks + usedblocks == size); + assert(nblocks + usedblocks + nlog == size); - for(i = 0; i < nblocks + usedblocks; i++) + for(i = 0; i < nblocks + usedblocks + nlog; i++) wsect(i, zeroes); memset(buf, 0, sizeof(buf)); diff --git a/param.h b/param.h index 70f88e8..ab1b9fe 100644 --- a/param.h +++ b/param.h @@ -10,3 +10,5 @@ #define USERTOP 0xA0000 // end of user address space #define PHYSTOP 0x1000000 // use phys mem up to here as free pool #define MAXARG 32 // max exec arguments +#define LOGSIZE 10 // size of log + diff --git a/syscall.c b/syscall.c index f6550a1..ce50a59 100644 --- a/syscall.c +++ b/syscall.c @@ -98,39 +98,52 @@ extern int sys_wait(void); extern int sys_write(void); extern int sys_uptime(void); +int +sys_init(void) +{ + initlog(); + return 0; +} + static int (*syscalls[])(void) = { -[SYS_chdir] sys_chdir, -[SYS_close] sys_close, -[SYS_dup] sys_dup, -[SYS_exec] sys_exec, -[SYS_exit] sys_exit, +[SYS_init] sys_init, [SYS_fork] sys_fork, -[SYS_fstat] sys_fstat, -[SYS_getpid] sys_getpid, -[SYS_kill] sys_kill, -[SYS_link] sys_link, -[SYS_mkdir] sys_mkdir, -[SYS_mknod] sys_mknod, -[SYS_open] sys_open, +[SYS_exit] sys_exit, +[SYS_wait] sys_wait, [SYS_pipe] sys_pipe, [SYS_read] sys_read, +[SYS_kill] sys_kill, +[SYS_exec] sys_exec, +[SYS_fstat] sys_fstat, +[SYS_chdir] sys_chdir, +[SYS_dup] sys_dup, +[SYS_getpid] sys_getpid, [SYS_sbrk] sys_sbrk, [SYS_sleep] sys_sleep, -[SYS_unlink] sys_unlink, -[SYS_wait] sys_wait, -[SYS_write] sys_write, [SYS_uptime] sys_uptime, +// File system calls that are run in a transaction: +[SYS_open] sys_open, +[SYS_write] sys_write, +[SYS_mknod] sys_mknod, +[SYS_unlink] sys_unlink, +[SYS_link] sys_link, +[SYS_mkdir] sys_mkdir, +[SYS_close] sys_close, }; void syscall(void) { int num; - + num = proc->tf->eax; - if(num >= 0 && num < NELEM(syscalls) && syscalls[num]) + if(num >= 0 && num < SYS_open && syscalls[num]) { + proc->tf->eax = syscalls[num](); + } else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) { + begin_trans(); proc->tf->eax = syscalls[num](); - else { + commit_trans(); + } else { cprintf("%d %s: unknown sys call %d\n", proc->pid, proc->name, num); proc->tf->eax = -1; diff --git a/syscall.h b/syscall.h index 3a0fbca..e9e43a2 100644 --- a/syscall.h +++ b/syscall.h @@ -1,22 +1,24 @@ // System call numbers +#define SYS_init 0 #define SYS_fork 1 #define SYS_exit 2 #define SYS_wait 3 #define SYS_pipe 4 -#define SYS_write 5 -#define SYS_read 6 -#define SYS_close 7 -#define SYS_kill 8 -#define SYS_exec 9 -#define SYS_open 10 -#define SYS_mknod 11 -#define SYS_unlink 12 -#define SYS_fstat 13 -#define SYS_link 14 -#define SYS_mkdir 15 -#define SYS_chdir 16 -#define SYS_dup 17 -#define SYS_getpid 18 -#define SYS_sbrk 19 -#define SYS_sleep 20 -#define SYS_uptime 21 +#define SYS_read 5 +#define SYS_kill 6 +#define SYS_exec 7 +#define SYS_fstat 8 +#define SYS_chdir 9 +#define SYS_dup 10 +#define SYS_getpid 11 +#define SYS_sbrk 12 +#define SYS_sleep 13 +#define SYS_uptime 14 + +#define SYS_open 15 +#define SYS_write 16 +#define SYS_mknod 17 +#define SYS_unlink 18 +#define SYS_link 19 +#define SYS_mkdir 20 +#define SYS_close 21 -- cgit v1.2.3