Merge commit 'origin/master' into page
This commit is contained in:
		
						commit
						dccb915282
					
				
					 61 changed files with 1606 additions and 1616 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| *~ | ||||
| _* | ||||
| *.o | ||||
| *.d | ||||
|  |  | |||
							
								
								
									
										93
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										93
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -26,19 +26,53 @@ OBJS = \ | |||
| 	uart.o\
 | ||||
| 	vectors.o\
 | ||||
| 	vm.o\
 | ||||
| 	log.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 | ||||
| 
 | ||||
| # 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 | ||||
| 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''
 | ||||
|  | @ -49,6 +83,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 | ||||
|  | @ -69,11 +108,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 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 | ||||
| 
 | ||||
| # 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 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 | ||||
| 
 | ||||
| tags: $(OBJS) bootother.S _init | ||||
| 	etags *.S *.c | ||||
| 
 | ||||
|  | @ -94,7 +145,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\
 | ||||
|  | @ -126,7 +177,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 | ||||
|  | @ -143,27 +194,33 @@ 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 | ||||
| ifndef CPUS | ||||
| CPUS := 2 | ||||
| endif | ||||
| QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) | ||||
| 
 | ||||
| qemu: fs.img xv6.img | ||||
| 	qemu -serial mon:stdio $(QEMUOPTS) | ||||
| 	$(QEMU) -serial mon:stdio $(QEMUOPTS) | ||||
| 
 | ||||
| qemu-memfs: xv6memfs.img | ||||
| 	$(QEMU) xv6memfs.img -smp $(CPUS) | ||||
| 
 | ||||
| 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
 | ||||
|  | @ -195,14 +252,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-rev3.tar.gz | ||||
| 	(cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz | ||||
| 
 | ||||
| .PHONY: dist-test dist | ||||
|  |  | |||
							
								
								
									
										13
									
								
								README
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								README
									
										
									
									
									
								
							|  | @ -19,6 +19,11 @@ 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. | ||||
| 
 | ||||
| The code in the files that constitute xv6 is | ||||
| Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. | ||||
|  | @ -39,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/. | ||||
|  |  | |||
							
								
								
									
										26
									
								
								bootasm.S
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								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 | ||||
|  | @ -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,23 +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 during the switch. | ||||
|   # 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. | ||||
| //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                       # Assemble for 32-bit mode | ||||
| .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 | ||||
|  |  | |||
|  | @ -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(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										77
									
								
								bootother.S
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								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. | ||||
| //PAGEBREAK! | ||||
|   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
 | ||||
|  |  | |||
							
								
								
									
										28
									
								
								console.c
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								console.c
									
										
									
									
									
								
							|  | @ -23,23 +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 = 0, neg = 0; | ||||
|   int i; | ||||
|   uint x; | ||||
| 
 | ||||
|   if(sgn && 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) | ||||
|  | @ -136,8 +137,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 +164,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; | ||||
|  | @ -202,8 +199,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); | ||||
|  | @ -211,9 +207,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){ | ||||
|  |  | |||
							
								
								
									
										26
									
								
								data.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								data.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| // 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 | ||||
| .align 4096
 | ||||
| .globl data
 | ||||
| data: | ||||
|   .word 1
 | ||||
							
								
								
									
										29
									
								
								defs.h
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								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); | ||||
|  | @ -62,7 +64,7 @@ void            ioapicinit(void); | |||
| // kalloc.c
 | ||||
| char*           kalloc(void); | ||||
| void            kfree(char*); | ||||
| void            kinit(char*,uint); | ||||
| void            kinit(void); | ||||
| 
 | ||||
| // kbd.c
 | ||||
| void            kbdintr(void); | ||||
|  | @ -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); | ||||
|  | @ -101,6 +109,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); | ||||
|  | @ -116,8 +125,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); | ||||
|  | @ -151,20 +160,20 @@ void            uartintr(void); | |||
| void            uartputc(int); | ||||
| 
 | ||||
| // vm.c
 | ||||
| void            pminit(void); | ||||
| void            ksegment(void); | ||||
| void            seginit(void); | ||||
| 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*, char*, uint); | ||||
| int             loaduvm(pde_t*, char*, struct inode *ip, uint, uint); | ||||
| 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*, uint, void*, uint); | ||||
| 
 | ||||
| // number of elements in fixed-size array
 | ||||
| #define NELEM(x) (sizeof(x)/sizeof((x)[0])) | ||||
|  |  | |||
							
								
								
									
										73
									
								
								exec.c
									
										
									
									
									
								
							
							
						
						
									
										73
									
								
								exec.c
									
										
									
									
									
								
							|  | @ -9,20 +9,18 @@ | |||
| int | ||||
| exec(char *path, char **argv) | ||||
| { | ||||
|   char *mem, *s, *last; | ||||
|   int i, argc, arglen, len, off; | ||||
|   uint sz, sp, spoffset, argp; | ||||
|   char *s, *last; | ||||
|   int i, off; | ||||
|   uint argc, sz, sp, ustack[3+MAXARG+1]; | ||||
|   struct elfhdr elf; | ||||
|   struct inode *ip; | ||||
|   struct proghdr ph; | ||||
|   pde_t *pgdir, *oldpgdir; | ||||
| 
 | ||||
|   pgdir = 0; | ||||
|   sz = 0; | ||||
| 
 | ||||
|   if((ip = namei(path)) == 0) | ||||
|     return -1; | ||||
|   ilock(ip); | ||||
|   pgdir = 0; | ||||
| 
 | ||||
|   // Check ELF header
 | ||||
|   if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) | ||||
|  | @ -30,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<elf.phnum; i++, off+=sizeof(ph)){ | ||||
|     if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) | ||||
|       goto bad; | ||||
|  | @ -41,49 +40,39 @@ 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)) == 0) | ||||
|       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) < 0) | ||||
|       goto bad; | ||||
|   } | ||||
|   iunlockput(ip); | ||||
|   ip = 0; | ||||
| 
 | ||||
|   // Allocate and initialize stack at sz
 | ||||
|   // Allocate a one-page stack at the next page boundary
 | ||||
|   sz = PGROUNDUP(sz); | ||||
|   sz += PGSIZE; // leave an invalid page
 | ||||
|   if (!allocuvm(pgdir, (char *)sz, PGSIZE)) | ||||
|   if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0) | ||||
|     goto bad; | ||||
|   mem = uva2ka(pgdir, (char *)sz); | ||||
|   spoffset = sz; | ||||
|   sz += PGSIZE; | ||||
| 
 | ||||
|   arglen = 0; | ||||
|   for(argc=0; argv[argc]; argc++) | ||||
|     arglen += strlen(argv[argc]) + 1; | ||||
|   arglen = (arglen+3) & ~3; | ||||
| 
 | ||||
|   // Push argument strings, prepare rest of stack in ustack.
 | ||||
|   sp = sz; | ||||
|   argp = sz - arglen - 4*(argc+1); | ||||
| 
 | ||||
|   // Copy argv strings and pointers to stack.
 | ||||
|   *(uint*)(mem+argp-spoffset + 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]
 | ||||
|   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; | ||||
| 
 | ||||
|   // Stack frame for main(argc, argv), below arguments.
 | ||||
|   sp = argp; | ||||
|   sp -= 4; | ||||
|   *(uint*)(mem+sp-spoffset) = argp; | ||||
|   sp -= 4; | ||||
|   *(uint*)(mem+sp-spoffset) = argc; | ||||
|   sp -= 4; | ||||
|   *(uint*)(mem+sp-spoffset) = 0xffffffff;   // fake return pc
 | ||||
|   ustack[0] = 0xffffffff;  // fake return PC
 | ||||
|   ustack[1] = argc; | ||||
|   ustack[2] = sp - (argc+1)*4;  // argv pointer
 | ||||
| 
 | ||||
|   sp -= (3+argc+1) * 4; | ||||
|   if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) | ||||
|     goto bad; | ||||
| 
 | ||||
|   // Save program name for debugging.
 | ||||
|   for(last=s=path; *s; s++) | ||||
|  | @ -97,15 +86,15 @@ exec(char *path, char **argv) | |||
|   proc->sz = sz; | ||||
|   proc->tf->eip = elf.entry;  // main
 | ||||
|   proc->tf->esp = sp; | ||||
| 
 | ||||
|   switchuvm(proc); | ||||
| 
 | ||||
|   freevm(oldpgdir); | ||||
| 
 | ||||
|   return 0; | ||||
| 
 | ||||
|  bad: | ||||
|   if (pgdir) freevm(pgdir); | ||||
|   if(pgdir) | ||||
|     freevm(pgdir); | ||||
|   if(ip) | ||||
|     iunlockput(ip); | ||||
|   return -1; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										14
									
								
								fs.c
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								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; | ||||
|  | @ -61,11 +61,11 @@ 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.
 | ||||
|         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; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								fs.h
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								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 | ||||
|  | @ -41,7 +42,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 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								ide.c
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								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; | ||||
|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										18
									
								
								kalloc.c
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								kalloc.c
									
										
									
									
									
								
							|  | @ -17,17 +17,21 @@ struct { | |||
|   struct run *freelist; | ||||
| } kmem; | ||||
| 
 | ||||
| extern char end[]; // first address after kernel loaded from ELF file
 | ||||
| 
 | ||||
| // Initialize free list of physical pages.
 | ||||
| void | ||||
| kinit(char *p, uint len) | ||||
| kinit(void) | ||||
| { | ||||
|   char *p; | ||||
| 
 | ||||
|   initlock(&kmem.lock, "kmem"); | ||||
|   char *p1 = (char*)PGROUNDUP((uint)p); | ||||
|   char *p2 = PGROUNDDOWN(p + len); | ||||
|   for( ; p1 < p2; p1 += 4096) | ||||
|     kfree(p1); | ||||
|   p = (char*)PGROUNDUP((uint)end); | ||||
|   for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE) | ||||
|     kfree(p); | ||||
| } | ||||
| 
 | ||||
| //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
 | ||||
|  | @ -37,7 +41,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.
 | ||||
|  | @ -54,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; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										164
									
								
								log.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								log.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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++; | ||||
| } | ||||
							
								
								
									
										55
									
								
								main.c
									
										
									
									
									
								
							
							
						
						
									
										55
									
								
								main.c
									
										
									
									
									
								
							|  | @ -7,40 +7,45 @@ | |||
| 
 | ||||
| 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.
 | ||||
| // 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
 | ||||
|   pminit();        // discover how much memory there is
 | ||||
|   jkstack();       // call mainc() on a properly-allocated stack 
 | ||||
|   seginit();       // set up segments
 | ||||
|   kinit();         // initialize memory allocator
 | ||||
|   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.
 | ||||
| // 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 +69,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) | ||||
| { | ||||
|  | @ -82,8 +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.
 | ||||
|   // 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); | ||||
| 
 | ||||
|  | @ -91,10 +98,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()
 | ||||
|  | @ -103,3 +113,6 @@ bootothers(void) | |||
|   } | ||||
| } | ||||
| 
 | ||||
| //PAGEBREAK!
 | ||||
| // Blank page.
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										58
									
								
								memide.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								memide.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										23
									
								
								mkfs.c
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								mkfs.c
									
										
									
									
									
								
							|  | @ -4,11 +4,15 @@ | |||
| #include <string.h> | ||||
| #include <fcntl.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #define stat xv6_stat  // avoid clash with host struct stat
 | ||||
| #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; | ||||
| 
 | ||||
|  | @ -77,20 +81,23 @@ 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 %lu) 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); | ||||
| 
 | ||||
|   wsect(1, &sb); | ||||
|   memset(buf, 0, sizeof(buf)); | ||||
|   memmove(buf, &sb, sizeof(sb)); | ||||
|   wsect(1, buf); | ||||
| 
 | ||||
|   rootino = ialloc(T_DIR); | ||||
|   assert(rootino == ROOTINO); | ||||
|  | @ -225,12 +232,12 @@ 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)); | ||||
|   } | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										39
									
								
								mmu.h
									
										
									
									
									
								
							
							
						
						
									
										39
									
								
								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,       \ | ||||
|  | @ -62,8 +75,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 +89,6 @@ struct segdesc { | |||
| #define STS_IG32    0xE     // 32-bit Interrupt Gate
 | ||||
| #define STS_TG32    0xF     // 32-bit Trap Gate
 | ||||
| 
 | ||||
| 
 | ||||
| // A linear address 'la' has a three-part structure as follows:
 | ||||
| //
 | ||||
| // +--------10------+-------10-------+---------12----------+
 | ||||
|  | @ -88,10 +98,10 @@ 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))) | ||||
|  | @ -99,7 +109,7 @@ struct segdesc { | |||
| // 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
 | ||||
|  | @ -130,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
 | ||||
|  |  | |||
							
								
								
									
										15
									
								
								mp.c
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								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) | ||||
|  | @ -114,8 +113,8 @@ mpinit(void) | |||
|     case MPPROC: | ||||
|       proc = (struct mpproc*)p; | ||||
|       if(ncpu != proc->apicid){ | ||||
|         cprintf("mpinit: ncpu=%d apicpid=%d", 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 +134,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.
 | ||||
|  |  | |||
							
								
								
									
										75
									
								
								multiboot.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								multiboot.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										4
									
								
								param.h
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								param.h
									
										
									
									
									
								
							|  | @ -7,4 +7,8 @@ | |||
| #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
 | ||||
| #define LOGSIZE      10  // size of log
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										29
									
								
								picirq.c
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								picirq.c
									
										
									
									
									
								
							|  | @ -82,32 +82,3 @@ picinit(void) | |||
|   if(irqmask != 0xFFFF) | ||||
|     picsetmask(irqmask); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Blank page.
 | ||||
|  |  | |||
							
								
								
									
										255
									
								
								proc.c
									
										
									
									
									
								
							
							
						
						
									
										255
									
								
								proc.c
									
										
									
									
									
								
							|  | @ -17,53 +17,18 @@ int nextpid = 1; | |||
| extern void forkret(void); | ||||
| extern void trapret(void); | ||||
| 
 | ||||
| static void wakeup1(void *chan); | ||||
| 
 | ||||
| void | ||||
| 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 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) | ||||
|  | @ -95,7 +60,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; | ||||
| 
 | ||||
|  | @ -103,6 +68,7 @@ found: | |||
|   p->context = (struct context*)sp; | ||||
|   memset(p->context, 0, sizeof *p->context); | ||||
|   p->context->eip = (uint)forkret; | ||||
| 
 | ||||
|   return p; | ||||
| } | ||||
| 
 | ||||
|  | @ -116,12 +82,10 @@ userinit(void) | |||
|    | ||||
|   p = allocproc(); | ||||
|   initproc = p; | ||||
|   if (!(p->pgdir = setupkvm())) | ||||
|   if((p->pgdir = setupkvm()) == 0) | ||||
|     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; | ||||
|  | @ -142,14 +106,17 @@ userinit(void) | |||
| int | ||||
| growproc(int n) | ||||
| { | ||||
|   uint sz; | ||||
|    | ||||
|   sz = proc->sz; | ||||
|   if(n > 0){ | ||||
|     if (!allocuvm(proc->pgdir, (char *)proc->sz, n)) | ||||
|     if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0) | ||||
|       return -1; | ||||
|   } else if(n < 0){ | ||||
|     if (!deallocuvm(proc->pgdir, (char *)(proc->sz + n), 0 - n)) | ||||
|     if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0) | ||||
|       return -1; | ||||
|   } | ||||
|   proc->sz += n; | ||||
|   proc->sz = sz; | ||||
|   switchuvm(proc); | ||||
|   return 0; | ||||
| } | ||||
|  | @ -168,7 +135,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; | ||||
|  | @ -192,6 +159,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.
 | ||||
|  | @ -356,89 +409,41 @@ 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.
 | ||||
| //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 | ||||
| exit(void) | ||||
| procdump(void) | ||||
| { | ||||
|   static char *states[] = { | ||||
|   [UNUSED]    "unused", | ||||
|   [EMBRYO]    "embryo", | ||||
|   [SLEEPING]  "sleep ", | ||||
|   [RUNNABLE]  "runble", | ||||
|   [RUNNING]   "run   ", | ||||
|   [ZOMBIE]    "zombie" | ||||
|   }; | ||||
|   int i; | ||||
|   struct proc *p; | ||||
|   int fd; | ||||
|   char *state; | ||||
|   uint pc[10]; | ||||
|    | ||||
|   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) | ||||
|     if(p->state == UNUSED) | ||||
|       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; | ||||
|     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"); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|     // 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
 | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										64
									
								
								proc.h
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								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;   // 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?
 | ||||
|   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;           // The currently-running process.
 | ||||
| }; | ||||
| 
 | ||||
| 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.  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");       // &cpus[cpunum()]
 | ||||
| extern struct proc *proc asm("%gs:4");     // cpus[cpunum()].proc
 | ||||
| 
 | ||||
| //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.
 | ||||
|  | @ -31,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
 | ||||
|  | @ -48,35 +78,5 @@ struct proc { | |||
| // Process memory is laid out contiguously, low addresses first:
 | ||||
| //   text
 | ||||
| //   original data and bss
 | ||||
| //   invalid page
 | ||||
| //   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.
 | ||||
|  |  | |||
							
								
								
									
										24
									
								
								runoff
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								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})){ | ||||
|  | @ -89,18 +96,13 @@ 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 | ||||
| 			if(!$leftwarn++) { | ||||
| 				print STDERR "assuming that sheet 1 is a left page.  double-check!\n"; | ||||
| 			} | ||||
| 			if($what eq "left" && !($toc{$file} =~ /^\d[13579]0/)){ | ||||
| 				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[02468]0/); | ||||
| 			$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; | ||||
| 		} | ||||
|  | @ -189,7 +191,9 @@ 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/^/    /' | ||||
| #	else | ||||
| #		echo $i defined but not used >&2 | ||||
| 	fi | ||||
| done | ||||
| ) >refs | ||||
|  |  | |||
|  | @ -22,8 +22,8 @@ proc.h | |||
| proc.c | ||||
| swtch.S | ||||
| kalloc.c | ||||
| data.S | ||||
| vm.c | ||||
| 
 | ||||
| # system calls | ||||
| traps.h | ||||
| vectors.pl | ||||
|  | @ -46,11 +46,10 @@ file.c | |||
| sysfile.c | ||||
| exec.c | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # pipes | ||||
| pipe.c | ||||
| 
 | ||||
| 
 | ||||
| # string operations | ||||
| string.c | ||||
| 
 | ||||
|  | @ -65,6 +64,7 @@ kbd.c | |||
| console.c | ||||
| timer.c | ||||
| uart.c | ||||
| multiboot.S | ||||
| 
 | ||||
| # user-level | ||||
| initcode.S | ||||
|  | @ -73,3 +73,6 @@ init.c | |||
| sh.c | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										50
									
								
								runoff.spec
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								runoff.spec
									
										
									
									
									
								
							|  | @ -1,3 +1,16 @@ | |||
| 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 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 (00).  "odd" means it | ||||
| # must start in the right of the two columns (50). | ||||
| # | ||||
| # You'd think these would be the other way around. | ||||
| 
 | ||||
| # types.h either | ||||
| # param.h either | ||||
| # defs.h either | ||||
|  | @ -9,25 +22,36 @@ | |||
| 
 | ||||
| even: bootasm.S  # mild preference | ||||
| even: bootother.S  # mild preference | ||||
| # bootmain.c either | ||||
| even: bootmain.c  # mild preference | ||||
| even: main.c | ||||
| # mp.c don't care at all | ||||
| # even: initcode.S | ||||
| # odd: init.c | ||||
| 
 | ||||
| # spinlock.h either | ||||
| # spinlock.c either | ||||
| 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 | ||||
| left: proc.h | ||||
| even: proc.h | ||||
| 
 | ||||
| # goal is to have two action-packed 2-page spreads, | ||||
| # one with | ||||
| #     ksegment usegment allocproc userinit growproc fork | ||||
| #     userinit growproc fork exit wait | ||||
| # and another with | ||||
| #     scheduler sched yield forkret sleep wakeup1 wakeup | ||||
| 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 | ||||
| left: vm.c | ||||
| odd: vm.c | ||||
| 
 | ||||
| # setjmp.S either | ||||
| # vm.c either | ||||
| # kalloc.c either | ||||
| 
 | ||||
| # syscall.h either | ||||
|  | @ -45,15 +69,25 @@ right: proc.c   # VERY important | |||
| # 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 | ||||
|  |  | |||
							
								
								
									
										2
									
								
								runoff1
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								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; | ||||
|  |  | |||
|  | @ -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"); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										13
									
								
								stressfs.c
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								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); | ||||
|  |  | |||
							
								
								
									
										57
									
								
								syscall.c
									
										
									
									
									
								
							
							
						
						
									
										57
									
								
								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.
 | ||||
|  | @ -46,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,10 +57,9 @@ 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 = proc->mem + i;   // XXXXX
 | ||||
|   *pp = (char *) i;   // XXXXX
 | ||||
|   *pp = (char*)i; | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -102,28 +98,37 @@ 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 | ||||
|  | @ -132,9 +137,13 @@ 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 { | ||||
|   } else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) { | ||||
|     begin_trans(); | ||||
|     proc->tf->eax = syscalls[num](); | ||||
|     commit_trans(); | ||||
|   } else { | ||||
|     cprintf("%d %s: unknown sys call %d\n", | ||||
|             proc->pid, proc->name, num); | ||||
|     proc->tf->eax = -1; | ||||
|  |  | |||
							
								
								
									
										36
									
								
								syscall.h
									
										
									
									
									
								
							
							
						
						
									
										36
									
								
								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 | ||||
|  |  | |||
|  | @ -344,7 +344,7 @@ sys_chdir(void) | |||
| int | ||||
| sys_exec(void) | ||||
| { | ||||
|   char *path, *argv[20]; | ||||
|   char *path, *argv[MAXARG]; | ||||
|   int i; | ||||
|   uint uargv, uarg; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										9
									
								
								toc.ftr
									
										
									
									
									
								
							
							
						
						
									
										9
									
								
								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 2358 | ||||
|         0317 2128 2166 2357 2358 | ||||
| 
 | ||||
| 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 2358 and is mentioned on five lines | ||||
| on sheets 03, 21, and 23. | ||||
|  |  | |||
							
								
								
									
										6
									
								
								trap.c
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								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(); | ||||
|  | @ -83,7 +86,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; | ||||
|  |  | |||
							
								
								
									
										4
									
								
								user.h
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								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*); | ||||
|  |  | |||
							
								
								
									
										150
									
								
								usertests.c
									
										
									
									
									
								
							
							
						
						
									
										150
									
								
								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]; | ||||
|  | @ -324,6 +326,7 @@ mem(void) | |||
|   void *m1, *m2; | ||||
|   int pid, ppid; | ||||
| 
 | ||||
|   printf(1, "mem test\n"); | ||||
|   ppid = getpid(); | ||||
|   if((pid = fork()) == 0){ | ||||
|     m1 = 0; | ||||
|  | @ -1234,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(); | ||||
|  | @ -1256,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"); | ||||
|  | @ -1268,13 +1273,13 @@ 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?
 | ||||
|  | @ -1318,8 +1323,8 @@ sbrktest(void) | |||
| 
 | ||||
|   // can we read the kernel's memory?
 | ||||
|   for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){ | ||||
|     int ppid = getpid(); | ||||
|     int pid = fork(); | ||||
|     ppid = getpid(); | ||||
|     pid = fork(); | ||||
|     if(pid < 0){ | ||||
|       printf(stdout, "fork failed\n"); | ||||
|       exit(); | ||||
|  | @ -1332,6 +1337,38 @@ sbrktest(void) | |||
|     wait(); | ||||
|   } | ||||
| 
 | ||||
|   // if we run the system out of memory, does it clean up the last
 | ||||
|   // failed allocation?
 | ||||
|   sbrk(-(sbrk(0) - oldbrk)); | ||||
|   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
 | ||||
|       for(;;) sleep(1000); | ||||
|     } | ||||
|     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)); | ||||
| 
 | ||||
|  | @ -1339,26 +1376,89 @@ 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"); | ||||
|   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, pid; | ||||
|   uint p; | ||||
| 
 | ||||
|   printf(stdout, "validate test\n"); | ||||
|   hi = 1100*1024; | ||||
| 
 | ||||
|   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(); | ||||
|     } | ||||
|     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(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   printf(stdout, "validate ok\n"); | ||||
| } | ||||
| 
 | ||||
| // does unintialized data start out zero?
 | ||||
| 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"); | ||||
| } | ||||
| 
 | ||||
| // 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){ | ||||
|     // should cause a trap:
 | ||||
|     p[-4096] = 'z'; | ||||
|     kill(ppid); | ||||
|     printf(stdout, "stack test failed: page before stack was writeable\n"); | ||||
|     char *args[32+1]; | ||||
|     int i; | ||||
|     for(i = 0; i < 32; i++) | ||||
|       args[i] = "bigargs test: failed\n                                                                                                                     "; | ||||
|     args[32] = 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(); | ||||
|   printf(stdout, "stack test OK\n"); | ||||
| } | ||||
| 
 | ||||
| int | ||||
|  | @ -1372,8 +1472,10 @@ main(int argc, char *argv[]) | |||
|   } | ||||
|   close(open("usertests.ran", O_CREATE)); | ||||
| 
 | ||||
|   stacktest(); | ||||
|   bigargtest(); | ||||
|   bsstest(); | ||||
|   sbrktest(); | ||||
|   validatetest(); | ||||
| 
 | ||||
|   opentest(); | ||||
|   writetest(); | ||||
|  |  | |||
							
								
								
									
										605
									
								
								vm.c
									
										
									
									
									
								
							
							
						
						
									
										605
									
								
								vm.c
									
										
									
									
									
								
							|  | @ -6,96 +6,22 @@ | |||
| #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..kernend      : mapped direct (for the kernel's text and data)
 | ||||
| //   kernend..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).
 | ||||
| extern char data[];  // defined in data.S
 | ||||
| 
 | ||||
| #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
 | ||||
| // 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) | ||||
| // Allocate one page table for the machine for the kernel address
 | ||||
| // space for scheduler processes.
 | ||||
| void | ||||
| kvmalloc(void) | ||||
| { | ||||
|   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; | ||||
| 
 | ||||
|     // 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; | ||||
|   } | ||||
|   return &pgtab[PTX(va)]; | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) | ||||
| { | ||||
|   char *first = PGROUNDDOWN(la); | ||||
|   char *last = PGROUNDDOWN(la + size - 1); | ||||
|   char *a = first; | ||||
|   while(1){ | ||||
|     pte_t *pte = walkpgdir(pgdir, a, 1); | ||||
|     if(pte == 0) | ||||
|       return 0; | ||||
|     if(*pte & PTE_P) | ||||
|       panic("remap"); | ||||
|     *pte = pa | perm | PTE_P; | ||||
|     if(a == last) | ||||
|       break; | ||||
|     a += PGSIZE; | ||||
|     pa += PGSIZE; | ||||
|   } | ||||
|   return 1; | ||||
|   kpgdir = setupkvm(); | ||||
| } | ||||
| 
 | ||||
| // Set up CPU's kernel segment descriptors.
 | ||||
| // Run once at boot time on each CPU.
 | ||||
| void | ||||
| ksegment(void) | ||||
| seginit(void) | ||||
| { | ||||
|   struct cpu *c; | ||||
| 
 | ||||
|  | @ -109,7 +35,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)); | ||||
|  | @ -120,252 +46,108 @@ ksegment(void) | |||
|   proc = 0; | ||||
| } | ||||
| 
 | ||||
| // Switch h/w page table and TSS registers to point to process p.
 | ||||
| void | ||||
| switchuvm(struct proc *p) | ||||
| // 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) | ||||
| { | ||||
|   pushcli(); | ||||
|   pde_t *pde; | ||||
|   pte_t *pgtab; | ||||
| 
 | ||||
|   // 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"); | ||||
| 
 | ||||
|   lcr3(PADDR(p->pgdir));  // switch to new address space
 | ||||
|   popcli(); | ||||
|   pde = &pgdir[PDX(va)]; | ||||
|   if(*pde & PTE_P){ | ||||
|     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(pgtab) | PTE_P | PTE_W | PTE_U; | ||||
|   } | ||||
|   return &pgtab[PTX(va)]; | ||||
| } | ||||
| 
 | ||||
| // Switch h/w page table register to the kernel-only page table, for when
 | ||||
| // no process is running.
 | ||||
| void | ||||
| switchkvm() | ||||
| // 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 | ||||
| mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm) | ||||
| { | ||||
|   lcr3(PADDR(kpgdir));   // Switch to the kernel page table
 | ||||
|   char *a, *last; | ||||
|   pte_t *pte; | ||||
|    | ||||
|   a = PGROUNDDOWN(la); | ||||
|   last = PGROUNDDOWN(la + size - 1); | ||||
|   for(;;){ | ||||
|     pte = walkpgdir(pgdir, a, 1); | ||||
|     if(pte == 0) | ||||
|       return -1; | ||||
|     if(*pte & PTE_P) | ||||
|       panic("remap"); | ||||
|     *pte = pa | perm | PTE_P; | ||||
|     if(a == last) | ||||
|       break; | ||||
|     a += PGSIZE; | ||||
|     pa += PGSIZE; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| // 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).
 | ||||
| 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) | ||||
| { | ||||
|   pde_t *pgdir; | ||||
|   struct kmap *k; | ||||
| 
 | ||||
|   // Allocate page directory
 | ||||
|   if (!(pgdir = (pde_t *) kalloc())) | ||||
|   if((pgdir = (pde_t*)kalloc()) == 0) | ||||
|     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 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)) | ||||
|     return 0; | ||||
|   // Map devices such as ioapic, lapic, ...
 | ||||
|   if (!mappages(pgdir, (void *)0xFE000000, 0x2000000, 0xFE000000, PTE_W)) | ||||
|   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; | ||||
| } | ||||
| 
 | ||||
| // 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 = walkpgdir(pgdir, uva, 0); | ||||
|   if (pte == 0) return 0; | ||||
|   uint pa = PTE_ADDR(*pte); | ||||
|   return (char *)pa; | ||||
| } | ||||
| 
 | ||||
| // 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.
 | ||||
| int | ||||
| allocuvm(pde_t *pgdir, char *addr, uint sz) | ||||
| { | ||||
|   if (addr + sz > (char*)USERTOP) | ||||
|     return 0; | ||||
|   char *first = PGROUNDDOWN(addr); | ||||
|   char *last = PGROUNDDOWN(addr + sz - 1); | ||||
|   char *a; | ||||
|   for(a = first; a <= last; a += PGSIZE){ | ||||
|     pte_t *pte = walkpgdir(pgdir, a, 0); | ||||
|     if(pte == 0 || (*pte & PTE_P) == 0){ | ||||
|       char *mem = kalloc(); | ||||
|       if(mem == 0){ | ||||
|         // XXX clean up?
 | ||||
|         return 0; | ||||
|       } | ||||
|       memset(mem, 0, PGSIZE); | ||||
|       mappages(pgdir, a, PGSIZE, PADDR(mem), PTE_W|PTE_U); | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| // deallocate some of the user pages, in response to sbrk()
 | ||||
| // with a negative argument. if addr is not page-aligned,
 | ||||
| // then only deallocates starting at the next page boundary.
 | ||||
| int | ||||
| deallocuvm(pde_t *pgdir, char *addr, uint sz) | ||||
| { | ||||
|   if (addr + sz > (char*)USERTOP) | ||||
|     return 0; | ||||
|   char *first = (char*) PGROUNDUP((uint)addr); | ||||
|   char *last = PGROUNDDOWN(addr + sz - 1); | ||||
|   char *a; | ||||
|   for(a = first; 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"); | ||||
|       kfree((void *) pa); | ||||
|       *pte = 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| // free a page table and all the physical memory pages
 | ||||
| // in the user part.
 | ||||
| void | ||||
| freevm(pde_t *pgdir) | ||||
| { | ||||
|   uint i, j, da; | ||||
| 
 | ||||
|   if (!pgdir) | ||||
|     panic("freevm: no pgdir\n"); | ||||
|   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; | ||||
|     } | ||||
|   } | ||||
|   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 *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))) | ||||
| 	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); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // given a parent process's page table, create a copy
 | ||||
| // of it for a child.
 | ||||
| pde_t* | ||||
| copyuvm(pde_t *pgdir, uint sz) | ||||
| { | ||||
|   pde_t *d = setupkvm(); | ||||
|   pte_t *pte; | ||||
|   uint pa, i; | ||||
|   char *mem; | ||||
| 
 | ||||
|   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())) | ||||
|         return 0; | ||||
|       memmove(mem, (char *)pa, PGSIZE); | ||||
|       if (!mappages(d, (void *)i, PGSIZE, PADDR(mem), PTE_W|PTE_U)) | ||||
|         return 0; | ||||
|     } | ||||
|   } | ||||
|   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 | ||||
| kvmalloc(void) | ||||
| { | ||||
|   kpgdir = setupkvm(); | ||||
| } | ||||
| 
 | ||||
| // Turn on paging.
 | ||||
| void | ||||
| vmenable(void) | ||||
|  | @ -378,3 +160,208 @@ vmenable(void) | |||
|   lcr0(cr0); | ||||
| } | ||||
| 
 | ||||
| // Switch h/w page table register to the kernel-only page table,
 | ||||
| // for when no process is running.
 | ||||
| void | ||||
| switchkvm(void) | ||||
| { | ||||
|   lcr3(PADDR(kpgdir));   // switch to the kernel page table
 | ||||
| } | ||||
| 
 | ||||
| // Switch TSS and h/w page table to correspond to process p.
 | ||||
| void | ||||
| switchuvm(struct proc *p) | ||||
| { | ||||
|   pushcli(); | ||||
|   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"); | ||||
|   lcr3(PADDR(p->pgdir));  // switch to new address space
 | ||||
|   popcli(); | ||||
| } | ||||
| 
 | ||||
| // Load the initcode into address 0 of pgdir.
 | ||||
| // sz must be less than a page.
 | ||||
| void | ||||
| inituvm(pde_t *pgdir, char *init, uint sz) | ||||
| { | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| // 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) | ||||
| { | ||||
|   uint i, pa, n; | ||||
|   pte_t *pte; | ||||
| 
 | ||||
|   if((uint)addr % PGSIZE != 0) | ||||
|     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"); | ||||
|     pa = PTE_ADDR(*pte); | ||||
|     if(sz - i < PGSIZE) | ||||
|       n = sz - i; | ||||
|     else | ||||
|       n = PGSIZE; | ||||
|     if(readi(ip, (char*)pa, offset+i, n) != n) | ||||
|       return -1; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| // 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) | ||||
| { | ||||
|   char *mem; | ||||
|   uint a; | ||||
| 
 | ||||
|   if(newsz > USERTOP) | ||||
|     return 0; | ||||
|   if(newsz < oldsz) | ||||
|     return oldsz; | ||||
| 
 | ||||
|   a = PGROUNDUP(oldsz); | ||||
|   for(; a < newsz; a += PGSIZE){ | ||||
|     mem = kalloc(); | ||||
|     if(mem == 0){ | ||||
|       cprintf("allocuvm out of memory\n"); | ||||
|       deallocuvm(pgdir, newsz, oldsz); | ||||
|       return 0; | ||||
|     } | ||||
|     memset(mem, 0, PGSIZE); | ||||
|     mappages(pgdir, (char*)a, PGSIZE, PADDR(mem), PTE_W|PTE_U); | ||||
|   } | ||||
|   return newsz; | ||||
| } | ||||
| 
 | ||||
| // 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, uint oldsz, uint newsz) | ||||
| { | ||||
|   pte_t *pte; | ||||
|   uint a, pa; | ||||
| 
 | ||||
|   if(newsz >= oldsz) | ||||
|     return oldsz; | ||||
| 
 | ||||
|   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) | ||||
|         panic("kfree"); | ||||
|       kfree((char*)pa); | ||||
|       *pte = 0; | ||||
|     } | ||||
|   } | ||||
|   return newsz; | ||||
| } | ||||
| 
 | ||||
| // Free a page table and all the physical memory pages
 | ||||
| // in the user part.
 | ||||
| void | ||||
| freevm(pde_t *pgdir) | ||||
| { | ||||
|   uint i; | ||||
| 
 | ||||
|   if(pgdir == 0) | ||||
|     panic("freevm: no pgdir"); | ||||
|   deallocuvm(pgdir, USERTOP, 0); | ||||
|   for(i = 0; i < NPDENTRIES; i++){ | ||||
|     if(pgdir[i] & PTE_P) | ||||
|       kfree((char*)PTE_ADDR(pgdir[i])); | ||||
|   } | ||||
|   kfree((char*)pgdir); | ||||
| } | ||||
| 
 | ||||
| // Given a parent process's page table, create a copy
 | ||||
| // of it for a child.
 | ||||
| pde_t* | ||||
| copyuvm(pde_t *pgdir, uint sz) | ||||
| { | ||||
|   pde_t *d; | ||||
|   pte_t *pte; | ||||
|   uint pa, i; | ||||
|   char *mem; | ||||
| 
 | ||||
|   if((d = setupkvm()) == 0) | ||||
|     return 0; | ||||
|   for(i = 0; i < sz; i += PGSIZE){ | ||||
|     if((pte = walkpgdir(pgdir, (void*)i, 0)) == 0) | ||||
|       panic("copyuvm: pte should exist"); | ||||
|     if(!(*pte & PTE_P)) | ||||
|       panic("copyuvm: page not present"); | ||||
|     pa = PTE_ADDR(*pte); | ||||
|     if((mem = kalloc()) == 0) | ||||
|       goto bad; | ||||
|     memmove(mem, (char*)pa, PGSIZE); | ||||
|     if(mappages(d, (void*)i, PGSIZE, PADDR(mem), PTE_W|PTE_U) < 0) | ||||
|       goto bad; | ||||
|   } | ||||
|   return d; | ||||
| 
 | ||||
| bad: | ||||
|   freevm(d); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| //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 *p, uint len) | ||||
| { | ||||
|   char *buf, *pa0; | ||||
|   uint n, va0; | ||||
|    | ||||
|   buf = (char*)p; | ||||
|   while(len > 0){ | ||||
|     va0 = (uint)PGROUNDDOWN(va); | ||||
|     pa0 = uva2ka(pgdir, (char*)va0); | ||||
|     if(pa0 == 0) | ||||
|       return -1; | ||||
|     n = PGSIZE - (va - va0); | ||||
|     if(n > len) | ||||
|       n = len; | ||||
|     memmove(pa0 + (va - va0), buf, n); | ||||
|     len -= n; | ||||
|     buf += n; | ||||
|     va = va0 + PGSIZE; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +0,0 @@ | |||
| index.html: index.txt mkhtml | ||||
| 	./mkhtml index.txt >_$@ && mv _$@ $@ | ||||
| 
 | ||||
							
								
								
									
										
											BIN
										
									
								
								web/boot.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/boot.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/disk.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/disk.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/exec.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/exec.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/fscall.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/fscall.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/fsdata.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/fsdata.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										412
									
								
								web/index.html
									
										
									
									
									
								
							
							
						
						
									
										412
									
								
								web/index.html
									
										
									
									
									
								
							|  | @ -1,4 +1,3 @@ | |||
| <!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version --> | ||||
| <html> | ||||
| <head> | ||||
| <title>Xv6, a simple Unix-like teaching operating system</title> | ||||
|  | @ -32,31 +31,36 @@ h2 { | |||
| --></style> | ||||
| </head> | ||||
| <body bgcolor=#ffffff> | ||||
| 
 | ||||
| <h1>Xv6, a simple Unix-like teaching operating system</h1> | ||||
| <br><br> | ||||
| 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. | ||||
| 
 | ||||
| <h2>Introduction</h2> | ||||
| 
 | ||||
| Xv6 is a teaching operating system developed in the summer of 2006 for | ||||
| MIT's operating systems | ||||
| course, <a href="http://pdos.csail.mit.edu/6.828">6.828: operating | ||||
| systems Engineering</a>. 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. | ||||
| 
 | ||||
| <p><font color="red">Status: The xv6 code is in pretty good shape, but | ||||
| the commentary is rough.</font> | ||||
| 
 | ||||
| <h2>History and Background</h2> | ||||
| 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. | ||||
| <br><br> | ||||
| 
 | ||||
| <p>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 <a href="#v6">Sixth | ||||
| Edition Unix (aka V6)</a> 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. | ||||
| 
 | ||||
| <p> | ||||
| 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. | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| <p> 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. | ||||
| 
 | ||||
| <p> | ||||
| 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. | ||||
| 
 | ||||
| <h2>Xv6 sources</h2> | ||||
| The latest xv6 is <a href="xv6-rev3.tar.gz">xv6-rev3.tar.gz</a>. | ||||
| 
 | ||||
| The latest xv6 is <a href="xv6-rev5.tar.gz">xv6-rev5.tar.gz</a>. | ||||
| 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 | ||||
| <a href="xv6-rev3.pdf">xv6-rev3.pdf</a>. | ||||
| <a href="xv6-rev5.pdf">xv6-rev5.pdf</a>. | ||||
| The xv6 source code is licensed under the traditional <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>; | ||||
| see the LICENSE file in the source distribution. | ||||
| <br><br> | ||||
| 
 | ||||
| <p> | ||||
| 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 <a href="../../2007/tools.html">6.828 tools page</a>. | ||||
| can be found on the <a href="../2010/tools.html">6.828 tools page</a>. | ||||
| 
 | ||||
| <h2>Lectures</h2> | ||||
| 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. | ||||
| <h2>Xv6 lecture material</h2> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 1. Operating systems</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| <a href="l1.html">lecture notes</a> | ||||
| <a href="os-lab-1.pdf">OS abstractions slides</a> | ||||
| 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. | ||||
| 
 | ||||
| <br><br><b><i>Lecture 2.  PC hardware and x86 programming</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: PC Assembly Language | ||||
| <br><br> | ||||
| Homework: familiarize with Bochs | ||||
| <br><br> | ||||
| <a href="l2.html">lecture notes</a> | ||||
| <a href="os-lab-2.pdf">x86 intro slides</a> | ||||
| <a href="x86-intro.html">homework</a> | ||||
| <h2>Xv6 commentary (rough)</h2> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 3.  Operating system organization</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: Engler et al., Exokernel: An Operating System Architecture | ||||
| for Application-Level Resource Management | ||||
| <br><br> | ||||
| <a href="l3.html">lecture notes</a> | ||||
| <p>The chapters are rough drafts. | ||||
| 
 | ||||
| <br><br><b><i>Lecture 4.  Address spaces using segmentation</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: x86 MMU handout, | ||||
| xv6: bootasm.S, bootother.S, <a href="src/bootmain.c.html">bootmain.c</a>, <a href="src/main.c.html">main.c</a>, <a href="src/init.c.html">init.c</a>, and setupsegs in <a href="src/proc.c.html">proc.c</a>. | ||||
| <br><br> | ||||
| Homework: Bochs stack introduction | ||||
| <br><br> | ||||
| <a href="l4.html">lecture notes</a> | ||||
| <a href="os-lab-3.pdf">x86 virtual memory slides</a> | ||||
| <a href="xv6-intro.html">homework</a> | ||||
| <p>Introduction yet to be written.<br> | ||||
| <ul> | ||||
| <li>read with the code side by side | ||||
| <li>code references look like (xxxx) or (xxxx-yyyy) in small text. | ||||
| <li><a href="xv6-rev5.pdf">this pdf</a> is the one with matching line numbers. | ||||
| <li>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. | ||||
| </ul> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 5.  Address spaces using page tables</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: x86 manual excerpts | ||||
| <br><br> | ||||
| Homework: stuff about gdt | ||||
| XXX not appropriate; should be in Lecture 4 | ||||
| <br><br> | ||||
| <a href="l5.html">lecture notes</a> | ||||
| <a href="unix.pdf">Chapter 0: Operating system interfaces</a> | ||||
| <blockquote> | ||||
| The Unix system call interface.  (rev 4) | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 6.  Interrupts and exceptions</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| It explains the x86 trap setup mechanisms and then | ||||
| examines their use in xv6's SETGATE (<a href="src/mmu.h.html">mmu.h</a>), | ||||
| tvinit (<a href="src/trap.c.html">trap.c</a>), idtinit (<a href="src/trap.c.html">trap.c</a>), <a href="src/vectors.pl.html">vectors.pl</a>, and vectors.S. | ||||
| <br><br> | ||||
| It then traces through a call to the system call open: | ||||
| <a href="src/init.c.html">init.c</a>, usys.S, vector48 and alltraps (vectors.S), trap (<a href="src/trap.c.html">trap.c</a>), | ||||
| syscall (<a href="src/syscall.c.html">syscall.c</a>), | ||||
| sys_open (<a href="src/sysfile.c.html">sysfile.c</a>), fetcharg, fetchint, argint, argptr, argstr (<a href="src/syscall.c.html">syscall.c</a>), | ||||
| <br><br> | ||||
| The interrupt controller, briefly: | ||||
| pic_init and pic_enable (<a href="src/picirq.c.html">picirq.c</a>). | ||||
| The timer and keyboard, briefly: | ||||
| timer_init (<a href="src/timer.c.html">timer.c</a>), console_init (<a href="src/console.c.html">console.c</a>). | ||||
| Enabling and disabling of interrupts. | ||||
| <br><br> | ||||
| Reading: x86 manual excerpts, | ||||
| xv6: trapasm.S, <a href="src/trap.c.html">trap.c</a>, <a href="src/syscall.c.html">syscall.c</a>, and usys.S. | ||||
| Skim <a href="src/lapic.c.html">lapic.c</a>, <a href="src/ioapic.c.html">ioapic.c</a>, <a href="src/picirq.c.html">picirq.c</a>. | ||||
| <br><br> | ||||
| Homework: Explain the 35 words on the top of the | ||||
| stack at first invocation of <code>syscall</code>. | ||||
| <br><br> | ||||
| <a href="l-interrupt.html">lecture notes</a> | ||||
| <a href="x86-intr.html">homework</a> | ||||
| <a href="boot.pdf">Chapter 1: Bootstrap</a> | ||||
| <blockquote> | ||||
| From power on to kernel start. (rev 4) | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 7.  Multiprocessors and locking</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| 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 (<a href="src/proc.c.html">proc.c</a>) | ||||
| and the spin lock implementation (<a href="src/spinlock.c.html">spinlock.c</a>). | ||||
| <br><br> | ||||
| Reading: xv6: <a href="src/spinlock.c.html">spinlock.c</a>.  Skim <a href="src/mp.c.html">mp.c</a>. | ||||
| <br><br> | ||||
| Homework: Interaction between locking and interrupts. | ||||
| Try not disabling interrupts in the disk driver and watch xv6 break. | ||||
| <br><br> | ||||
| <a href="l-lock.html">lecture notes</a> | ||||
| <a href="xv6-lock.html">homework</a> | ||||
| <a href="mem.pdf">Chapter 2: Processes</a> | ||||
| <blockquote> | ||||
| Memory and process allocation, segments, the first user process. (rev 4) | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 8.  Threads, processes and context switching</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Illustrations: thread switching (swtch.S), scheduler (<a href="src/proc.c.html">proc.c</a>), sys_fork (<a href="src/sysproc.c.html">sysproc.c</a>) | ||||
| <br><br> | ||||
| Reading: <a href="src/proc.c.html">proc.c</a>, swtch.S, sys_fork (<a href="src/sysproc.c.html">sysproc.c</a>) | ||||
| <br><br> | ||||
| Homework: trace through stack switching. | ||||
| <br><br> | ||||
| <a href="l-threads.html">lecture notes (need to be updated to use swtch)</a> | ||||
| <a href="xv6-sched.html">homework</a> | ||||
| <a href="trap.pdf">Chapter 3: Traps</a> | ||||
| <blockquote> | ||||
| Low-level trap mechanism, trap handler, system call arguments, sbrk, fork. | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 9.  Processes and coordination</i></b> | ||||
| <br><br> | ||||
| This lecture introduces the idea of sequence coordination | ||||
| and then examines the particular solution illustrated by | ||||
| sleep and wakeup (<a href="src/proc.c.html">proc.c</a>). | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: <a href="src/proc.c.html">proc.c</a>, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (<a href="src/sysproc.c.html">sysproc.c</a>). | ||||
| <br><br> | ||||
| Homework: Explain how sleep and wakeup would break | ||||
| without proc_table_lock.  Explain how devices would break | ||||
| without second lock argument to sleep. | ||||
| <br><br> | ||||
| <a href="l-coordination.html">lecture notes</a> | ||||
| <a href="xv6-sleep.html">homework</a> | ||||
| <a href="lock.pdf">Chapter 4: Locks</a> | ||||
| <blockquote> | ||||
| Locks and interrupts. | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 10.  Files and disk I/O</i></b> | ||||
| <br><br> | ||||
| 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. | ||||
| <br><br> | ||||
| Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and | ||||
| 	code related to these calls in <a href="src/fs.c.html">fs.c</a>, <a href="src/bio.c.html">bio.c</a>, <a href="src/ide.c.html">ide.c</a>, and <a href="src/file.c.html">file.c</a>. | ||||
| <br><br> | ||||
| Homework: Add print to bwrite to trace every disk write. | ||||
| Explain the disk writes caused by some simple shell commands. | ||||
| <br><br> | ||||
| <a href="l-fs.html">lecture notes</a> | ||||
| <a href="xv6-disk.html">homework</a> | ||||
| <a href="sched.pdf">Chapter 5: Scheduling and coordination</a> | ||||
| <blockquote> | ||||
| Scheduling, sleep and wakeup, pipes, wait and exit. | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 11.  Naming</i></b> | ||||
| <br><br> | ||||
| The last lecture discussed on-disk file system representation. | ||||
| This lecture covers the implementation of | ||||
| file system paths (namei in <a href="src/fs.c.html">fs.c</a>) | ||||
| and also discusses the security problems of a shared /tmp | ||||
| and symbolic links. | ||||
| <br><br> | ||||
| Understanding exec (<a href="src/exec.c.html">exec.c</a>) is left as an exercise. | ||||
| <br><br> | ||||
| Reading: namei in <a href="src/fs.c.html">fs.c</a>, <a href="src/sysfile.c.html">sysfile.c</a>, <a href="src/file.c.html">file.c</a>. | ||||
| <br><br> | ||||
| Homework: Explain how to implement symbolic links in xv6. | ||||
| <br><br> | ||||
| <a href="l-name.html">lecture notes</a> | ||||
| <a href="xv6-names.html">homework</a> | ||||
| <a href="disk.pdf">Chapter 6: Buffer cache</a> | ||||
| <blockquote> | ||||
| Buffer cache and IDE disk driver. | ||||
| </blockquote> | ||||
| 
 | ||||
| <br><br><b><i>Lecture 12.  High-performance file systems</i></b> | ||||
| <br><br> | ||||
| This lecture is the first of the research paper-based lectures. | ||||
| It discusses the “soft updates” paper, | ||||
| using xv6 as a concrete example. | ||||
| <a href="fsdata.pdf">Chapter 7: File system data</a> | ||||
| <blockquote> | ||||
| Block in use bitmap, block allocation, inode structure, inode contents, | ||||
| directories, path names. | ||||
| </blockquote> | ||||
| 
 | ||||
| <a href="fscall.pdf">Chapter 8: File system calls</a> | ||||
| <blockquote> | ||||
| FIle descriptors, open, close, dup, read, write. | ||||
| </blockquote> | ||||
| 
 | ||||
| <a href="exec.pdf">Chapter 9: Exec</a> | ||||
| <blockquote> | ||||
| Exec | ||||
| </blockquote> | ||||
| 
 | ||||
| Appendix A: Low-level C and inline assembly | ||||
| <blockquote> | ||||
| Intro to C and inline assembly for people who only know Java (say). | ||||
| Examples drawn entirely from xv6 source. | ||||
| </blockquote> | ||||
| 
 | ||||
| Appendix B: Additional drivers. | ||||
| <blockquote> | ||||
| Keyboard, screen, probably MP hardware. | ||||
| </blockquote> | ||||
| 
 | ||||
| <a name="v6"></a> | ||||
| <h2>Unix Version 6</h2> | ||||
| 
 | ||||
| <p>6.828's xv6 is inspired by Unix V6 and by: | ||||
| 
 | ||||
| <ul> | ||||
| 
 | ||||
| <li>Lions' <i>Commentary on UNIX' 6th Edition</i>, John Lions, Peer to | ||||
| Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, 2000). | ||||
| 	<ul> | ||||
| 
 | ||||
| 	<li>An on-line version of the <a | ||||
| 	href="http://www.lemis.com/grog/Documentation/Lions/">Lions | ||||
| 	commentary</a>, and <a href="http://v6.cuzuco.com/">the source code</a>. | ||||
| 
 | ||||
| 
 | ||||
| 	<li>The v6 source code is also available <a | ||||
| href="http://minnie.tuhs.org/UnixTree/V6/usr/sys/">online</a> | ||||
| 	through <a | ||||
| 	href="http://minnie.tuhs.org/PUPS/">the PDP Unix Preservation | ||||
| 	Society</a>. | ||||
| 	</ul> | ||||
| 
 | ||||
| </ul> | ||||
| 
 | ||||
| The following are useful to read the original code: | ||||
| <ul> | ||||
| <li><i> | ||||
| The PDP11/40 Processor Handbook</i>, Digital Equipment Corporation, 1972. | ||||
| <ul> | ||||
| <li>A <a href="http://pdos.csail.mit.edu/6.828/2005/readings/pdp11-40.pdf">PDF</a> (made from scanned images,  | ||||
| and not text-searchable) | ||||
| <li>A <a href="http://pdos.csail.mit.edu/6.828/2005/pdp11/">web-based | ||||
| version</a> that is indexed by instruction name. | ||||
| </ul> | ||||
| 
 | ||||
| </ul> | ||||
| 
 | ||||
| <h2>Feedback</h2> | ||||
| If you are interested in using xv6 or have used xv6 in a course, | ||||
|  | @ -346,13 +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. | ||||
| <br><br> | ||||
| <p> | ||||
| Russ Cox (rsc@swtch.com)<br> | ||||
| Frans Kaashoek (kaashoek@mit.edu)<br> | ||||
| Robert Morris (rtm@mit.edu) | ||||
| <br><br> | ||||
| <p> | ||||
| You can reach all of us at 6.828-staff@pdos.csail.mit.edu. | ||||
| <br><br> | ||||
| <br><br> | ||||
| </body> | ||||
| </html> | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										339
									
								
								web/index.txt
									
										
									
									
									
								
							
							
						
						
									
										339
									
								
								web/index.txt
									
										
									
									
									
								
							|  | @ -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-rev2.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 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 <code>syscall</code>. | ||||
| 
 | ||||
| [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)<br> | ||||
| Frans Kaashoek (kaashoek@mit.edu)<br> | ||||
| 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. | ||||
							
								
								
									
										
											BIN
										
									
								
								web/lock.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/lock.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/mem.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/mem.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										70
									
								
								web/mkhtml
									
										
									
									
									
								
							
							
						
						
									
										70
									
								
								web/mkhtml
									
										
									
									
									
								
							|  | @ -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/^$/<br><br>/mg; | ||||
| $text =~ s!\b([a-z0-9]+\.(c|s|pl|h))\b!<a href="src/$1.html">$1</a>!g; | ||||
| $text =~ s!^(Lecture [0-9]+\. .*?)$!<b><i>$1</i></b>!mg; | ||||
| $text =~ s!^\* (.*?)$!<h2>$1</h2>!mg; | ||||
| $text =~ s!((<br>)+\n)+<h2>!\n<h2>!g; | ||||
| $text =~ s!</h2>\n?((<br>)+\n)+!</h2>\n!g; | ||||
| $text =~ s!((<br>)+\n)+<b>!\n<br><br><b>!g; | ||||
| $text =~ s!\b\s*--\s*\b!\–!g; | ||||
| $text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!<a href="$1">$2</a>!g; | ||||
| $text =~ s!\[([^ \t]+)\]!<a href="$1">$1</a>!g; | ||||
| 
 | ||||
| $text =~ s!``!\“!g; | ||||
| $text =~ s!''!\”!g; | ||||
| 
 | ||||
| print <<EOF; | ||||
| <!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version --> | ||||
| <html> | ||||
| <head> | ||||
| <title>$title</title> | ||||
| <style type="text/css"><!-- | ||||
| body { | ||||
| 	background-color: white; | ||||
| 	color: black; | ||||
| 	font-size: medium; | ||||
| 	line-height: 1.2em; | ||||
| 	margin-left: 0.5in; | ||||
| 	margin-right: 0.5in; | ||||
| 	margin-top: 0; | ||||
| 	margin-bottom: 0; | ||||
| } | ||||
| 
 | ||||
| h1 { | ||||
| 	text-indent: 0in; | ||||
| 	text-align: left; | ||||
| 	margin-top: 2em; | ||||
| 	font-weight: bold; | ||||
| 	font-size: 1.4em; | ||||
| } | ||||
| 
 | ||||
| h2 { | ||||
| 	text-indent: 0in; | ||||
| 	text-align: left; | ||||
| 	margin-top: 2em; | ||||
| 	font-weight: bold; | ||||
| 	font-size: 1.2em; | ||||
| } | ||||
| --></style> | ||||
| </head> | ||||
| <body bgcolor=#ffffff> | ||||
| <h1>$title</h1> | ||||
| <br><br> | ||||
| EOF | ||||
| print $text; | ||||
| print <<EOF; | ||||
| </body> | ||||
| </html> | ||||
| EOF | ||||
							
								
								
									
										
											BIN
										
									
								
								web/sched.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/sched.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/trap.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/trap.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								web/unix.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/unix.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										87
									
								
								x86.h
									
										
									
									
									
								
							
							
						
						
									
										87
									
								
								x86.h
									
										
									
									
									
								
							|  | @ -90,25 +90,28 @@ 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 uint | ||||
| rebp(void) | ||||
| { | ||||
|   uint val; | ||||
|   asm volatile("movl %%ebp,%0" : "=r" (val)); | ||||
|   return val; | ||||
| } | ||||
| 
 | ||||
| static inline uint | ||||
| resp(void) | ||||
| { | ||||
|   uint val; | ||||
|   asm volatile("movl %%esp,%0" : "=r" (val)); | ||||
|   return val; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| cli(void) | ||||
| { | ||||
|  | @ -121,66 +124,56 @@ sti(void) | |||
|   asm volatile("sti"); | ||||
| } | ||||
| 
 | ||||
| static inline void lcr0(uint val) | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| //PAGEBREAK!
 | ||||
| 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)); | ||||
|   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().
 | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								xv6-rev4.tar.gz
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								xv6-rev4.tar.gz
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								xv6-rev5.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								xv6-rev5.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								xv6-rev5.tar.gz
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								xv6-rev5.tar.gz
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Frans Kaashoek
						Frans Kaashoek