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 | *.o | ||||||
| *.d | *.d | ||||||
|  |  | ||||||
							
								
								
									
										93
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										93
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -26,19 +26,53 @@ OBJS = \ | ||||||
| 	uart.o\
 | 	uart.o\
 | ||||||
| 	vectors.o\
 | 	vectors.o\
 | ||||||
| 	vm.o\
 | 	vm.o\
 | ||||||
|  | 	log.o\
 | ||||||
| 
 | 
 | ||||||
| # Cross-compiling (e.g., on Mac OS X)
 | # Cross-compiling (e.g., on Mac OS X)
 | ||||||
| TOOLPREFIX = i386-jos-elf- | #TOOLPREFIX = i386-jos-elf-
 | ||||||
| 
 | 
 | ||||||
| # Using native tools (e.g., on X86 Linux)
 | # Using native tools (e.g., on X86 Linux)
 | ||||||
| #TOOLPREFIX = 
 | #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 | CC = $(TOOLPREFIX)gcc | ||||||
| AS = $(TOOLPREFIX)gas | AS = $(TOOLPREFIX)gas | ||||||
| LD = $(TOOLPREFIX)ld | LD = $(TOOLPREFIX)ld | ||||||
| OBJCOPY = $(TOOLPREFIX)objcopy | OBJCOPY = $(TOOLPREFIX)objcopy | ||||||
| OBJDUMP = $(TOOLPREFIX)objdump | 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) | CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) | ||||||
| ASFLAGS = -m32 -gdwarf-2 | ASFLAGS = -m32 -gdwarf-2 | ||||||
| # FreeBSD ld wants ``elf_i386_fbsd''
 | # 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=bootblock of=xv6.img conv=notrunc | ||||||
| 	dd if=kernel of=xv6.img seek=1 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 | bootblock: bootasm.S bootmain.c | ||||||
| 	$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c | 	$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c | ||||||
| 	$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S | 	$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S | ||||||
|  | @ -69,11 +108,23 @@ initcode: initcode.S | ||||||
| 	$(OBJCOPY) -S -O binary initcode.out initcode | 	$(OBJCOPY) -S -O binary initcode.out initcode | ||||||
| 	$(OBJDUMP) -S initcode.o > initcode.asm | 	$(OBJDUMP) -S initcode.o > initcode.asm | ||||||
| 
 | 
 | ||||||
| kernel: $(OBJS) bootother initcode | kernel: $(OBJS) multiboot.o data.o bootother initcode | ||||||
| 	$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother | 	$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother | ||||||
| 	$(OBJDUMP) -S kernel > kernel.asm | 	$(OBJDUMP) -S kernel > kernel.asm | ||||||
| 	$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym | 	$(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 | tags: $(OBJS) bootother.S _init | ||||||
| 	etags *.S *.c | 	etags *.S *.c | ||||||
| 
 | 
 | ||||||
|  | @ -94,7 +145,7 @@ _forktest: forktest.o $(ULIB) | ||||||
| 	$(OBJDUMP) -S _forktest > forktest.asm | 	$(OBJDUMP) -S _forktest > forktest.asm | ||||||
| 
 | 
 | ||||||
| mkfs: mkfs.c fs.h | mkfs: mkfs.c fs.h | ||||||
| 	gcc -Wall -o mkfs mkfs.c | 	gcc -m32 -Werror -Wall -o mkfs mkfs.c | ||||||
| 
 | 
 | ||||||
| UPROGS=\
 | UPROGS=\
 | ||||||
| 	_cat\
 | 	_cat\
 | ||||||
|  | @ -126,7 +177,7 @@ clean: | ||||||
| 
 | 
 | ||||||
| # make a printout
 | # make a printout
 | ||||||
| FILES = $(shell grep -v '^\#' runoff.list) | FILES = $(shell grep -v '^\#' runoff.list) | ||||||
| PRINT = runoff.list $(FILES) | PRINT = runoff.list runoff.spec $(FILES) | ||||||
| 
 | 
 | ||||||
| xv6.pdf: $(PRINT) | xv6.pdf: $(PRINT) | ||||||
| 	./runoff | 	./runoff | ||||||
|  | @ -143,27 +194,33 @@ bochs : fs.img xv6.img | ||||||
| # try to generate a unique GDB port
 | # try to generate a unique GDB port
 | ||||||
| GDBPORT = $(shell expr `id -u` % 5000 + 25000) | GDBPORT = $(shell expr `id -u` % 5000 + 25000) | ||||||
| # QEMU's gdb stub command line changed in 0.11
 | # 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)"; \
 | 	then echo "-gdb tcp::$(GDBPORT)"; \
 | ||||||
| 	else echo "-s -p $(GDBPORT)"; fi) | 	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: 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-nox: fs.img xv6.img | ||||||
| 	qemu -nographic $(QEMUOPTS) | 	$(QEMU) -nographic $(QEMUOPTS) | ||||||
| 
 | 
 | ||||||
| .gdbinit: .gdbinit.tmpl | .gdbinit: .gdbinit.tmpl | ||||||
| 	sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ | 	sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ | ||||||
| 
 | 
 | ||||||
| qemu-gdb: fs.img xv6.img .gdbinit | qemu-gdb: fs.img xv6.img .gdbinit | ||||||
| 	@echo "*** Now run 'gdb'." 1>&2 | 	@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 | qemu-nox-gdb: fs.img xv6.img .gdbinit | ||||||
| 	@echo "*** Now run 'gdb'." 1>&2 | 	@echo "*** Now run 'gdb'." 1>&2 | ||||||
| 	qemu -nographic $(QEMUOPTS) -S $(QEMUGDB) | 	$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) | ||||||
| 
 | 
 | ||||||
| # CUT HERE
 | # CUT HERE
 | ||||||
| # prepare dist for students
 | # prepare dist for students
 | ||||||
|  | @ -195,14 +252,16 @@ dist-test: | ||||||
| 	rm -rf dist-test | 	rm -rf dist-test | ||||||
| 	mkdir dist-test | 	mkdir dist-test | ||||||
| 	cp dist/* dist-test | 	cp dist/* dist-test | ||||||
| 	cd dist-test; ../m print | 	cd dist-test; $(MAKE) print | ||||||
| 	cd dist-test; ../m bochs || true | 	cd dist-test; $(MAKE) bochs || true | ||||||
| 	cd dist-test; ../m qemu | 	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.
 | # make a new revision.
 | ||||||
| tar: | tar: | ||||||
| 	rm -rf /tmp/xv6 | 	rm -rf /tmp/xv6 | ||||||
| 	mkdir -p /tmp/xv6 | 	mkdir -p /tmp/xv6 | ||||||
| 	cp dist/* dist/.gdbinit.tmpl /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) |     Russ Cox (context switching, locking) | ||||||
|     Cliff Frey (MP) |     Cliff Frey (MP) | ||||||
|     Xiao Yu (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 | The code in the files that constitute xv6 is | ||||||
| Copyright 2006-2007 Frans Kaashoek, Robert Morris, and Russ Cox. | 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. | To run xv6, you can use Bochs or QEMU, both PC simulators. | ||||||
| Bochs makes debugging easier, but QEMU is much faster.  | 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 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  | To run in QEMU, run "make qemu". | ||||||
| standard output. |  | ||||||
| 
 | 
 | ||||||
| To create a typeset version of the code, run "make xv6.pdf". | To create a typeset version of the code, run "make xv6.pdf".  This | ||||||
| This requires the "mpage" text formatting utility. | requires the "mpage" utility.  See http://www.mesa.nl/pub/mpage/. | ||||||
| See http://www.mesa.nl/pub/mpage/. |  | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								bootasm.S
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								bootasm.S
									
										
									
									
									
								
							|  | @ -13,7 +13,7 @@ | ||||||
| .code16                       # Assemble for 16-bit mode | .code16                       # Assemble for 16-bit mode | ||||||
| .globl start
 | .globl start
 | ||||||
| start: | start: | ||||||
|   cli                         # Disable interrupts |   cli                         # BIOS enabled interrupts; disable | ||||||
| 
 | 
 | ||||||
|   # Set up the important data segment registers (DS, ES, SS). |   # Set up the important data segment registers (DS, ES, SS). | ||||||
|   xorw    %ax,%ax             # Segment number zero |   xorw    %ax,%ax             # Segment number zero | ||||||
|  | @ -21,10 +21,8 @@ start: | ||||||
|   movw    %ax,%es             # -> Extra Segment |   movw    %ax,%es             # -> Extra Segment | ||||||
|   movw    %ax,%ss             # -> Stack Segment |   movw    %ax,%ss             # -> Stack Segment | ||||||
| 
 | 
 | ||||||
|   # Enable A20: |   # Physical address line A20 is tied to zero so that the first PCs  | ||||||
|   #   For backwards compatibility with the earliest PCs, physical |   # with 2 MB would run software that assumed 1 MB.  Undo that. | ||||||
|   #   address line 20 is tied low, so that addresses higher than |  | ||||||
|   #   1MB wrap around to zero by default.  This code undoes this. |  | ||||||
| seta20.1: | seta20.1: | ||||||
|   inb     $0x64,%al               # Wait for not busy |   inb     $0x64,%al               # Wait for not busy | ||||||
|   testb   $0x2,%al |   testb   $0x2,%al | ||||||
|  | @ -41,23 +39,21 @@ seta20.2: | ||||||
|   movb    $0xdf,%al               # 0xdf -> port 0x60 |   movb    $0xdf,%al               # 0xdf -> port 0x60 | ||||||
|   outb    %al,$0x60 |   outb    %al,$0x60 | ||||||
| 
 | 
 | ||||||
| //PAGEBREAK! |   # Switch from real to protected mode.  Use a bootstrap GDT that makes | ||||||
|   # Switch from real to protected mode, using a bootstrap GDT |   # virtual addresses map dierctly to  physical addresses so that the | ||||||
|   # and segment translation that makes virtual addresses  |   # effective memory map doesn't change during the transition. | ||||||
|   # identical to physical addresses, so that the  |  | ||||||
|   # effective memory map does not change during the switch. |  | ||||||
|   lgdt    gdtdesc |   lgdt    gdtdesc | ||||||
|   movl    %cr0, %eax |   movl    %cr0, %eax | ||||||
|   orl     $CR0_PE, %eax |   orl     $CR0_PE, %eax | ||||||
|   movl    %eax, %cr0 |   movl    %eax, %cr0 | ||||||
|    | 
 | ||||||
|   # This ljmp is how you load the CS (Code Segment) register. | //PAGEBREAK! | ||||||
|   # SEG_ASM produces segment descriptors with the 32-bit mode |   # Complete transition to 32-bit protected mode by using long jmp | ||||||
|   # flag set (the D flag), so addresses and word operands will |   # to reload %cs and %eip.  The segment registers are set up with no | ||||||
|   # default to 32 bits after this jump. |   # translation, so that the mapping is still the identity mapping. | ||||||
|   ljmp    $(SEG_KCODE<<3), $start32 |   ljmp    $(SEG_KCODE<<3), $start32 | ||||||
| 
 | 
 | ||||||
| .code32                       # Assemble for 32-bit mode | .code32  # Tell assembler to generate 32-bit code now. | ||||||
| start32: | start32: | ||||||
|   # Set up the protected-mode data segment registers |   # Set up the protected-mode data segment registers | ||||||
|   movw    $(SEG_KDATA<<3), %ax    # Our data segment selector |   movw    $(SEG_KDATA<<3), %ax    # Our data segment selector | ||||||
|  |  | ||||||
|  | @ -33,8 +33,8 @@ bootmain(void) | ||||||
|   // Load each program segment (ignores ph flags).
 |   // Load each program segment (ignores ph flags).
 | ||||||
|   ph = (struct proghdr*)((uchar*)elf + elf->phoff); |   ph = (struct proghdr*)((uchar*)elf + elf->phoff); | ||||||
|   eph = ph + elf->phnum; |   eph = ph + elf->phnum; | ||||||
|   for(; ph < eph; ph++) { |   for(; ph < eph; ph++){ | ||||||
|     va = (uchar*)(ph->va & 0xFFFFFF); |     va = (uchar*)ph->va; | ||||||
|     readseg(va, ph->filesz, ph->offset); |     readseg(va, ph->filesz, ph->offset); | ||||||
|     if(ph->memsz > ph->filesz) |     if(ph->memsz > ph->filesz) | ||||||
|       stosb(va + ph->filesz, 0, 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.
 |   // Call the entry point from the ELF header.
 | ||||||
|   // Does not return!
 |   // Does not return!
 | ||||||
|   entry = (void(*)(void))(elf->entry & 0xFFFFFF); |   entry = (void(*)(void))(elf->entry); | ||||||
|   entry(); |   entry(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								bootother.S
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								bootother.S
									
										
									
									
									
								
							|  | @ -9,80 +9,69 @@ | ||||||
| # Because this code sets DS to zero, it must sit | # Because this code sets DS to zero, it must sit | ||||||
| # at an address in the low 2^16 bytes. | # at an address in the low 2^16 bytes. | ||||||
| # | # | ||||||
| # Bootothers (in main.c) sends the STARTUPs, one at a time. | # Bootothers (in main.c) sends the STARTUPs one at a time. | ||||||
| # It puts this code (start) at 0x7000. | # It copies this code (start) at 0x7000. | ||||||
| # It puts the correct %esp in start-4, | # It puts the address of a newly allocated per-core stack in start-4, | ||||||
| # and the place to jump to in start-8. | # and the address of the place to jump to (mpmain) in start-8. | ||||||
| # | # | ||||||
| # This code is identical to bootasm.S except: | # This code is identical to bootasm.S except: | ||||||
| #   - it does not need to enable A20 | #   - it does not need to enable A20 | ||||||
| #   - it uses the address at start-4 for the %esp | #   - it uses the address at start-4 for the %esp | ||||||
| #   - it jumps to the address at start-8 instead of calling bootmain | #   - it jumps to the address at start-8 instead of calling bootmain | ||||||
| 
 | 
 | ||||||
| #define SEG_KCODE 1  // kernel code | #define SEG_KCODE 1 | ||||||
| #define SEG_KDATA 2  // kernel data+stack | #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
 | .globl start
 | ||||||
| start: | start: | ||||||
|   cli                         # Disable interrupts |   cli             | ||||||
| 
 | 
 | ||||||
|   # Set up the important data segment registers (DS, ES, SS). |   xorw    %ax,%ax | ||||||
|   xorw    %ax,%ax             # Segment number zero |   movw    %ax,%ds | ||||||
|   movw    %ax,%ds             # -> Data Segment |   movw    %ax,%es | ||||||
|   movw    %ax,%es             # -> Extra Segment |   movw    %ax,%ss | ||||||
|   movw    %ax,%ss             # -> Stack Segment |  | ||||||
| 
 | 
 | ||||||
| //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 |   lgdt    gdtdesc | ||||||
|   movl    %cr0, %eax |   movl    %cr0, %eax | ||||||
|   orl     $CR0_PE, %eax |   orl     $CR0_PE, %eax | ||||||
|   movl    %eax, %cr0 |   movl    %eax, %cr0 | ||||||
| 
 | 
 | ||||||
|   # This ljmp is how you load the CS (Code Segment) register. | //PAGEBREAK! | ||||||
|   # SEG_ASM produces segment descriptors with the 32-bit mode |  | ||||||
|   # flag set (the D flag), so addresses and word operands will |  | ||||||
|   # default to 32 bits after this jump. |  | ||||||
|   ljmp    $(SEG_KCODE<<3), $start32 |   ljmp    $(SEG_KCODE<<3), $start32 | ||||||
| 
 | 
 | ||||||
| .code32                       # Assemble for 32-bit mode | .code32 | ||||||
| start32: | start32: | ||||||
|   # Set up the protected-mode data segment registers |   movw    $(SEG_KDATA<<3), %ax | ||||||
|   movw    $(SEG_KDATA<<3), %ax    # Our data segment selector |   movw    %ax, %ds | ||||||
|   movw    %ax, %ds                # -> DS: Data Segment |   movw    %ax, %es | ||||||
|   movw    %ax, %es                # -> ES: Extra Segment |   movw    %ax, %ss | ||||||
|   movw    %ax, %ss                # -> SS: Stack Segment |   movw    $0, %ax | ||||||
|   movw    $0, %ax                 # Zero segments not ready for use |   movw    %ax, %fs | ||||||
|   movw    %ax, %fs                # -> FS |   movw    %ax, %gs | ||||||
|   movw    %ax, %gs                # -> GS |  | ||||||
| 
 | 
 | ||||||
|   # Set up the stack pointer and call into C. |   # switch to the stack allocated by bootothers() | ||||||
|   movl    start-4, %esp |   movl    start-4, %esp | ||||||
|  | 
 | ||||||
|  |   # call mpmain() | ||||||
|   call	*(start-8) |   call	*(start-8) | ||||||
| 
 | 
 | ||||||
|   # If the call returns (it shouldn't), trigger a Bochs |   movw    $0x8a00, %ax | ||||||
|   # breakpoint if running under Bochs, then loop. |  | ||||||
|   movw    $0x8a00, %ax            # 0x8a00 -> port 0x8a00 |  | ||||||
|   movw    %ax, %dx |   movw    %ax, %dx | ||||||
|   outw    %ax, %dx |   outw    %ax, %dx | ||||||
|   movw    $0x8ae0, %ax            # 0x8ae0 -> port 0x8a00 |   movw    $0x8ae0, %ax | ||||||
|   outw    %ax, %dx |   outw    %ax, %dx | ||||||
| spin: | spin: | ||||||
|   jmp     spin |   jmp     spin | ||||||
| 
 | 
 | ||||||
| # Bootstrap GDT | .p2align 2
 | ||||||
| .p2align 2                                # force 4 byte alignment |  | ||||||
| gdt: | gdt: | ||||||
|   SEG_NULLASM                             # null seg |   SEG_NULLASM | ||||||
|   SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)   # code seg |   SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) | ||||||
|   SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg |   SEG_ASM(STA_W, 0x0, 0xffffffff) | ||||||
| 
 | 
 | ||||||
| gdtdesc: | gdtdesc: | ||||||
|   .word   (gdtdesc - gdt - 1)                            # sizeof(gdt) - 1 |   .word   (gdtdesc - gdt - 1) | ||||||
|   .long   gdt                             # address gdt |   .long   gdt
 | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								console.c
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								console.c
									
										
									
									
									
								
							|  | @ -18,28 +18,29 @@ static void consputc(int); | ||||||
| static int panicked = 0; | static int panicked = 0; | ||||||
| 
 | 
 | ||||||
| static struct { | static struct { | ||||||
| 	struct spinlock lock; |   struct spinlock lock; | ||||||
| 	int locking; |   int locking; | ||||||
| } cons; | } cons; | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| printint(int xx, int base, int sgn) | printint(int xx, int base, int sign) | ||||||
| { | { | ||||||
|   static char digits[] = "0123456789abcdef"; |   static char digits[] = "0123456789abcdef"; | ||||||
|   char buf[16]; |   char buf[16]; | ||||||
|   int i = 0, neg = 0; |   int i; | ||||||
|   uint x; |   uint x; | ||||||
| 
 | 
 | ||||||
|   if(sgn && xx < 0){ |   if(sign && (sign = xx < 0)) | ||||||
|     neg = 1; |  | ||||||
|     x = -xx; |     x = -xx; | ||||||
|   } else |   else | ||||||
|     x = xx; |     x = xx; | ||||||
| 
 | 
 | ||||||
|  |   i = 0; | ||||||
|   do{ |   do{ | ||||||
|     buf[i++] = digits[x % base]; |     buf[i++] = digits[x % base]; | ||||||
|   }while((x /= base) != 0); |   }while((x /= base) != 0); | ||||||
|   if(neg) | 
 | ||||||
|  |   if(sign) | ||||||
|     buf[i++] = '-'; |     buf[i++] = '-'; | ||||||
| 
 | 
 | ||||||
|   while(--i >= 0) |   while(--i >= 0) | ||||||
|  | @ -136,8 +137,7 @@ cgaputc(int c) | ||||||
|   if(c == '\n') |   if(c == '\n') | ||||||
|     pos += 80 - pos%80; |     pos += 80 - pos%80; | ||||||
|   else if(c == BACKSPACE){ |   else if(c == BACKSPACE){ | ||||||
|     if(pos > 0) |     if(pos > 0) --pos; | ||||||
|       crt[--pos] = ' ' | 0x0700; |  | ||||||
|   } else |   } else | ||||||
|     crt[pos++] = (c&0xff) | 0x0700;  // black on white
 |     crt[pos++] = (c&0xff) | 0x0700;  // black on white
 | ||||||
|    |    | ||||||
|  | @ -163,16 +163,13 @@ consputc(int c) | ||||||
|       ; |       ; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (c == BACKSPACE) { |   if(c == BACKSPACE){ | ||||||
|     uartputc('\b'); |     uartputc('\b'); uartputc(' '); uartputc('\b'); | ||||||
|     uartputc(' '); |  | ||||||
|     uartputc('\b'); |  | ||||||
|   } else |   } else | ||||||
|     uartputc(c); |     uartputc(c); | ||||||
|   cgaputc(c); |   cgaputc(c); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //PAGEBREAK: 50
 |  | ||||||
| #define INPUT_BUF 128 | #define INPUT_BUF 128 | ||||||
| struct { | struct { | ||||||
|   struct spinlock lock; |   struct spinlock lock; | ||||||
|  | @ -202,8 +199,7 @@ consoleintr(int (*getc)(void)) | ||||||
|         consputc(BACKSPACE); |         consputc(BACKSPACE); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case C('H'):  // Backspace
 |     case C('H'): case '\x7f':  // Backspace
 | ||||||
|     case '\x7f': |  | ||||||
|       if(input.e != input.w){ |       if(input.e != input.w){ | ||||||
|         input.e--; |         input.e--; | ||||||
|         consputc(BACKSPACE); |         consputc(BACKSPACE); | ||||||
|  | @ -211,9 +207,7 @@ consoleintr(int (*getc)(void)) | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       if(c != 0 && input.e-input.r < INPUT_BUF){ |       if(c != 0 && input.e-input.r < INPUT_BUF){ | ||||||
|         // The serial port produces 0x13, not 0x10
 |         c = (c == '\r') ? '\n' : c; | ||||||
|         if(c == '\r') |  | ||||||
|           c = '\n'; |  | ||||||
|         input.buf[input.e++ % INPUT_BUF] = c; |         input.buf[input.e++ % INPUT_BUF] = c; | ||||||
|         consputc(c); |         consputc(c); | ||||||
|         if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ |         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
 | ||||||
							
								
								
									
										31
									
								
								defs.h
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								defs.h
									
										
									
									
									
								
							|  | @ -6,6 +6,7 @@ struct pipe; | ||||||
| struct proc; | struct proc; | ||||||
| struct spinlock; | struct spinlock; | ||||||
| struct stat; | struct stat; | ||||||
|  | struct superblock; | ||||||
| 
 | 
 | ||||||
| // bio.c
 | // bio.c
 | ||||||
| void            binit(void); | void            binit(void); | ||||||
|  | @ -32,6 +33,7 @@ int             filestat(struct file*, struct stat*); | ||||||
| int             filewrite(struct file*, char*, int n); | int             filewrite(struct file*, char*, int n); | ||||||
| 
 | 
 | ||||||
| // fs.c
 | // fs.c
 | ||||||
|  | void            readsb(int dev, struct superblock *sb); | ||||||
| int             dirlink(struct inode*, char*, uint); | int             dirlink(struct inode*, char*, uint); | ||||||
| struct inode*   dirlookup(struct inode*, char*, uint*); | struct inode*   dirlookup(struct inode*, char*, uint*); | ||||||
| struct inode*   ialloc(uint, short); | struct inode*   ialloc(uint, short); | ||||||
|  | @ -62,7 +64,7 @@ void            ioapicinit(void); | ||||||
| // kalloc.c
 | // kalloc.c
 | ||||||
| char*           kalloc(void); | char*           kalloc(void); | ||||||
| void            kfree(char*); | void            kfree(char*); | ||||||
| void            kinit(char*,uint); | void            kinit(void); | ||||||
| 
 | 
 | ||||||
| // kbd.c
 | // kbd.c
 | ||||||
| void            kbdintr(void); | void            kbdintr(void); | ||||||
|  | @ -75,6 +77,12 @@ void            lapicinit(int); | ||||||
| void            lapicstartap(uchar, uint); | void            lapicstartap(uchar, uint); | ||||||
| void            microdelay(int); | void            microdelay(int); | ||||||
| 
 | 
 | ||||||
|  | // log.c
 | ||||||
|  | void            initlog(void); | ||||||
|  | void            log_write(struct buf*); | ||||||
|  | void            begin_trans(); | ||||||
|  | void            commit_trans(); | ||||||
|  | 
 | ||||||
| // mp.c
 | // mp.c
 | ||||||
| extern int      ismp; | extern int      ismp; | ||||||
| int             mpbcpu(void); | int             mpbcpu(void); | ||||||
|  | @ -101,6 +109,7 @@ int             kill(int); | ||||||
| void            pinit(void); | void            pinit(void); | ||||||
| void            procdump(void); | void            procdump(void); | ||||||
| void            scheduler(void) __attribute__((noreturn)); | void            scheduler(void) __attribute__((noreturn)); | ||||||
|  | void            sched(void); | ||||||
| void            sleep(void*, struct spinlock*); | void            sleep(void*, struct spinlock*); | ||||||
| void            userinit(void); | void            userinit(void); | ||||||
| int             wait(void); | int             wait(void); | ||||||
|  | @ -116,8 +125,8 @@ void            getcallerpcs(void*, uint*); | ||||||
| int             holding(struct spinlock*); | int             holding(struct spinlock*); | ||||||
| void            initlock(struct spinlock*, char*); | void            initlock(struct spinlock*, char*); | ||||||
| void            release(struct spinlock*); | void            release(struct spinlock*); | ||||||
| void            pushcli(); | void            pushcli(void); | ||||||
| void            popcli(); | void            popcli(void); | ||||||
| 
 | 
 | ||||||
| // string.c
 | // string.c
 | ||||||
| int             memcmp(const void*, const void*, uint); | int             memcmp(const void*, const void*, uint); | ||||||
|  | @ -151,20 +160,20 @@ void            uartintr(void); | ||||||
| void            uartputc(int); | void            uartputc(int); | ||||||
| 
 | 
 | ||||||
| // vm.c
 | // vm.c
 | ||||||
| void            pminit(void); | void            seginit(void); | ||||||
| void            ksegment(void); |  | ||||||
| void            kvmalloc(void); | void            kvmalloc(void); | ||||||
| void            vmenable(void); | void            vmenable(void); | ||||||
| pde_t*          setupkvm(void); | pde_t*          setupkvm(void); | ||||||
| char*           uva2ka(pde_t*, char*); | char*           uva2ka(pde_t*, char*); | ||||||
| int             allocuvm(pde_t*, char*, uint); | int             allocuvm(pde_t*, uint, uint); | ||||||
| int             deallocuvm(pde_t *pgdir, char *addr, uint sz); | int             deallocuvm(pde_t*, uint, uint); | ||||||
| void            freevm(pde_t*); | void            freevm(pde_t*); | ||||||
| void            inituvm(pde_t*, char*, char*, uint); | void            inituvm(pde_t*, char*, uint); | ||||||
| int             loaduvm(pde_t*, char*, struct inode *ip, uint, uint); | int             loaduvm(pde_t*, char*, struct inode*, uint, uint); | ||||||
| pde_t*          copyuvm(pde_t*,uint); | pde_t*          copyuvm(pde_t*, uint); | ||||||
| void            switchuvm(struct proc*); | void            switchuvm(struct proc*); | ||||||
| void            switchkvm(); | void            switchkvm(void); | ||||||
|  | int             copyout(pde_t*, uint, void*, uint); | ||||||
| 
 | 
 | ||||||
| // number of elements in fixed-size array
 | // number of elements in fixed-size array
 | ||||||
| #define NELEM(x) (sizeof(x)/sizeof((x)[0])) | #define NELEM(x) (sizeof(x)/sizeof((x)[0])) | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								exec.c
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								exec.c
									
										
									
									
									
								
							|  | @ -9,20 +9,18 @@ | ||||||
| int | int | ||||||
| exec(char *path, char **argv) | exec(char *path, char **argv) | ||||||
| { | { | ||||||
|   char *mem, *s, *last; |   char *s, *last; | ||||||
|   int i, argc, arglen, len, off; |   int i, off; | ||||||
|   uint sz, sp, spoffset, argp; |   uint argc, sz, sp, ustack[3+MAXARG+1]; | ||||||
|   struct elfhdr elf; |   struct elfhdr elf; | ||||||
|   struct inode *ip; |   struct inode *ip; | ||||||
|   struct proghdr ph; |   struct proghdr ph; | ||||||
|   pde_t *pgdir, *oldpgdir; |   pde_t *pgdir, *oldpgdir; | ||||||
| 
 | 
 | ||||||
|   pgdir = 0; |  | ||||||
|   sz = 0; |  | ||||||
| 
 |  | ||||||
|   if((ip = namei(path)) == 0) |   if((ip = namei(path)) == 0) | ||||||
|     return -1; |     return -1; | ||||||
|   ilock(ip); |   ilock(ip); | ||||||
|  |   pgdir = 0; | ||||||
| 
 | 
 | ||||||
|   // Check ELF header
 |   // Check ELF header
 | ||||||
|   if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) |   if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) | ||||||
|  | @ -30,10 +28,11 @@ exec(char *path, char **argv) | ||||||
|   if(elf.magic != ELF_MAGIC) |   if(elf.magic != ELF_MAGIC) | ||||||
|     goto bad; |     goto bad; | ||||||
| 
 | 
 | ||||||
|   if (!(pgdir = setupkvm())) |   if((pgdir = setupkvm()) == 0) | ||||||
|     goto bad; |     goto bad; | ||||||
| 
 | 
 | ||||||
|   // Load program into memory.
 |   // Load program into memory.
 | ||||||
|  |   sz = 0; | ||||||
|   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ |   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ | ||||||
|     if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) |     if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) | ||||||
|       goto bad; |       goto bad; | ||||||
|  | @ -41,49 +40,39 @@ exec(char *path, char **argv) | ||||||
|       continue; |       continue; | ||||||
|     if(ph.memsz < ph.filesz) |     if(ph.memsz < ph.filesz) | ||||||
|       goto bad; |       goto bad; | ||||||
|     if (!allocuvm(pgdir, (char *)ph.va, ph.memsz)) |     if((sz = allocuvm(pgdir, sz, ph.va + ph.memsz)) == 0) | ||||||
|       goto bad; |       goto bad; | ||||||
|     if(ph.va + ph.memsz > sz) |     if(loaduvm(pgdir, (char*)ph.va, ip, ph.offset, ph.filesz) < 0) | ||||||
|       sz = ph.va + ph.memsz; |  | ||||||
|     if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz)) |  | ||||||
|       goto bad; |       goto bad; | ||||||
|   } |   } | ||||||
|   iunlockput(ip); |   iunlockput(ip); | ||||||
|  |   ip = 0; | ||||||
| 
 | 
 | ||||||
|   // Allocate and initialize stack at sz
 |   // Allocate a one-page stack at the next page boundary
 | ||||||
|   sz = PGROUNDUP(sz); |   sz = PGROUNDUP(sz); | ||||||
|   sz += PGSIZE; // leave an invalid page
 |   if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0) | ||||||
|   if (!allocuvm(pgdir, (char *)sz, PGSIZE)) |  | ||||||
|     goto bad; |     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; |   sp = sz; | ||||||
|   argp = sz - arglen - 4*(argc+1); |   for(argc = 0; argv[argc]; argc++) { | ||||||
| 
 |     if(argc >= MAXARG) | ||||||
|   // Copy argv strings and pointers to stack.
 |       goto bad; | ||||||
|   *(uint*)(mem+argp-spoffset + 4*argc) = 0;  // argv[argc]
 |     sp -= strlen(argv[argc]) + 1; | ||||||
|   for(i=argc-1; i>=0; i--){ |     sp &= ~3; | ||||||
|     len = strlen(argv[i]) + 1; |     if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) | ||||||
|     sp -= len; |       goto bad; | ||||||
|     memmove(mem+sp-spoffset, argv[i], len); |     ustack[3+argc] = sp; | ||||||
|     *(uint*)(mem+argp-spoffset + 4*i) = sp;  // argv[i]
 |  | ||||||
|   } |   } | ||||||
|  |   ustack[3+argc] = 0; | ||||||
| 
 | 
 | ||||||
|   // Stack frame for main(argc, argv), below arguments.
 |   ustack[0] = 0xffffffff;  // fake return PC
 | ||||||
|   sp = argp; |   ustack[1] = argc; | ||||||
|   sp -= 4; |   ustack[2] = sp - (argc+1)*4;  // argv pointer
 | ||||||
|   *(uint*)(mem+sp-spoffset) = argp; | 
 | ||||||
|   sp -= 4; |   sp -= (3+argc+1) * 4; | ||||||
|   *(uint*)(mem+sp-spoffset) = argc; |   if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) | ||||||
|   sp -= 4; |     goto bad; | ||||||
|   *(uint*)(mem+sp-spoffset) = 0xffffffff;   // fake return pc
 |  | ||||||
| 
 | 
 | ||||||
|   // Save program name for debugging.
 |   // Save program name for debugging.
 | ||||||
|   for(last=s=path; *s; s++) |   for(last=s=path; *s; s++) | ||||||
|  | @ -97,15 +86,15 @@ exec(char *path, char **argv) | ||||||
|   proc->sz = sz; |   proc->sz = sz; | ||||||
|   proc->tf->eip = elf.entry;  // main
 |   proc->tf->eip = elf.entry;  // main
 | ||||||
|   proc->tf->esp = sp; |   proc->tf->esp = sp; | ||||||
| 
 |   switchuvm(proc); | ||||||
|   switchuvm(proc);  |  | ||||||
| 
 |  | ||||||
|   freevm(oldpgdir); |   freevm(oldpgdir); | ||||||
| 
 | 
 | ||||||
|   return 0; |   return 0; | ||||||
| 
 | 
 | ||||||
|  bad: |  bad: | ||||||
|   if (pgdir) freevm(pgdir); |   if(pgdir) | ||||||
|   iunlockput(ip); |     freevm(pgdir); | ||||||
|  |   if(ip) | ||||||
|  |     iunlockput(ip); | ||||||
|   return -1; |   return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								fs.c
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								fs.c
									
										
									
									
									
								
							|  | @ -25,7 +25,7 @@ | ||||||
| static void itrunc(struct inode*); | static void itrunc(struct inode*); | ||||||
| 
 | 
 | ||||||
| // Read the super block.
 | // Read the super block.
 | ||||||
| static void | void | ||||||
| readsb(int dev, struct superblock *sb) | readsb(int dev, struct superblock *sb) | ||||||
| { | { | ||||||
|   struct buf *bp; |   struct buf *bp; | ||||||
|  | @ -61,11 +61,11 @@ balloc(uint dev) | ||||||
|   readsb(dev, &sb); |   readsb(dev, &sb); | ||||||
|   for(b = 0; b < sb.size; b += BPB){ |   for(b = 0; b < sb.size; b += BPB){ | ||||||
|     bp = bread(dev, BBLOCK(b, sb.ninodes)); |     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); |       m = 1 << (bi % 8); | ||||||
|       if((bp->data[bi/8] & m) == 0){  // Is block free?
 |       if((bp->data[bi/8] & m) == 0){  // Is block free?
 | ||||||
|         bp->data[bi/8] |= m;  // Mark block in use on disk.
 |         bp->data[bi/8] |= m;  // Mark block in use on disk.
 | ||||||
|         bwrite(bp); |         log_write(bp); | ||||||
|         brelse(bp); |         brelse(bp); | ||||||
|         return b + bi; |         return b + bi; | ||||||
|       } |       } | ||||||
|  | @ -92,7 +92,7 @@ bfree(int dev, uint b) | ||||||
|   if((bp->data[bi/8] & m) == 0) |   if((bp->data[bi/8] & m) == 0) | ||||||
|     panic("freeing free block"); |     panic("freeing free block"); | ||||||
|   bp->data[bi/8] &= ~m;  // Mark block free on disk.
 |   bp->data[bi/8] &= ~m;  // Mark block free on disk.
 | ||||||
|   bwrite(bp); |   log_write(bp); | ||||||
|   brelse(bp); |   brelse(bp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -159,7 +159,7 @@ ialloc(uint dev, short type) | ||||||
|     if(dip->type == 0){  // a free inode
 |     if(dip->type == 0){  // a free inode
 | ||||||
|       memset(dip, 0, sizeof(*dip)); |       memset(dip, 0, sizeof(*dip)); | ||||||
|       dip->type = type; |       dip->type = type; | ||||||
|       bwrite(bp);   // mark it allocated on the disk
 |       log_write(bp);   // mark it allocated on the disk
 | ||||||
|       brelse(bp); |       brelse(bp); | ||||||
|       return iget(dev, inum); |       return iget(dev, inum); | ||||||
|     } |     } | ||||||
|  | @ -183,7 +183,7 @@ iupdate(struct inode *ip) | ||||||
|   dip->nlink = ip->nlink; |   dip->nlink = ip->nlink; | ||||||
|   dip->size = ip->size; |   dip->size = ip->size; | ||||||
|   memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); |   memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); | ||||||
|   bwrite(bp); |   log_write(bp); | ||||||
|   brelse(bp); |   brelse(bp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -339,7 +339,7 @@ bmap(struct inode *ip, uint bn) | ||||||
|     a = (uint*)bp->data; |     a = (uint*)bp->data; | ||||||
|     if((addr = a[bn]) == 0){ |     if((addr = a[bn]) == 0){ | ||||||
|       a[bn] = addr = balloc(ip->dev); |       a[bn] = addr = balloc(ip->dev); | ||||||
|       bwrite(bp); |       log_write(bp); | ||||||
|     } |     } | ||||||
|     brelse(bp); |     brelse(bp); | ||||||
|     return addr; |     return addr; | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								fs.h
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								fs.h
									
										
									
									
									
								
							|  | @ -13,6 +13,7 @@ struct superblock { | ||||||
|   uint size;         // Size of file system image (blocks)
 |   uint size;         // Size of file system image (blocks)
 | ||||||
|   uint nblocks;      // Number of data blocks
 |   uint nblocks;      // Number of data blocks
 | ||||||
|   uint ninodes;      // Number of inodes.
 |   uint ninodes;      // Number of inodes.
 | ||||||
|  |   uint nlog;         // Number of log blocks
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define NDIRECT 12 | #define NDIRECT 12 | ||||||
|  | @ -41,7 +42,6 @@ struct dinode { | ||||||
| // Block containing bit for block b
 | // Block containing bit for block b
 | ||||||
| #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) | #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) | ||||||
| 
 | 
 | ||||||
| // PAGEBREAK: 10
 |  | ||||||
| // Directory is a file containing a sequence of dirent structures.
 | // Directory is a file containing a sequence of dirent structures.
 | ||||||
| #define DIRSIZ 14 | #define DIRSIZ 14 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								ide.c
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								ide.c
									
										
									
									
									
								
							|  | @ -96,7 +96,7 @@ ideintr(void) | ||||||
|   acquire(&idelock); |   acquire(&idelock); | ||||||
|   if((b = idequeue) == 0){ |   if((b = idequeue) == 0){ | ||||||
|     release(&idelock); |     release(&idelock); | ||||||
|     cprintf("Spurious IDE interrupt.\n"); |     // cprintf("spurious IDE interrupt\n");
 | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   idequeue = b->qnext; |   idequeue = b->qnext; | ||||||
|  | @ -131,7 +131,7 @@ iderw(struct buf *b) | ||||||
|   if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) |   if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) | ||||||
|     panic("iderw: nothing to do"); |     panic("iderw: nothing to do"); | ||||||
|   if(b->dev != 0 && !havedisk1) |   if(b->dev != 0 && !havedisk1) | ||||||
|     panic("idrw: ide disk 1 not present"); |     panic("iderw: ide disk 1 not present"); | ||||||
| 
 | 
 | ||||||
|   acquire(&idelock); |   acquire(&idelock); | ||||||
| 
 | 
 | ||||||
|  | @ -147,7 +147,7 @@ iderw(struct buf *b) | ||||||
|    |    | ||||||
|   // Wait for request to finish.
 |   // Wait for request to finish.
 | ||||||
|   // Assuming will not sleep too long: ignore proc->killed.
 |   // Assuming will not sleep too long: ignore proc->killed.
 | ||||||
|   while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) { |   while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ | ||||||
|     sleep(b, &idelock); |     sleep(b, &idelock); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,9 +3,12 @@ | ||||||
| #include "syscall.h" | #include "syscall.h" | ||||||
| #include "traps.h" | #include "traps.h" | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # exec(init, argv) | # exec(init, argv) | ||||||
| .globl start
 | .globl start
 | ||||||
| start: | start: | ||||||
|  |   movl $SYS_init, %eax | ||||||
|  |   int $T_SYSCALL | ||||||
|   pushl $argv |   pushl $argv | ||||||
|   pushl $init |   pushl $init | ||||||
|   pushl $0  // where caller pc would be |   pushl $0  // where caller pc would be | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								kalloc.c
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								kalloc.c
									
										
									
									
									
								
							|  | @ -17,17 +17,21 @@ struct { | ||||||
|   struct run *freelist; |   struct run *freelist; | ||||||
| } kmem; | } kmem; | ||||||
| 
 | 
 | ||||||
|  | extern char end[]; // first address after kernel loaded from ELF file
 | ||||||
|  | 
 | ||||||
| // Initialize free list of physical pages.
 | // Initialize free list of physical pages.
 | ||||||
| void | void | ||||||
| kinit(char *p, uint len) | kinit(void) | ||||||
| { | { | ||||||
|  |   char *p; | ||||||
|  | 
 | ||||||
|   initlock(&kmem.lock, "kmem"); |   initlock(&kmem.lock, "kmem"); | ||||||
|   char *p1 = (char*)PGROUNDUP((uint)p); |   p = (char*)PGROUNDUP((uint)end); | ||||||
|   char *p2 = PGROUNDDOWN(p + len); |   for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE) | ||||||
|   for( ; p1 < p2; p1 += 4096) |     kfree(p); | ||||||
|     kfree(p1); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | //PAGEBREAK: 21
 | ||||||
| // Free the page of physical memory pointed at by v,
 | // Free the page of physical memory pointed at by v,
 | ||||||
| // which normally should have been returned by a
 | // which normally should have been returned by a
 | ||||||
| // call to kalloc().  (The exception is when
 | // call to kalloc().  (The exception is when
 | ||||||
|  | @ -37,14 +41,14 @@ kfree(char *v) | ||||||
| { | { | ||||||
|   struct run *r; |   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"); |     panic("kfree"); | ||||||
| 
 | 
 | ||||||
|   // Fill with junk to catch dangling refs.
 |   // Fill with junk to catch dangling refs.
 | ||||||
|   memset(v, 1, PGSIZE); |   memset(v, 1, PGSIZE); | ||||||
| 
 | 
 | ||||||
|   acquire(&kmem.lock); |   acquire(&kmem.lock); | ||||||
|   r = (struct run *) v; |   r = (struct run*)v; | ||||||
|   r->next = kmem.freelist; |   r->next = kmem.freelist; | ||||||
|   kmem.freelist = r; |   kmem.freelist = r; | ||||||
|   release(&kmem.lock); |   release(&kmem.lock); | ||||||
|  | @ -54,7 +58,7 @@ kfree(char *v) | ||||||
| // Returns a pointer that the kernel can use.
 | // Returns a pointer that the kernel can use.
 | ||||||
| // Returns 0 if the memory cannot be allocated.
 | // Returns 0 if the memory cannot be allocated.
 | ||||||
| char* | char* | ||||||
| kalloc() | kalloc(void) | ||||||
| { | { | ||||||
|   struct run *r; |   struct run *r; | ||||||
| 
 | 
 | ||||||
|  | @ -63,6 +67,6 @@ kalloc() | ||||||
|   if(r) |   if(r) | ||||||
|     kmem.freelist = r->next; |     kmem.freelist = r->next; | ||||||
|   release(&kmem.lock); |   release(&kmem.lock); | ||||||
|   return (char*) r; |   return (char*)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++; | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								main.c
									
										
									
									
									
								
							
							
						
						
									
										59
									
								
								main.c
									
										
									
									
									
								
							|  | @ -7,40 +7,45 @@ | ||||||
| 
 | 
 | ||||||
| static void bootothers(void); | static void bootothers(void); | ||||||
| static void mpmain(void); | static void mpmain(void); | ||||||
| void jkstack(void)  __attribute__((noreturn)); | void jmpkstack(void)  __attribute__((noreturn)); | ||||||
| void mainc(void); | void mainc(void); | ||||||
| 
 | 
 | ||||||
| // Bootstrap processor starts running C code here.
 | // 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 | int | ||||||
| main(void) | main(void) | ||||||
| { | { | ||||||
|   mpinit();        // collect info about this machine
 |   mpinit();        // collect info about this machine
 | ||||||
|   lapicinit(mpbcpu()); |   lapicinit(mpbcpu()); | ||||||
|   ksegment();      // set up segments
 |   seginit();       // set up segments
 | ||||||
|   picinit();       // interrupt controller
 |   kinit();         // initialize memory allocator
 | ||||||
|   ioapicinit();    // another interrupt controller
 |   jmpkstack();     // call mainc() on a properly-allocated stack 
 | ||||||
|   consoleinit();   // I/O devices & their interrupts
 |  | ||||||
|   uartinit();      // serial port
 |  | ||||||
|   pminit();        // discover how much memory there is
 |  | ||||||
|   jkstack();       // call mainc() on a properly-allocated stack 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| jkstack(void) | jmpkstack(void) | ||||||
| { | { | ||||||
|   char *kstack = kalloc(); |   char *kstack, *top; | ||||||
|   if (!kstack) |    | ||||||
|     panic("jkstack\n"); |   kstack = kalloc(); | ||||||
|   char *top = kstack + PGSIZE; |   if(kstack == 0) | ||||||
|   asm volatile("movl %0,%%esp" : : "r" (top)); |     panic("jmpkstack kalloc"); | ||||||
|   asm volatile("call mainc"); |   top = kstack + PGSIZE; | ||||||
|   panic("jkstack"); |   asm volatile("movl %0,%%esp; call mainc" : : "r" (top)); | ||||||
|  |   panic("jmpkstack"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Set up hardware and software.
 | ||||||
|  | // Runs only on the boostrap processor.
 | ||||||
| void | void | ||||||
| mainc(void) | mainc(void) | ||||||
| { | { | ||||||
|   cprintf("\ncpu%d: starting xv6\n\n", cpu->id); |   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
 |   kvmalloc();      // initialize the kernel page table
 | ||||||
|   pinit();         // process table
 |   pinit();         // process table
 | ||||||
|   tvinit();        // trap vectors
 |   tvinit();        // trap vectors
 | ||||||
|  | @ -63,17 +68,18 @@ mainc(void) | ||||||
| static void | static void | ||||||
| mpmain(void) | mpmain(void) | ||||||
| { | { | ||||||
|   if(cpunum() != mpbcpu()) { |   if(cpunum() != mpbcpu()){ | ||||||
|     ksegment(); |     seginit(); | ||||||
|     lapicinit(cpunum()); |     lapicinit(cpunum()); | ||||||
|   } |   } | ||||||
|   vmenable();        // turn on paging
 |   vmenable();        // turn on paging
 | ||||||
|   cprintf("cpu%d: starting\n", cpu->id); |   cprintf("cpu%d: starting\n", cpu->id); | ||||||
|   idtinit();       // load idt register
 |   idtinit();       // load idt register
 | ||||||
|   xchg(&cpu->booted, 1); |   xchg(&cpu->booted, 1); // tell bootothers() we're up
 | ||||||
|   scheduler();     // start running processes
 |   scheduler();     // start running processes
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Start the non-boot processors.
 | ||||||
| static void | static void | ||||||
| bootothers(void) | bootothers(void) | ||||||
| { | { | ||||||
|  | @ -82,19 +88,23 @@ bootothers(void) | ||||||
|   struct cpu *c; |   struct cpu *c; | ||||||
|   char *stack; |   char *stack; | ||||||
| 
 | 
 | ||||||
|   // Write bootstrap code to unused memory at 0x7000.  The linker has
 |   // Write bootstrap code to unused memory at 0x7000.
 | ||||||
|   // placed the start of bootother.S there.
 |   // The linker has placed the image of bootother.S in
 | ||||||
|   code = (uchar *) 0x7000; |   // _binary_bootother_start.
 | ||||||
|  |   code = (uchar*)0x7000; | ||||||
|   memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); |   memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); | ||||||
| 
 | 
 | ||||||
|   for(c = cpus; c < cpus+ncpu; c++){ |   for(c = cpus; c < cpus+ncpu; c++){ | ||||||
|     if(c == cpus+cpunum())  // We've started already.
 |     if(c == cpus+cpunum())  // We've started already.
 | ||||||
|       continue; |       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(); |     stack = kalloc(); | ||||||
|     *(void**)(code-4) = stack + KSTACKSIZE; |     *(void**)(code-4) = stack + KSTACKSIZE; | ||||||
|     *(void**)(code-8) = mpmain; |     *(void**)(code-8) = mpmain; | ||||||
|  | 
 | ||||||
|     lapicstartap(c->id, (uint)code); |     lapicstartap(c->id, (uint)code); | ||||||
| 
 | 
 | ||||||
|     // Wait for cpu to finish mpmain()
 |     // 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; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								mkfs.c
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								mkfs.c
									
										
									
									
									
								
							|  | @ -4,11 +4,15 @@ | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #define stat xv6_stat  // avoid clash with host struct stat
 | ||||||
| #include "types.h" | #include "types.h" | ||||||
| #include "fs.h" | #include "fs.h" | ||||||
| #include "stat.h" | #include "stat.h" | ||||||
|  | #include "param.h" | ||||||
| 
 | 
 | ||||||
| int nblocks = 995; | int nblocks = 985; | ||||||
|  | int nlog = LOGSIZE; | ||||||
| int ninodes = 200; | int ninodes = 200; | ||||||
| int size = 1024; | int size = 1024; | ||||||
| 
 | 
 | ||||||
|  | @ -33,7 +37,7 @@ ushort | ||||||
| xshort(ushort x) | xshort(ushort x) | ||||||
| { | { | ||||||
|   ushort y; |   ushort y; | ||||||
|   uchar *a = (uchar*) &y; |   uchar *a = (uchar*)&y; | ||||||
|   a[0] = x; |   a[0] = x; | ||||||
|   a[1] = x >> 8; |   a[1] = x >> 8; | ||||||
|   return y; |   return y; | ||||||
|  | @ -43,7 +47,7 @@ uint | ||||||
| xint(uint x) | xint(uint x) | ||||||
| { | { | ||||||
|   uint y; |   uint y; | ||||||
|   uchar *a = (uchar*) &y; |   uchar *a = (uchar*)&y; | ||||||
|   a[0] = x; |   a[0] = x; | ||||||
|   a[1] = x >> 8; |   a[1] = x >> 8; | ||||||
|   a[2] = x >> 16; |   a[2] = x >> 16; | ||||||
|  | @ -77,20 +81,23 @@ main(int argc, char *argv[]) | ||||||
|   sb.size = xint(size); |   sb.size = xint(size); | ||||||
|   sb.nblocks = xint(nblocks); // so whole disk is size sectors
 |   sb.nblocks = xint(nblocks); // so whole disk is size sectors
 | ||||||
|   sb.ninodes = xint(ninodes); |   sb.ninodes = xint(ninodes); | ||||||
|  |   sb.nlog = xint(nlog); | ||||||
| 
 | 
 | ||||||
|   bitblocks = size/(512*8) + 1; |   bitblocks = size/(512*8) + 1; | ||||||
|   usedblocks = ninodes / IPB + 3 + bitblocks; |   usedblocks = ninodes / IPB + 3 + bitblocks; | ||||||
|   freeblock = usedblocks; |   freeblock = usedblocks; | ||||||
| 
 | 
 | ||||||
|   printf("used %d (bit %d ninode %lu) free %u total %d\n", usedblocks, |   printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks, | ||||||
|          bitblocks, ninodes/IPB + 1, freeblock, nblocks+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(i, zeroes); | ||||||
| 
 | 
 | ||||||
|   wsect(1, &sb); |   memset(buf, 0, sizeof(buf)); | ||||||
|  |   memmove(buf, &sb, sizeof(sb)); | ||||||
|  |   wsect(1, buf); | ||||||
| 
 | 
 | ||||||
|   rootino = ialloc(T_DIR); |   rootino = ialloc(T_DIR); | ||||||
|   assert(rootino == ROOTINO); |   assert(rootino == ROOTINO); | ||||||
|  | @ -173,7 +180,7 @@ winode(uint inum, struct dinode *ip) | ||||||
| 
 | 
 | ||||||
|   bn = i2b(inum); |   bn = i2b(inum); | ||||||
|   rsect(bn, buf); |   rsect(bn, buf); | ||||||
|   dip = ((struct dinode*) buf) + (inum % IPB); |   dip = ((struct dinode*)buf) + (inum % IPB); | ||||||
|   *dip = *ip; |   *dip = *ip; | ||||||
|   wsect(bn, buf); |   wsect(bn, buf); | ||||||
| } | } | ||||||
|  | @ -187,7 +194,7 @@ rinode(uint inum, struct dinode *ip) | ||||||
| 
 | 
 | ||||||
|   bn = i2b(inum); |   bn = i2b(inum); | ||||||
|   rsect(bn, buf); |   rsect(bn, buf); | ||||||
|   dip = ((struct dinode*) buf) + (inum % IPB); |   dip = ((struct dinode*)buf) + (inum % IPB); | ||||||
|   *ip = *dip; |   *ip = *dip; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -225,12 +232,12 @@ balloc(int used) | ||||||
|   int i; |   int i; | ||||||
| 
 | 
 | ||||||
|   printf("balloc: first %d blocks have been allocated\n", used); |   printf("balloc: first %d blocks have been allocated\n", used); | ||||||
|   assert(used < 512); |   assert(used < 512*8); | ||||||
|   bzero(buf, 512); |   bzero(buf, 512); | ||||||
|   for(i = 0; i < used; i++) { |   for(i = 0; i < used; i++){ | ||||||
|     buf[i/8] = buf[i/8] | (0x1 << (i%8)); |     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); |   wsect(ninodes / IPB + 3, buf); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -239,7 +246,7 @@ balloc(int used) | ||||||
| void | void | ||||||
| iappend(uint inum, void *xp, int n) | iappend(uint inum, void *xp, int n) | ||||||
| { | { | ||||||
|   char *p = (char*) xp; |   char *p = (char*)xp; | ||||||
|   uint fbn, off, n1; |   uint fbn, off, n1; | ||||||
|   struct dinode din; |   struct dinode din; | ||||||
|   char buf[512]; |   char buf[512]; | ||||||
|  | @ -252,24 +259,24 @@ iappend(uint inum, void *xp, int n) | ||||||
|   while(n > 0){ |   while(n > 0){ | ||||||
|     fbn = off / 512; |     fbn = off / 512; | ||||||
|     assert(fbn < MAXFILE); |     assert(fbn < MAXFILE); | ||||||
|     if(fbn < NDIRECT) { |     if(fbn < NDIRECT){ | ||||||
|       if(xint(din.addrs[fbn]) == 0) { |       if(xint(din.addrs[fbn]) == 0){ | ||||||
|         din.addrs[fbn] = xint(freeblock++); |         din.addrs[fbn] = xint(freeblock++); | ||||||
|         usedblocks++; |         usedblocks++; | ||||||
|       } |       } | ||||||
|       x = xint(din.addrs[fbn]); |       x = xint(din.addrs[fbn]); | ||||||
|     } else { |     } else { | ||||||
|       if(xint(din.addrs[NDIRECT]) == 0) { |       if(xint(din.addrs[NDIRECT]) == 0){ | ||||||
|         // printf("allocate indirect block\n");
 |         // printf("allocate indirect block\n");
 | ||||||
|         din.addrs[NDIRECT] = xint(freeblock++); |         din.addrs[NDIRECT] = xint(freeblock++); | ||||||
|         usedblocks++; |         usedblocks++; | ||||||
|       } |       } | ||||||
|       // printf("read indirect block\n");
 |       // printf("read indirect block\n");
 | ||||||
|       rsect(xint(din.addrs[NDIRECT]), (char*) indirect); |       rsect(xint(din.addrs[NDIRECT]), (char*)indirect); | ||||||
|       if(indirect[fbn - NDIRECT] == 0) { |       if(indirect[fbn - NDIRECT] == 0){ | ||||||
|         indirect[fbn - NDIRECT] = xint(freeblock++); |         indirect[fbn - NDIRECT] = xint(freeblock++); | ||||||
|         usedblocks++; |         usedblocks++; | ||||||
|         wsect(xint(din.addrs[NDIRECT]), (char*) indirect); |         wsect(xint(din.addrs[NDIRECT]), (char*)indirect); | ||||||
|       } |       } | ||||||
|       x = xint(indirect[fbn-NDIRECT]); |       x = xint(indirect[fbn-NDIRECT]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										47
									
								
								mmu.h
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								mmu.h
									
										
									
									
									
								
							|  | @ -24,6 +24,20 @@ | ||||||
| #define FL_VIP          0x00100000      // Virtual Interrupt Pending
 | #define FL_VIP          0x00100000      // Virtual Interrupt Pending
 | ||||||
| #define FL_ID           0x00200000      // ID flag
 | #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
 | // Segment Descriptor
 | ||||||
| struct segdesc { | struct segdesc { | ||||||
|   uint lim_15_0 : 16;  // Low bits of segment limit
 |   uint lim_15_0 : 16;  // Low bits of segment limit
 | ||||||
|  | @ -46,7 +60,6 @@ struct segdesc { | ||||||
| { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff,      \ | { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff,      \ | ||||||
|   ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,       \ |   ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,       \ | ||||||
|   (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } |   (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } | ||||||
| 
 |  | ||||||
| #define SEG16(type, base, lim, dpl) (struct segdesc)  \ | #define SEG16(type, base, lim, dpl) (struct segdesc)  \ | ||||||
| { (lim) & 0xffff, (uint)(base) & 0xffff,              \ | { (lim) & 0xffff, (uint)(base) & 0xffff,              \ | ||||||
|   ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,       \ |   ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,       \ | ||||||
|  | @ -62,8 +75,6 @@ struct segdesc { | ||||||
| #define STA_R       0x2     // Readable (executable segments)
 | #define STA_R       0x2     // Readable (executable segments)
 | ||||||
| #define STA_A       0x1     // Accessed
 | #define STA_A       0x1     // Accessed
 | ||||||
| 
 | 
 | ||||||
| // 
 |  | ||||||
| 
 |  | ||||||
| // System segment type bits
 | // System segment type bits
 | ||||||
| #define STS_T16A    0x1     // Available 16-bit TSS
 | #define STS_T16A    0x1     // Available 16-bit TSS
 | ||||||
| #define STS_LDT     0x2     // Local Descriptor Table
 | #define STS_LDT     0x2     // Local Descriptor Table
 | ||||||
|  | @ -78,7 +89,6 @@ struct segdesc { | ||||||
| #define STS_IG32    0xE     // 32-bit Interrupt Gate
 | #define STS_IG32    0xE     // 32-bit Interrupt Gate
 | ||||||
| #define STS_TG32    0xF     // 32-bit Trap Gate
 | #define STS_TG32    0xF     // 32-bit Trap Gate
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // A linear address 'la' has a three-part structure as follows:
 | // A linear address 'la' has a three-part structure as follows:
 | ||||||
| //
 | //
 | ||||||
| // +--------10------+-------10-------+---------12----------+
 | // +--------10------+-------10-------+---------12----------+
 | ||||||
|  | @ -88,18 +98,18 @@ struct segdesc { | ||||||
| //  \--- PDX(la) --/ \--- PTX(la) --/ 
 | //  \--- PDX(la) --/ \--- PTX(la) --/ 
 | ||||||
| 
 | 
 | ||||||
| // page directory index
 | // page directory index
 | ||||||
| #define PDX(la)		((((uint) (la)) >> PDXSHIFT) & 0x3FF) | #define PDX(la)		(((uint)(la) >> PDXSHIFT) & 0x3FF) | ||||||
| 
 | 
 | ||||||
| // page table index
 | // page table index
 | ||||||
| #define PTX(la)		((((uint) (la)) >> PTXSHIFT) & 0x3FF) | #define PTX(la)		(((uint)(la) >> PTXSHIFT) & 0x3FF) | ||||||
| 
 | 
 | ||||||
| // construct linear address from indexes and offset
 | // construct linear address from indexes and offset
 | ||||||
| #define PGADDR(d, t, o)	((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) | #define PGADDR(d, t, o)	((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) | ||||||
| 
 | 
 | ||||||
| // turn a kernel linear address into a physical address.
 | // turn a kernel linear address into a physical address.
 | ||||||
| // all of the kernel data structures have linear and
 | // all of the kernel data structures have linear and
 | ||||||
| // physical addresses that are equal.
 | // physical addresses that are equal.
 | ||||||
| #define PADDR(a)       ((uint) a) | #define PADDR(a)       ((uint)(a)) | ||||||
| 
 | 
 | ||||||
| // Page directory and page table constants.
 | // Page directory and page table constants.
 | ||||||
| #define NPDENTRIES	1024		// page directory entries per page directory
 | #define NPDENTRIES	1024		// page directory entries per page directory
 | ||||||
|  | @ -126,25 +136,10 @@ struct segdesc { | ||||||
| #define PTE_MBZ		0x180	// Bits must be zero
 | #define PTE_MBZ		0x180	// Bits must be zero
 | ||||||
| 
 | 
 | ||||||
| // Address in page table or page directory entry
 | // Address in page table or page directory entry
 | ||||||
| #define PTE_ADDR(pte)	((uint) (pte) & ~0xFFF) | #define PTE_ADDR(pte)	((uint)(pte) & ~0xFFF) | ||||||
| 
 | 
 | ||||||
| typedef uint pte_t; | 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
 | // Task state segment format
 | ||||||
| struct taskstate { | struct taskstate { | ||||||
|   uint link;         // Old ts selector
 |   uint link;         // Old ts selector
 | ||||||
|  | @ -210,7 +205,7 @@ struct gatedesc { | ||||||
| //        this interrupt/trap gate explicitly using an int instruction.
 | //        this interrupt/trap gate explicitly using an int instruction.
 | ||||||
| #define SETGATE(gate, istrap, sel, off, d)                \ | #define SETGATE(gate, istrap, sel, off, d)                \ | ||||||
| {                                                         \ | {                                                         \ | ||||||
|   (gate).off_15_0 = (uint) (off) & 0xffff;                \ |   (gate).off_15_0 = (uint)(off) & 0xffff;                \ | ||||||
|   (gate).cs = (sel);                                      \ |   (gate).cs = (sel);                                      \ | ||||||
|   (gate).args = 0;                                        \ |   (gate).args = 0;                                        \ | ||||||
|   (gate).rsv1 = 0;                                        \ |   (gate).rsv1 = 0;                                        \ | ||||||
|  | @ -218,6 +213,6 @@ struct gatedesc { | ||||||
|   (gate).s = 0;                                           \ |   (gate).s = 0;                                           \ | ||||||
|   (gate).dpl = (d);                                       \ |   (gate).dpl = (d);                                       \ | ||||||
|   (gate).p = 1;                                           \ |   (gate).p = 1;                                           \ | ||||||
|   (gate).off_31_16 = (uint) (off) >> 16;                  \ |   (gate).off_31_16 = (uint)(off) >> 16;                  \ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								mp.c
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								mp.c
									
										
									
									
									
								
							|  | @ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len) | ||||||
| { | { | ||||||
|   uchar *e, *p; |   uchar *e, *p; | ||||||
| 
 | 
 | ||||||
|   cprintf("mpsearch1 0x%x %d\n", addr, len); |  | ||||||
|   e = addr+len; |   e = addr+len; | ||||||
|   for(p = addr; p < e; p += sizeof(struct mp)) |   for(p = addr; p < e; p += sizeof(struct mp)) | ||||||
|     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) |     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) | ||||||
|  | @ -113,9 +112,9 @@ mpinit(void) | ||||||
|     switch(*p){ |     switch(*p){ | ||||||
|     case MPPROC: |     case MPPROC: | ||||||
|       proc = (struct mpproc*)p; |       proc = (struct mpproc*)p; | ||||||
|       if(ncpu != proc->apicid) { |       if(ncpu != proc->apicid){ | ||||||
|         cprintf("mpinit: ncpu=%d apicpid=%d", ncpu, proc->apicid); |         cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid); | ||||||
|         panic("mpinit"); |         ismp = 0; | ||||||
|       } |       } | ||||||
|       if(proc->flags & MPBOOT) |       if(proc->flags & MPBOOT) | ||||||
|         bcpu = &cpus[ncpu]; |         bcpu = &cpus[ncpu]; | ||||||
|  | @ -135,9 +134,17 @@ mpinit(void) | ||||||
|       continue; |       continue; | ||||||
|     default: |     default: | ||||||
|       cprintf("mpinit: unknown config type %x\n", *p); |       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){ |   if(mp->imcrp){ | ||||||
|     // Bochs doesn't support IMCR, so this doesn't run on Bochs.
 |     // Bochs doesn't support IMCR, so this doesn't run on Bochs.
 | ||||||
|     // But it would on real hardware.
 |     // 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 NINODE       50  // maximum number of active i-nodes
 | ||||||
| #define NDEV         10  // maximum major device number
 | #define NDEV         10  // maximum major device number
 | ||||||
| #define ROOTDEV       1  // device number of file system root disk
 | #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 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) |   if(irqmask != 0xFFFF) | ||||||
|     picsetmask(irqmask); |     picsetmask(irqmask); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // Blank page.
 |  | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								pipe.c
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								pipe.c
									
										
									
									
									
								
							|  | @ -66,7 +66,7 @@ pipeclose(struct pipe *p, int writable) | ||||||
|     p->readopen = 0; |     p->readopen = 0; | ||||||
|     wakeup(&p->nwrite); |     wakeup(&p->nwrite); | ||||||
|   } |   } | ||||||
|   if(p->readopen == 0 && p->writeopen == 0) { |   if(p->readopen == 0 && p->writeopen == 0){ | ||||||
|     release(&p->lock); |     release(&p->lock); | ||||||
|     kfree((char*)p); |     kfree((char*)p); | ||||||
|   } else |   } else | ||||||
|  | @ -81,7 +81,7 @@ pipewrite(struct pipe *p, char *addr, int n) | ||||||
| 
 | 
 | ||||||
|   acquire(&p->lock); |   acquire(&p->lock); | ||||||
|   for(i = 0; i < n; i++){ |   for(i = 0; i < n; i++){ | ||||||
|     while(p->nwrite == p->nread + PIPESIZE) {  //DOC: pipewrite-full
 |     while(p->nwrite == p->nread + PIPESIZE){  //DOC: pipewrite-full
 | ||||||
|       if(p->readopen == 0 || proc->killed){ |       if(p->readopen == 0 || proc->killed){ | ||||||
|         release(&p->lock); |         release(&p->lock); | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
							
								
								
									
										259
									
								
								proc.c
									
										
									
									
									
								
							
							
						
						
									
										259
									
								
								proc.c
									
										
									
									
									
								
							|  | @ -17,53 +17,18 @@ int nextpid = 1; | ||||||
| extern void forkret(void); | extern void forkret(void); | ||||||
| extern void trapret(void); | extern void trapret(void); | ||||||
| 
 | 
 | ||||||
|  | static void wakeup1(void *chan); | ||||||
|  | 
 | ||||||
| void | void | ||||||
| pinit(void) | pinit(void) | ||||||
| { | { | ||||||
|   initlock(&ptable.lock, "ptable"); |   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
 | //PAGEBREAK: 32
 | ||||||
| // Look in the process table for an UNUSED proc.
 | // 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.
 | // Otherwise return 0.
 | ||||||
| static struct proc* | static struct proc* | ||||||
| allocproc(void) | allocproc(void) | ||||||
|  | @ -95,7 +60,7 @@ found: | ||||||
|   p->tf = (struct trapframe*)sp; |   p->tf = (struct trapframe*)sp; | ||||||
|    |    | ||||||
|   // Set up new context to start executing at forkret,
 |   // Set up new context to start executing at forkret,
 | ||||||
|   // which returns to trapret (see below).
 |   // which returns to trapret.
 | ||||||
|   sp -= 4; |   sp -= 4; | ||||||
|   *(uint*)sp = (uint)trapret; |   *(uint*)sp = (uint)trapret; | ||||||
| 
 | 
 | ||||||
|  | @ -103,6 +68,7 @@ found: | ||||||
|   p->context = (struct context*)sp; |   p->context = (struct context*)sp; | ||||||
|   memset(p->context, 0, sizeof *p->context); |   memset(p->context, 0, sizeof *p->context); | ||||||
|   p->context->eip = (uint)forkret; |   p->context->eip = (uint)forkret; | ||||||
|  | 
 | ||||||
|   return p; |   return p; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -116,12 +82,10 @@ userinit(void) | ||||||
|    |    | ||||||
|   p = allocproc(); |   p = allocproc(); | ||||||
|   initproc = p; |   initproc = p; | ||||||
|   if (!(p->pgdir = setupkvm())) |   if((p->pgdir = setupkvm()) == 0) | ||||||
|     panic("userinit: out of memory?"); |     panic("userinit: out of memory?"); | ||||||
|   if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size)) |   inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); | ||||||
|     panic("userinit: out of memory?"); |   p->sz = PGSIZE; | ||||||
|   inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size); |  | ||||||
|   p->sz = PGROUNDUP((int)_binary_initcode_size); |  | ||||||
|   memset(p->tf, 0, sizeof(*p->tf)); |   memset(p->tf, 0, sizeof(*p->tf)); | ||||||
|   p->tf->cs = (SEG_UCODE << 3) | DPL_USER; |   p->tf->cs = (SEG_UCODE << 3) | DPL_USER; | ||||||
|   p->tf->ds = (SEG_UDATA << 3) | DPL_USER; |   p->tf->ds = (SEG_UDATA << 3) | DPL_USER; | ||||||
|  | @ -142,14 +106,17 @@ userinit(void) | ||||||
| int | int | ||||||
| growproc(int n) | growproc(int n) | ||||||
| { | { | ||||||
|  |   uint sz; | ||||||
|  |    | ||||||
|  |   sz = proc->sz; | ||||||
|   if(n > 0){ |   if(n > 0){ | ||||||
|     if (!allocuvm(proc->pgdir, (char *)proc->sz, n)) |     if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0) | ||||||
|       return -1; |       return -1; | ||||||
|   } else if(n < 0){ |   } 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; |       return -1; | ||||||
|   } |   } | ||||||
|   proc->sz += n; |   proc->sz = sz; | ||||||
|   switchuvm(proc); |   switchuvm(proc); | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  | @ -168,7 +135,7 @@ fork(void) | ||||||
|     return -1; |     return -1; | ||||||
| 
 | 
 | ||||||
|   // Copy process state from p.
 |   // 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); |     kfree(np->kstack); | ||||||
|     np->kstack = 0; |     np->kstack = 0; | ||||||
|     np->state = UNUSED; |     np->state = UNUSED; | ||||||
|  | @ -192,6 +159,92 @@ fork(void) | ||||||
|   return pid; |   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
 | //PAGEBREAK: 42
 | ||||||
| // Per-CPU process scheduler.
 | // Per-CPU process scheduler.
 | ||||||
| // Each CPU calls scheduler() after setting itself up.
 | // Each CPU calls scheduler() after setting itself up.
 | ||||||
|  | @ -356,89 +409,41 @@ kill(int pid) | ||||||
|   return -1; |   return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Exit the current process.  Does not return.
 | //PAGEBREAK: 36
 | ||||||
| // An exited process remains in the zombie state
 | // Print a process listing to console.  For debugging.
 | ||||||
| // until its parent calls wait() to find out it exited.
 | // Runs when user types ^P on console.
 | ||||||
|  | // No lock to avoid wedging a stuck machine further.
 | ||||||
| void | 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; |   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++){ |   for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ | ||||||
|     if(p->parent == proc){ |     if(p->state == UNUSED) | ||||||
|       p->parent = initproc; |       continue; | ||||||
|       if(p->state == ZOMBIE) |     if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) | ||||||
|         wakeup1(initproc); |       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"); | ||||||
| 
 |  | ||||||
|   // 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
 |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |  | ||||||
							
								
								
									
										64
									
								
								proc.h
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								proc.h
									
										
									
									
									
								
							|  | @ -8,6 +8,36 @@ | ||||||
| #define SEG_TSS   6  // this process's task state
 | #define SEG_TSS   6  // this process's task state
 | ||||||
| #define NSEGS     7 | #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.
 | // Saved registers for kernel context switches.
 | ||||||
| // Don't need to save all the segment registers (%cs, etc),
 | // Don't need to save all the segment registers (%cs, etc),
 | ||||||
| // because they are constant across kernel contexts.
 | // because they are constant across kernel contexts.
 | ||||||
|  | @ -31,13 +61,13 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; | ||||||
| // Per-process state
 | // Per-process state
 | ||||||
| struct proc { | struct proc { | ||||||
|   uint sz;                     // Size of process memory (bytes)
 |   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
 |   char *kstack;                // Bottom of kernel stack for this process
 | ||||||
|   enum procstate state;        // Process state
 |   enum procstate state;        // Process state
 | ||||||
|   volatile int pid;            // Process ID
 |   volatile int pid;            // Process ID
 | ||||||
|   struct proc *parent;         // Parent process
 |   struct proc *parent;         // Parent process
 | ||||||
|   struct trapframe *tf;        // Trap frame for current syscall
 |   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
 |   void *chan;                  // If non-zero, sleeping on chan
 | ||||||
|   int killed;                  // If non-zero, have been killed
 |   int killed;                  // If non-zero, have been killed
 | ||||||
|   struct file *ofile[NOFILE];  // Open files
 |   struct file *ofile[NOFILE];  // Open files
 | ||||||
|  | @ -48,35 +78,5 @@ struct proc { | ||||||
| // Process memory is laid out contiguously, low addresses first:
 | // Process memory is laid out contiguously, low addresses first:
 | ||||||
| //   text
 | //   text
 | ||||||
| //   original data and bss
 | //   original data and bss
 | ||||||
| //   invalid page
 |  | ||||||
| //   fixed-size stack
 | //   fixed-size stack
 | ||||||
| //   expandable heap
 | //   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; | 			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: (.*)/){ | 		if(/even: (.*)/){ | ||||||
| 			$file = $1; | 			$file = $1; | ||||||
| 			if(!defined($toc{$file})){ | 			if(!defined($toc{$file})){ | ||||||
|  | @ -89,18 +96,13 @@ perl -e ' | ||||||
| 				print STDERR "Have no toc for $file\n"; | 				print STDERR "Have no toc for $file\n"; | ||||||
| 				next; | 				next; | ||||||
| 			} | 			} | ||||||
| 			# this assumes that sheet 1 of code is a left page | 			if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){ | ||||||
| 			# double-check the PDF | 				print STDERR "$file does not start on a left page [$toc{$file}]\n"; | ||||||
| 			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"; |  | ||||||
| 			} | 			} | ||||||
| 			# why does this not work if I inline $x in the if? | 			# 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){ | 			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; | 			next; | ||||||
| 		} | 		} | ||||||
|  | @ -189,7 +191,9 @@ do | ||||||
| 	uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` | 	uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` | ||||||
| 	if [ "x$defs" != "x$uses" ]; then | 	if [ "x$defs" != "x$uses" ]; then | ||||||
| 		echo $i $defs | 		echo $i $defs | ||||||
| 		echo $uses |fmt -24 | sed 's/^/    /' | 		echo $uses |fmt -29 | sed 's/^/    /' | ||||||
|  | #	else | ||||||
|  | #		echo $i defined but not used >&2 | ||||||
| 	fi | 	fi | ||||||
| done | done | ||||||
| ) >refs | ) >refs | ||||||
|  |  | ||||||
|  | @ -22,8 +22,8 @@ proc.h | ||||||
| proc.c | proc.c | ||||||
| swtch.S | swtch.S | ||||||
| kalloc.c | kalloc.c | ||||||
|  | data.S | ||||||
| vm.c | vm.c | ||||||
| 
 |  | ||||||
| # system calls | # system calls | ||||||
| traps.h | traps.h | ||||||
| vectors.pl | vectors.pl | ||||||
|  | @ -46,11 +46,10 @@ file.c | ||||||
| sysfile.c | sysfile.c | ||||||
| exec.c | exec.c | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # pipes | # pipes | ||||||
| pipe.c | pipe.c | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # string operations | # string operations | ||||||
| string.c | string.c | ||||||
| 
 | 
 | ||||||
|  | @ -65,6 +64,7 @@ kbd.c | ||||||
| console.c | console.c | ||||||
| timer.c | timer.c | ||||||
| uart.c | uart.c | ||||||
|  | multiboot.S | ||||||
| 
 | 
 | ||||||
| # user-level | # user-level | ||||||
| initcode.S | initcode.S | ||||||
|  | @ -73,3 +73,6 @@ init.c | ||||||
| sh.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 | # types.h either | ||||||
| # param.h either | # param.h either | ||||||
| # defs.h either | # defs.h either | ||||||
|  | @ -9,25 +22,36 @@ | ||||||
| 
 | 
 | ||||||
| even: bootasm.S  # mild preference | even: bootasm.S  # mild preference | ||||||
| even: bootother.S  # mild preference | even: bootother.S  # mild preference | ||||||
| # bootmain.c either | even: bootmain.c  # mild preference | ||||||
| even: main.c | even: main.c | ||||||
| # mp.c don't care at all | # mp.c don't care at all | ||||||
| # even: initcode.S | # even: initcode.S | ||||||
| # odd: init.c | # odd: init.c | ||||||
| 
 | 
 | ||||||
| # spinlock.h either | # spinlock.h either | ||||||
| # spinlock.c either | left: spinlock.h  # mild preference | ||||||
| even: proc.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, | # goal is to have two action-packed 2-page spreads, | ||||||
| # one with | # one with | ||||||
| #     ksegment usegment allocproc userinit growproc fork | #     userinit growproc fork exit wait | ||||||
| # and another with | # and another with | ||||||
| #     scheduler sched yield forkret sleep wakeup1 wakeup | #     scheduler sched yield forkret sleep wakeup1 wakeup | ||||||
| right: proc.c   # VERY important | right: proc.c   # VERY important | ||||||
|  | even: proc.c   # VERY important | ||||||
|  | 
 | ||||||
|  | # A few more action packed spreads | ||||||
|  | # page table creation and process loading | ||||||
|  | #     walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm | ||||||
|  | # process memory management | ||||||
|  | #     allocuvm deallocuvm freevm | ||||||
|  | left: vm.c | ||||||
|  | odd: vm.c | ||||||
| 
 | 
 | ||||||
| # setjmp.S either |  | ||||||
| # vm.c either |  | ||||||
| # kalloc.c either | # kalloc.c either | ||||||
| 
 | 
 | ||||||
| # syscall.h either | # syscall.h either | ||||||
|  | @ -45,15 +69,25 @@ right: proc.c   # VERY important | ||||||
| # file.h either | # file.h either | ||||||
| # fs.h either | # fs.h either | ||||||
| # fsvar.h either | # fsvar.h either | ||||||
| left: ide.c | # left: ide.c # mild preference | ||||||
|  | even: ide.c | ||||||
| # odd: bio.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 | odd: fs.c   # VERY important | ||||||
|  | left: fs.c  # mild preference | ||||||
| # file.c either | # file.c either | ||||||
| # exec.c either | # exec.c either | ||||||
| # sysfile.c either | # sysfile.c either | ||||||
| 
 | 
 | ||||||
| # even: pipe.c  # mild preference | # even: pipe.c  # mild preference | ||||||
| # string.c either | # string.c either | ||||||
| left: kbd.h | # left: kbd.h  # mild preference | ||||||
|  | even: kbd.h | ||||||
| even: console.c | even: console.c | ||||||
| odd: sh.c | odd: sh.c | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								runoff1
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								runoff1
									
										
									
									
									
								
							|  | @ -33,7 +33,7 @@ for($i=0; $i<@lines; ){ | ||||||
| 	last if $i>=@lines; | 	last if $i>=@lines; | ||||||
| 
 | 
 | ||||||
| 	# If the rest of the file fits, use the whole thing. | 	# If the rest of the file fits, use the whole thing. | ||||||
| 	if(@lines <= $i+50){ | 	if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){ | ||||||
| 		$breakbefore = @lines; | 		$breakbefore = @lines; | ||||||
| 	}else{ | 	}else{ | ||||||
| 		# Find a good next page break; | 		# Find a good next page break; | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ initlock(struct spinlock *lk, char *name) | ||||||
| void | void | ||||||
| acquire(struct spinlock *lk) | acquire(struct spinlock *lk) | ||||||
| { | { | ||||||
|   pushcli(); |   pushcli(); // disable interrupts to avoid deadlock.
 | ||||||
|   if(holding(lk)) |   if(holding(lk)) | ||||||
|     panic("acquire"); |     panic("acquire"); | ||||||
| 
 | 
 | ||||||
|  | @ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[]) | ||||||
|    |    | ||||||
|   ebp = (uint*)v - 2; |   ebp = (uint*)v - 2; | ||||||
|   for(i = 0; i < 10; i++){ |   for(i = 0; i < 10; i++){ | ||||||
|     if(ebp == 0 || ebp < (uint *) 0x100000 || ebp == (uint*)0xffffffff) |     if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff) | ||||||
|       break; |       break; | ||||||
|     pcs[i] = ebp[1];     // saved %eip
 |     pcs[i] = ebp[1];     // saved %eip
 | ||||||
|     ebp = (uint*)ebp[0]; // saved %ebp
 |     ebp = (uint*)ebp[0]; // saved %ebp
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								stressfs.c
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								stressfs.c
									
										
									
									
									
								
							|  | @ -14,21 +14,20 @@ | ||||||
| int | int | ||||||
| main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||||
| { | { | ||||||
|   int i; |   int fd, i; | ||||||
|  |   char path[] = "stressfs0"; | ||||||
|  | 
 | ||||||
|   printf(1, "stressfs starting\n"); |   printf(1, "stressfs starting\n"); | ||||||
| 
 | 
 | ||||||
|   for (i = 0; i < 4; i++) { |   for(i = 0; i < 4; i++) | ||||||
|     if (fork() > 0) { |     if(fork() > 0) | ||||||
|       break; |       break; | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   printf(1, "%d\n", i); |   printf(1, "%d\n", i); | ||||||
| 
 | 
 | ||||||
|   char path[] = "stressfs0"; |  | ||||||
|   path[8] += i; |   path[8] += i; | ||||||
|   int fd = open(path, O_CREATE | O_RDWR); |   fd = open(path, O_CREATE | O_RDWR); | ||||||
|   for (i = 0; i < 100; i++) |   for(i = 0; i < 100; i++) | ||||||
|     printf(fd, "%d\n", i); |     printf(fd, "%d\n", i); | ||||||
|   close(fd); |   close(fd); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										63
									
								
								syscall.c
									
										
									
									
									
								
							
							
						
						
									
										63
									
								
								syscall.c
									
										
									
									
									
								
							|  | @ -22,8 +22,6 @@ fetchint(struct proc *p, uint addr, int *ip) | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // XXX should we copy the string?
 |  | ||||||
| 
 |  | ||||||
| // Fetch the nul-terminated string at addr from process p.
 | // Fetch the nul-terminated string at addr from process p.
 | ||||||
| // Doesn't actually copy the string - just sets *pp to point at it.
 | // Doesn't actually copy the string - just sets *pp to point at it.
 | ||||||
| // Returns length of string, not including nul.
 | // Returns length of string, not including nul.
 | ||||||
|  | @ -34,8 +32,8 @@ fetchstr(struct proc *p, uint addr, char **pp) | ||||||
| 
 | 
 | ||||||
|   if(addr >= p->sz) |   if(addr >= p->sz) | ||||||
|     return -1; |     return -1; | ||||||
|   *pp = (char *) addr; |   *pp = (char*)addr; | ||||||
|   ep = (char *) p->sz; |   ep = (char*)p->sz; | ||||||
|   for(s = *pp; s < ep; s++) |   for(s = *pp; s < ep; s++) | ||||||
|     if(*s == 0) |     if(*s == 0) | ||||||
|       return s - *pp; |       return s - *pp; | ||||||
|  | @ -46,8 +44,7 @@ fetchstr(struct proc *p, uint addr, char **pp) | ||||||
| int | int | ||||||
| argint(int n, int *ip) | argint(int n, int *ip) | ||||||
| { | { | ||||||
|   int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip); |   return fetchint(proc, proc->tf->esp + 4 + 4*n, ip); | ||||||
|   return x; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Fetch the nth word-sized system call argument as a pointer
 | // 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) |   if(argint(n, &i) < 0) | ||||||
|     return -1; |     return -1; | ||||||
|   if((uint)i >= proc->sz || (uint)i+size >= proc->sz) |   if((uint)i >= proc->sz || (uint)i+size > proc->sz) | ||||||
|     return -1; |     return -1; | ||||||
|   // *pp = proc->mem + i;   // XXXXX
 |   *pp = (char*)i; | ||||||
|   *pp = (char *) i;   // XXXXX
 |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -102,39 +98,52 @@ extern int sys_wait(void); | ||||||
| extern int sys_write(void); | extern int sys_write(void); | ||||||
| extern int sys_uptime(void); | extern int sys_uptime(void); | ||||||
| 
 | 
 | ||||||
|  | int | ||||||
|  | sys_init(void) | ||||||
|  | { | ||||||
|  |   initlog(); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int (*syscalls[])(void) = { | static int (*syscalls[])(void) = { | ||||||
| [SYS_chdir]   sys_chdir, | [SYS_init]    sys_init, | ||||||
| [SYS_close]   sys_close, |  | ||||||
| [SYS_dup]     sys_dup, |  | ||||||
| [SYS_exec]    sys_exec, |  | ||||||
| [SYS_exit]    sys_exit, |  | ||||||
| [SYS_fork]    sys_fork, | [SYS_fork]    sys_fork, | ||||||
| [SYS_fstat]   sys_fstat, | [SYS_exit]    sys_exit, | ||||||
| [SYS_getpid]  sys_getpid, | [SYS_wait]    sys_wait, | ||||||
| [SYS_kill]    sys_kill, |  | ||||||
| [SYS_link]    sys_link, |  | ||||||
| [SYS_mkdir]   sys_mkdir, |  | ||||||
| [SYS_mknod]   sys_mknod, |  | ||||||
| [SYS_open]    sys_open, |  | ||||||
| [SYS_pipe]    sys_pipe, | [SYS_pipe]    sys_pipe, | ||||||
| [SYS_read]    sys_read, | [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_sbrk]    sys_sbrk, | ||||||
| [SYS_sleep]   sys_sleep, | [SYS_sleep]   sys_sleep, | ||||||
| [SYS_unlink]  sys_unlink, |  | ||||||
| [SYS_wait]    sys_wait, |  | ||||||
| [SYS_write]   sys_write, |  | ||||||
| [SYS_uptime]  sys_uptime, | [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 | void | ||||||
| syscall(void) | syscall(void) | ||||||
| { | { | ||||||
|   int num; |   int num; | ||||||
|    | 
 | ||||||
|   num = proc->tf->eax; |   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](); |     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", |     cprintf("%d %s: unknown sys call %d\n", | ||||||
|             proc->pid, proc->name, num); |             proc->pid, proc->name, num); | ||||||
|     proc->tf->eax = -1; |     proc->tf->eax = -1; | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								syscall.h
									
										
									
									
									
								
							
							
						
						
									
										36
									
								
								syscall.h
									
										
									
									
									
								
							|  | @ -1,22 +1,24 @@ | ||||||
| // System call numbers
 | // System call numbers
 | ||||||
|  | #define SYS_init    0 | ||||||
| #define SYS_fork    1 | #define SYS_fork    1 | ||||||
| #define SYS_exit    2 | #define SYS_exit    2 | ||||||
| #define SYS_wait    3 | #define SYS_wait    3 | ||||||
| #define SYS_pipe    4 | #define SYS_pipe    4 | ||||||
| #define SYS_write   5 | #define SYS_read    5 | ||||||
| #define SYS_read    6 | #define SYS_kill    6 | ||||||
| #define SYS_close   7 | #define SYS_exec    7 | ||||||
| #define SYS_kill    8 | #define SYS_fstat   8 | ||||||
| #define SYS_exec    9 | #define SYS_chdir   9 | ||||||
| #define SYS_open   10 | #define SYS_dup    10 | ||||||
| #define SYS_mknod  11 | #define SYS_getpid 11 | ||||||
| #define SYS_unlink 12 | #define SYS_sbrk   12 | ||||||
| #define SYS_fstat  13 | #define SYS_sleep  13 | ||||||
| #define SYS_link   14 | #define SYS_uptime 14 | ||||||
| #define SYS_mkdir  15 | 
 | ||||||
| #define SYS_chdir  16 | #define SYS_open   15 | ||||||
| #define SYS_dup    17 | #define SYS_write  16 | ||||||
| #define SYS_getpid 18 | #define SYS_mknod  17 | ||||||
| #define SYS_sbrk   19 | #define SYS_unlink 18 | ||||||
| #define SYS_sleep  20 | #define SYS_link   19 | ||||||
| #define SYS_uptime 21 | #define SYS_mkdir  20 | ||||||
|  | #define SYS_close  21 | ||||||
|  |  | ||||||
|  | @ -344,11 +344,11 @@ sys_chdir(void) | ||||||
| int | int | ||||||
| sys_exec(void) | sys_exec(void) | ||||||
| { | { | ||||||
|   char *path, *argv[20]; |   char *path, *argv[MAXARG]; | ||||||
|   int i; |   int i; | ||||||
|   uint uargv, uarg; |   uint uargv, uarg; | ||||||
| 
 | 
 | ||||||
|   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) { |   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ | ||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
|   memset(argv, 0, sizeof(argv)); |   memset(argv, 0, sizeof(argv)); | ||||||
|  |  | ||||||
							
								
								
									
										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 | where the name is defined.  Successive lines in an entry list the line | ||||||
| numbers where the name is used.  For example, this entry: | numbers where the name is used.  For example, this entry: | ||||||
| 
 | 
 | ||||||
|     swtch 2208 |     swtch 2358 | ||||||
|         0318 1928 1967 2207 |         0317 2128 2166 2357 2358 | ||||||
|         2208 |  | ||||||
| 
 | 
 | ||||||
| indicates that swtch is defined on line 2208 and is mentioned on five lines | indicates that swtch is defined on line 2358 and is mentioned on five lines | ||||||
| on sheets 03, 19, and 22. | on sheets 03, 21, and 23. | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								trap.c
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								trap.c
									
										
									
									
									
								
							|  | @ -59,6 +59,9 @@ trap(struct trapframe *tf) | ||||||
|     ideintr(); |     ideintr(); | ||||||
|     lapiceoi(); |     lapiceoi(); | ||||||
|     break; |     break; | ||||||
|  |   case T_IRQ0 + IRQ_IDE+1: | ||||||
|  |     // Bochs generates spurious IDE1 interrupts.
 | ||||||
|  |     break; | ||||||
|   case T_IRQ0 + IRQ_KBD: |   case T_IRQ0 + IRQ_KBD: | ||||||
|     kbdintr(); |     kbdintr(); | ||||||
|     lapiceoi(); |     lapiceoi(); | ||||||
|  | @ -83,9 +86,10 @@ trap(struct trapframe *tf) | ||||||
|       panic("trap"); |       panic("trap"); | ||||||
|     } |     } | ||||||
|     // In user space, assume process misbehaved.
 |     // 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,  |             proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,  | ||||||
| 	    rcr2()); |             rcr2()); | ||||||
|     proc->killed = 1; |     proc->killed = 1; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								ulib.c
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								ulib.c
									
										
									
									
									
								
							|  | @ -45,7 +45,7 @@ strchr(const char *s, char c) | ||||||
| { | { | ||||||
|   for(; *s; s++) |   for(; *s; s++) | ||||||
|     if(*s == c) |     if(*s == c) | ||||||
|       return (char*) s; |       return (char*)s; | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ free(void *ap) | ||||||
| { | { | ||||||
|   Header *bp, *p; |   Header *bp, *p; | ||||||
| 
 | 
 | ||||||
|   bp = (Header*) ap - 1; |   bp = (Header*)ap - 1; | ||||||
|   for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) |   for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) | ||||||
|     if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) |     if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) | ||||||
|       break; |       break; | ||||||
|  | @ -52,7 +52,7 @@ morecore(uint nu) | ||||||
|   if(nu < 4096) |   if(nu < 4096) | ||||||
|     nu = 4096; |     nu = 4096; | ||||||
|   p = sbrk(nu * sizeof(Header)); |   p = sbrk(nu * sizeof(Header)); | ||||||
|   if(p == (char*) -1) |   if(p == (char*)-1) | ||||||
|     return 0; |     return 0; | ||||||
|   hp = (Header*)p; |   hp = (Header*)p; | ||||||
|   hp->s.size = nu; |   hp->s.size = nu; | ||||||
|  | @ -81,7 +81,7 @@ malloc(uint nbytes) | ||||||
|         p->s.size = nunits; |         p->s.size = nunits; | ||||||
|       } |       } | ||||||
|       freep = prevp; |       freep = prevp; | ||||||
|       return (void*) (p + 1); |       return (void*)(p + 1); | ||||||
|     } |     } | ||||||
|     if(p == freep) |     if(p == freep) | ||||||
|       if((p = morecore(nunits)) == 0) |       if((p = morecore(nunits)) == 0) | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								user.h
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								user.h
									
										
									
									
									
								
							|  | @ -18,10 +18,10 @@ int link(char*, char*); | ||||||
| int mkdir(char*); | int mkdir(char*); | ||||||
| int chdir(char*); | int chdir(char*); | ||||||
| int dup(int); | int dup(int); | ||||||
| int getpid(); | int getpid(void); | ||||||
| char* sbrk(int); | char* sbrk(int); | ||||||
| int sleep(int); | int sleep(int); | ||||||
| int uptime(); | int uptime(void); | ||||||
| 
 | 
 | ||||||
| // ulib.c
 | // ulib.c
 | ||||||
| int stat(char*, struct stat*); | int stat(char*, struct stat*); | ||||||
|  |  | ||||||
							
								
								
									
										210
									
								
								usertests.c
									
										
									
									
									
								
							
							
						
						
									
										210
									
								
								usertests.c
									
										
									
									
									
								
							|  | @ -3,6 +3,8 @@ | ||||||
| #include "user.h" | #include "user.h" | ||||||
| #include "fs.h" | #include "fs.h" | ||||||
| #include "fcntl.h" | #include "fcntl.h" | ||||||
|  | #include "syscall.h" | ||||||
|  | #include "traps.h" | ||||||
| 
 | 
 | ||||||
| char buf[2048]; | char buf[2048]; | ||||||
| char name[3]; | char name[3]; | ||||||
|  | @ -45,12 +47,12 @@ writetest(void) | ||||||
|     printf(stdout, "error: creat small failed!\n"); |     printf(stdout, "error: creat small failed!\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|   for(i = 0; i < 100; i++) { |   for(i = 0; i < 100; i++){ | ||||||
|     if(write(fd, "aaaaaaaaaa", 10) != 10) { |     if(write(fd, "aaaaaaaaaa", 10) != 10){ | ||||||
|       printf(stdout, "error: write aa %d new file failed\n", i); |       printf(stdout, "error: write aa %d new file failed\n", i); | ||||||
|       exit(); |       exit(); | ||||||
|     } |     } | ||||||
|     if(write(fd, "bbbbbbbbbb", 10) != 10) { |     if(write(fd, "bbbbbbbbbb", 10) != 10){ | ||||||
|       printf(stdout, "error: write bb %d new file failed\n", i); |       printf(stdout, "error: write bb %d new file failed\n", i); | ||||||
|       exit(); |       exit(); | ||||||
|     } |     } | ||||||
|  | @ -65,7 +67,7 @@ writetest(void) | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|   i = read(fd, buf, 2000); |   i = read(fd, buf, 2000); | ||||||
|   if(i == 2000) { |   if(i == 2000){ | ||||||
|     printf(stdout, "read succeeded ok\n"); |     printf(stdout, "read succeeded ok\n"); | ||||||
|   } else { |   } else { | ||||||
|     printf(stdout, "read failed\n"); |     printf(stdout, "read failed\n"); | ||||||
|  | @ -73,7 +75,7 @@ writetest(void) | ||||||
|   } |   } | ||||||
|   close(fd); |   close(fd); | ||||||
| 
 | 
 | ||||||
|   if(unlink("small") < 0) { |   if(unlink("small") < 0){ | ||||||
|     printf(stdout, "unlink small failed\n"); |     printf(stdout, "unlink small failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -93,9 +95,9 @@ writetest1(void) | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for(i = 0; i < MAXFILE; i++) { |   for(i = 0; i < MAXFILE; i++){ | ||||||
|     ((int*) buf)[0] = i; |     ((int*)buf)[0] = i; | ||||||
|     if(write(fd, buf, 512) != 512) { |     if(write(fd, buf, 512) != 512){ | ||||||
|       printf(stdout, "error: write big file failed\n", i); |       printf(stdout, "error: write big file failed\n", i); | ||||||
|       exit(); |       exit(); | ||||||
|     } |     } | ||||||
|  | @ -110,19 +112,19 @@ writetest1(void) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   n = 0; |   n = 0; | ||||||
|   for(;;) { |   for(;;){ | ||||||
|     i = read(fd, buf, 512); |     i = read(fd, buf, 512); | ||||||
|     if(i == 0) { |     if(i == 0){ | ||||||
|       if(n == MAXFILE - 1) { |       if(n == MAXFILE - 1){ | ||||||
|         printf(stdout, "read only %d blocks from big", n); |         printf(stdout, "read only %d blocks from big", n); | ||||||
|         exit(); |         exit(); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     } else if(i != 512) { |     } else if(i != 512){ | ||||||
|       printf(stdout, "read failed %d\n", i); |       printf(stdout, "read failed %d\n", i); | ||||||
|       exit(); |       exit(); | ||||||
|     } |     } | ||||||
|     if(((int*)buf)[0] != n) { |     if(((int*)buf)[0] != n){ | ||||||
|       printf(stdout, "read content of block %d is %d\n", |       printf(stdout, "read content of block %d is %d\n", | ||||||
|              n, ((int*)buf)[0]); |              n, ((int*)buf)[0]); | ||||||
|       exit(); |       exit(); | ||||||
|  | @ -130,7 +132,7 @@ writetest1(void) | ||||||
|     n++; |     n++; | ||||||
|   } |   } | ||||||
|   close(fd); |   close(fd); | ||||||
|   if(unlink("big") < 0) { |   if(unlink("big") < 0){ | ||||||
|     printf(stdout, "unlink big failed\n"); |     printf(stdout, "unlink big failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -146,14 +148,14 @@ createtest(void) | ||||||
| 
 | 
 | ||||||
|   name[0] = 'a'; |   name[0] = 'a'; | ||||||
|   name[2] = '\0'; |   name[2] = '\0'; | ||||||
|   for(i = 0; i < 52; i++) { |   for(i = 0; i < 52; i++){ | ||||||
|     name[1] = '0' + i; |     name[1] = '0' + i; | ||||||
|     fd = open(name, O_CREATE|O_RDWR); |     fd = open(name, O_CREATE|O_RDWR); | ||||||
|     close(fd); |     close(fd); | ||||||
|   } |   } | ||||||
|   name[0] = 'a'; |   name[0] = 'a'; | ||||||
|   name[2] = '\0'; |   name[2] = '\0'; | ||||||
|   for(i = 0; i < 52; i++) { |   for(i = 0; i < 52; i++){ | ||||||
|     name[1] = '0' + i; |     name[1] = '0' + i; | ||||||
|     unlink(name); |     unlink(name); | ||||||
|   } |   } | ||||||
|  | @ -164,22 +166,22 @@ void dirtest(void) | ||||||
| { | { | ||||||
|   printf(stdout, "mkdir test\n"); |   printf(stdout, "mkdir test\n"); | ||||||
| 
 | 
 | ||||||
|   if(mkdir("dir0") < 0) { |   if(mkdir("dir0") < 0){ | ||||||
|     printf(stdout, "mkdir failed\n"); |     printf(stdout, "mkdir failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if(chdir("dir0") < 0) { |   if(chdir("dir0") < 0){ | ||||||
|     printf(stdout, "chdir dir0 failed\n"); |     printf(stdout, "chdir dir0 failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if(chdir("..") < 0) { |   if(chdir("..") < 0){ | ||||||
|     printf(stdout, "chdir .. failed\n"); |     printf(stdout, "chdir .. failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if(unlink("dir0") < 0) { |   if(unlink("dir0") < 0){ | ||||||
|     printf(stdout, "unlink dir0 failed\n"); |     printf(stdout, "unlink dir0 failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -190,7 +192,7 @@ void | ||||||
| exectest(void) | exectest(void) | ||||||
| { | { | ||||||
|   printf(stdout, "exec test\n"); |   printf(stdout, "exec test\n"); | ||||||
|   if(exec("echo", echoargv) < 0) { |   if(exec("echo", echoargv) < 0){ | ||||||
|     printf(stdout, "exec echo failed\n"); |     printf(stdout, "exec echo failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -324,20 +326,21 @@ mem(void) | ||||||
|   void *m1, *m2; |   void *m1, *m2; | ||||||
|   int pid, ppid; |   int pid, ppid; | ||||||
| 
 | 
 | ||||||
|  |   printf(1, "mem test\n"); | ||||||
|   ppid = getpid(); |   ppid = getpid(); | ||||||
|   if((pid = fork()) == 0){ |   if((pid = fork()) == 0){ | ||||||
|     m1 = 0; |     m1 = 0; | ||||||
|     while((m2 = malloc(10001)) != 0) { |     while((m2 = malloc(10001)) != 0){ | ||||||
|       *(char**) m2 = m1; |       *(char**)m2 = m1; | ||||||
|       m1 = m2; |       m1 = m2; | ||||||
|     } |     } | ||||||
|     while(m1) { |     while(m1){ | ||||||
|       m2 = *(char**)m1; |       m2 = *(char**)m1; | ||||||
|       free(m1); |       free(m1); | ||||||
|       m1 = m2; |       m1 = m2; | ||||||
|     } |     } | ||||||
|     m1 = malloc(1024*20); |     m1 = malloc(1024*20); | ||||||
|     if(m1 == 0) { |     if(m1 == 0){ | ||||||
|       printf(1, "couldn't allocate mem?!!\n"); |       printf(1, "couldn't allocate mem?!!\n"); | ||||||
|       kill(ppid); |       kill(ppid); | ||||||
|       exit(); |       exit(); | ||||||
|  | @ -1234,16 +1237,18 @@ forktest(void) | ||||||
| void | void | ||||||
| sbrktest(void) | sbrktest(void) | ||||||
| { | { | ||||||
|   int pid; |   int fds[2], pid, pids[32], ppid; | ||||||
|   char *oldbrk = sbrk(0); |   char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch; | ||||||
|  |   uint amt; | ||||||
| 
 | 
 | ||||||
|   printf(stdout, "sbrk test\n"); |   printf(stdout, "sbrk test\n"); | ||||||
|  |   oldbrk = sbrk(0); | ||||||
| 
 | 
 | ||||||
|   // can one sbrk() less than a page?
 |   // can one sbrk() less than a page?
 | ||||||
|   char *a = sbrk(0); |   a = sbrk(0); | ||||||
|   int i; |   int i; | ||||||
|   for(i = 0; i < 5000; i++){ |   for(i = 0; i < 5000; i++){ | ||||||
|     char *b = sbrk(1); |     b = sbrk(1); | ||||||
|     if(b != a){ |     if(b != a){ | ||||||
|       printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); |       printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); | ||||||
|       exit(); |       exit(); | ||||||
|  | @ -1256,7 +1261,7 @@ sbrktest(void) | ||||||
|     printf(stdout, "sbrk test fork failed\n"); |     printf(stdout, "sbrk test fork failed\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|   char *c = sbrk(1); |   c = sbrk(1); | ||||||
|   c = sbrk(1); |   c = sbrk(1); | ||||||
|   if(c != a + 1){ |   if(c != a + 1){ | ||||||
|     printf(stdout, "sbrk test failed post-fork\n"); |     printf(stdout, "sbrk test failed post-fork\n"); | ||||||
|  | @ -1268,18 +1273,18 @@ sbrktest(void) | ||||||
| 
 | 
 | ||||||
|   // can one allocate the full 640K?
 |   // can one allocate the full 640K?
 | ||||||
|   a = sbrk(0); |   a = sbrk(0); | ||||||
|   uint amt = (640 * 1024) - (uint) a; |   amt = (640 * 1024) - (uint)a; | ||||||
|   char *p = sbrk(amt); |   p = sbrk(amt); | ||||||
|   if(p != a){ |   if(p != a){ | ||||||
|     printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a); |     printf(stdout, "sbrk test failed 640K test, p %x a %x\n", p, a); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|   char *lastaddr = (char *)(640 * 1024 - 1); |   lastaddr = (char*)(640 * 1024 - 1); | ||||||
|   *lastaddr = 99; |   *lastaddr = 99; | ||||||
| 
 | 
 | ||||||
|   // is one forbidden from allocating more than 640K?
 |   // is one forbidden from allocating more than 640K?
 | ||||||
|   c = sbrk(4096); |   c = sbrk(4096); | ||||||
|   if(c != (char *) 0xffffffff){ |   if(c != (char*)0xffffffff){ | ||||||
|     printf(stdout, "sbrk allocated more than 640K, c %x\n", c); |     printf(stdout, "sbrk allocated more than 640K, c %x\n", c); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -1287,7 +1292,7 @@ sbrktest(void) | ||||||
|   // can one de-allocate?
 |   // can one de-allocate?
 | ||||||
|   a = sbrk(0); |   a = sbrk(0); | ||||||
|   c = sbrk(-4096); |   c = sbrk(-4096); | ||||||
|   if(c == (char *) 0xffffffff){ |   if(c == (char*)0xffffffff){ | ||||||
|     printf(stdout, "sbrk could not deallocate\n"); |     printf(stdout, "sbrk could not deallocate\n"); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
|  | @ -1311,15 +1316,15 @@ sbrktest(void) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   c = sbrk(4096); |   c = sbrk(4096); | ||||||
|   if(c != (char *) 0xffffffff){ |   if(c != (char*)0xffffffff){ | ||||||
|     printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c); |     printf(stdout, "sbrk was able to re-allocate beyond 640K, c %x\n", c); | ||||||
|     exit(); |     exit(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // can we read the kernel's memory?
 |   // can we read the kernel's memory?
 | ||||||
|   for(a = (char*)(640*1024); a < (char *)2000000; a += 50000){ |   for(a = (char*)(640*1024); a < (char*)2000000; a += 50000){ | ||||||
|     int ppid = getpid(); |     ppid = getpid(); | ||||||
|     int pid = fork(); |     pid = fork(); | ||||||
|     if(pid < 0){ |     if(pid < 0){ | ||||||
|       printf(stdout, "fork failed\n"); |       printf(stdout, "fork failed\n"); | ||||||
|       exit(); |       exit(); | ||||||
|  | @ -1332,6 +1337,38 @@ sbrktest(void) | ||||||
|     wait(); |     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) |   if(sbrk(0) > oldbrk) | ||||||
|     sbrk(-(sbrk(0) - oldbrk)); |     sbrk(-(sbrk(0) - oldbrk)); | ||||||
| 
 | 
 | ||||||
|  | @ -1339,26 +1376,89 @@ sbrktest(void) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| stacktest(void) | validateint(int *p) | ||||||
| { | { | ||||||
|   printf(stdout, "stack test\n"); |   int res; | ||||||
|   char dummy = 1; |   asm("mov %%esp, %%ebx\n\t" | ||||||
|   char *p = &dummy; |       "mov %3, %%esp\n\t" | ||||||
|   int ppid = getpid(); |       "int %2\n\t" | ||||||
|   int pid = fork(); |       "mov %%ebx, %%esp" : | ||||||
|   if(pid < 0){ |       "=a" (res) : | ||||||
|     printf(stdout, "fork failed\n"); |       "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) : | ||||||
|     exit(); |       "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){ |   if(pid == 0){ | ||||||
|     // should cause a trap:
 |     char *args[32+1]; | ||||||
|     p[-4096] = 'z'; |     int i; | ||||||
|     kill(ppid); |     for(i = 0; i < 32; i++) | ||||||
|     printf(stdout, "stack test failed: page before stack was writeable\n"); |       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(); |     exit(); | ||||||
|   } |   } | ||||||
|   wait(); |   wait(); | ||||||
|   printf(stdout, "stack test OK\n"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
|  | @ -1372,8 +1472,10 @@ main(int argc, char *argv[]) | ||||||
|   } |   } | ||||||
|   close(open("usertests.ran", O_CREATE)); |   close(open("usertests.ran", O_CREATE)); | ||||||
| 
 | 
 | ||||||
|   stacktest(); |   bigargtest(); | ||||||
|  |   bsstest(); | ||||||
|   sbrktest(); |   sbrktest(); | ||||||
|  |   validatetest(); | ||||||
| 
 | 
 | ||||||
|   opentest(); |   opentest(); | ||||||
|   writetest(); |   writetest(); | ||||||
|  |  | ||||||
							
								
								
									
										605
									
								
								vm.c
									
										
									
									
									
								
							
							
						
						
									
										605
									
								
								vm.c
									
										
									
									
									
								
							|  | @ -6,96 +6,22 @@ | ||||||
| #include "proc.h" | #include "proc.h" | ||||||
| #include "elf.h" | #include "elf.h" | ||||||
| 
 | 
 | ||||||
| // The mappings from logical to linear are one to one (i.e.,
 | extern char data[];  // defined in data.S
 | ||||||
| // 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).
 |  | ||||||
| 
 | 
 | ||||||
| #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()
 | static pde_t *kpgdir;  // for use in scheduler()
 | ||||||
| 
 | 
 | ||||||
| // return the address of the PTE in page table pgdir
 | // Allocate one page table for the machine for the kernel address
 | ||||||
| // that corresponds to linear address va.  if create!=0,
 | // space for scheduler processes.
 | ||||||
| // create any required page table pages.
 | void | ||||||
| static pte_t * | kvmalloc(void) | ||||||
| walkpgdir(pde_t *pgdir, const void *va, int create) |  | ||||||
| { | { | ||||||
|   uint r; |   kpgdir = setupkvm(); | ||||||
|   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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Set up CPU's kernel segment descriptors.
 | // Set up CPU's kernel segment descriptors.
 | ||||||
| // Run once at boot time on each CPU.
 | // Run once at boot time on each CPU.
 | ||||||
| void | void | ||||||
| ksegment(void) | seginit(void) | ||||||
| { | { | ||||||
|   struct cpu *c; |   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_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER); | ||||||
|   c->gdt[SEG_UDATA] = SEG(STA_W, 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); |   c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0); | ||||||
| 
 | 
 | ||||||
|   lgdt(c->gdt, sizeof(c->gdt)); |   lgdt(c->gdt, sizeof(c->gdt)); | ||||||
|  | @ -120,250 +46,106 @@ ksegment(void) | ||||||
|   proc = 0; |   proc = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Switch h/w page table and TSS registers to point to process p.
 | // Return the address of the PTE in page table pgdir
 | ||||||
| void | // that corresponds to linear address va.  If create!=0,
 | ||||||
| switchuvm(struct proc *p) | // 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
 |   pde = &pgdir[PDX(va)]; | ||||||
|   cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0); |   if(*pde & PTE_P){ | ||||||
|   cpu->gdt[SEG_TSS].s = 0; |     pgtab = (pte_t*)PTE_ADDR(*pde); | ||||||
|   cpu->ts.ss0 = SEG_KDATA << 3; |   } else { | ||||||
|   cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; |     if(!create || (pgtab = (pte_t*)kalloc()) == 0) | ||||||
|   ltr(SEG_TSS << 3); |       return 0; | ||||||
| 
 |     // Make sure all those PTE_P bits are zero.
 | ||||||
|   if (p->pgdir == 0) |     memset(pgtab, 0, PGSIZE); | ||||||
|     panic("switchuvm: no pgdir\n"); |     // The permissions here are overly generous, but they can
 | ||||||
| 
 |     // be further restricted by the permissions in the page table 
 | ||||||
|   lcr3(PADDR(p->pgdir));  // switch to new address space
 |     // entries, if necessary.
 | ||||||
|   popcli(); |     *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
 | // Create PTEs for linear addresses starting at la that refer to
 | ||||||
| // no process is running.
 | // physical addresses starting at pa. la and size might not
 | ||||||
| void | // be page-aligned.
 | ||||||
| switchkvm() | 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.
 | // Set up kernel part of a page table.
 | ||||||
| pde_t* | pde_t* | ||||||
| setupkvm(void) | setupkvm(void) | ||||||
| { | { | ||||||
|   pde_t *pgdir; |   pde_t *pgdir; | ||||||
|  |   struct kmap *k; | ||||||
| 
 | 
 | ||||||
|   // Allocate page directory
 |   if((pgdir = (pde_t*)kalloc()) == 0) | ||||||
|   if (!(pgdir = (pde_t *) kalloc())) |  | ||||||
|     return 0; |     return 0; | ||||||
|   memset(pgdir, 0, PGSIZE); |   memset(pgdir, 0, PGSIZE); | ||||||
|   // Map IO space from 640K to 1Mbyte
 |   k = kmap; | ||||||
|   if (!mappages(pgdir, (void *)USERTOP, 0x60000, USERTOP, PTE_W)) |   for(k = kmap; k < &kmap[NELEM(kmap)]; k++) | ||||||
|     return 0; |     if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 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)) |  | ||||||
|     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 0; | ||||||
|   } |  | ||||||
|   return 1; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void |   return pgdir; | ||||||
| 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.
 | // Turn on paging.
 | ||||||
|  | @ -378,3 +160,208 @@ vmenable(void) | ||||||
|   lcr0(cr0); |   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> | <html> | ||||||
| <head> | <head> | ||||||
| <title>Xv6, a simple Unix-like teaching operating system</title> | <title>Xv6, a simple Unix-like teaching operating system</title> | ||||||
|  | @ -32,31 +31,36 @@ h2 { | ||||||
| --></style> | --></style> | ||||||
| </head> | </head> | ||||||
| <body bgcolor=#ffffff> | <body bgcolor=#ffffff> | ||||||
|  | 
 | ||||||
| <h1>Xv6, a simple Unix-like teaching operating system</h1> | <h1>Xv6, a simple Unix-like teaching operating system</h1> | ||||||
| <br><br> | 
 | ||||||
| Xv6 is a teaching operating system developed | <h2>Introduction</h2> | ||||||
| in the summer of 2006 for MIT's operating systems course, | 
 | ||||||
| “6.828: Operating Systems Engineering.” | Xv6 is a teaching operating system developed in the summer of 2006 for | ||||||
| We used it for 6.828 in Fall 2006 and Fall 2007 | MIT's operating systems | ||||||
| and are using it this semester (Fall 2008). | course, <a href="http://pdos.csail.mit.edu/6.828">6.828: operating | ||||||
| We hope that xv6 will be useful in other courses too. | systems Engineering</a>. We hope that xv6 will be useful in other | ||||||
| This page collects resources to aid the use of xv6 | courses too.  This page collects resources to aid the use of xv6 in | ||||||
| in other courses. | 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> | <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 | <p>For many years, MIT had no operating systems course.  In the fall | ||||||
| created a new, experimental course (6.097) | of 2002, Frans Kaashoek, Josh Cates, and Emil Sit created a new, | ||||||
| to teach operating systems engineering. | experimental course (6.097) to teach operating systems engineering. | ||||||
| In the course lectures, the class worked through Sixth Edition Unix (aka V6) | In the course lectures, the class worked through <a href="#v6">Sixth | ||||||
| using John Lions's famous commentary. | Edition Unix (aka V6)</a> using John Lions's famous commentary.  In | ||||||
| In the lab assignments, students wrote most of an exokernel operating | the lab assignments, students wrote most of an exokernel operating | ||||||
| system, eventually named Jos, for the Intel x86. | system, eventually named Jos, for the Intel x86.  Exposing students to | ||||||
| Exposing students to multiple systems–V6 and Jos–helped | multiple systems–V6 and Jos–helped develop a sense of the | ||||||
| develop a sense of the spectrum of operating system designs. | spectrum of operating system designs.  In the fall of 2003, the | ||||||
| In the fall of 2003, the experimental 6.097 became the | experimental 6.097 became the official course 6.828; the course has | ||||||
| official course 6.828; the course has been offered each fall since then. | been offered each fall since then. | ||||||
| <br><br> | 
 | ||||||
|  | <p> | ||||||
| V6 presented pedagogic challenges from the start. | V6 presented pedagogic challenges from the start. | ||||||
| Students doubted the relevance of an obsolete 30-year-old operating system | Students doubted the relevance of an obsolete 30-year-old operating system | ||||||
| written in an obsolete programming language (pre-K&R C) | written in an obsolete programming language (pre-K&R C) | ||||||
|  | @ -76,13 +80,12 @@ uniprocessors such as | ||||||
| enabling/disabling interrupts) and helps relevance. | enabling/disabling interrupts) and helps relevance. | ||||||
| Finally, writing a new system allowed us to write cleaner versions | Finally, writing a new system allowed us to write cleaner versions | ||||||
| of the rougher parts of V6, like the scheduler and file system. | of the rougher parts of V6, like the scheduler and file system. | ||||||
| <br><br> | <p> 6.828 substituted xv6 for V6 in the fall of 2006.  Based on | ||||||
| 6.828 substituted xv6 for V6 in the fall of 2006. | that experience, we cleaned up rough patches of xv6.  Since then, xv6 | ||||||
| Based on that experience, we cleaned up rough patches | has stabilized, so we are making it available in the hopes that others | ||||||
| of xv6 for the course in the fall of 2007. | will find it useful too. | ||||||
| Since then, xv6 has stabilized, so we are making it | 
 | ||||||
| available in the hopes that others will find it useful too. | <p> | ||||||
| <br><br> |  | ||||||
| 6.828 uses both xv6 and Jos. | 6.828 uses both xv6 and Jos. | ||||||
| Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua, | Courses taught at UCLA, NYU, Peking University, Stanford, Tsinghua, | ||||||
| and University Texas (Austin) have used | and University Texas (Austin) have used | ||||||
|  | @ -90,14 +93,16 @@ Jos without xv6; we believe other courses could use | ||||||
| xv6 without Jos, though we are not aware of any that have. | xv6 without Jos, though we are not aware of any that have. | ||||||
| 
 | 
 | ||||||
| <h2>Xv6 sources</h2> | <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 | We distribute the sources in electronic form but also as | ||||||
| a printed booklet with line numbers that keep everyone | a printed booklet with line numbers that keep everyone | ||||||
| together during lectures.  The booklet is available as | 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>; | 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. | see the LICENSE file in the source distribution. | ||||||
| <br><br> | 
 | ||||||
|  | <p> | ||||||
| xv6 compiles using the GNU C compiler, | xv6 compiles using the GNU C compiler, | ||||||
| targeted at the x86 using ELF binaries. | targeted at the x86 using ELF binaries. | ||||||
| On BSD and Linux systems, you can use the native compilers; | 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 | Xv6 does boot on real hardware, but typically | ||||||
| we run it using the Bochs emulator. | we run it using the Bochs emulator. | ||||||
| Both the GCC cross compiler and Bochs | 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> | <h2>Xv6 lecture material</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. |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 1. Operating systems</i></b> | In 6.828, the lectures in the first half of the course introduce the | ||||||
| <br><br> | PC hardware, the Intel x86, and then xv6.  The lectures in the second | ||||||
| The first lecture introduces both the general topic of | half consider advanced topics using research papers; for some, xv6 | ||||||
| operating systems and the specific approach of 6.828. | serves as a useful base for making discussions concrete.  The lecture | ||||||
| After defining “operating system,” the lecture | notes are available from the 6.828 schedule page, and the chapters of | ||||||
| examines the implementation of a Unix shell | the commentary are below. | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 2.  PC hardware and x86 programming</i></b> | <h2>Xv6 commentary (rough)</h2> | ||||||
| <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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 3.  Operating system organization</i></b> | <p>The chapters are rough drafts. | ||||||
| <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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 4.  Address spaces using segmentation</i></b> | <p>Introduction yet to be written.<br> | ||||||
| <br><br> | <ul> | ||||||
| This is the first lecture that uses xv6. | <li>read with the code side by side | ||||||
| It introduces the idea of address spaces and the | <li>code references look like (xxxx) or (xxxx-yyyy) in small text. | ||||||
| details of the x86 segmentation hardware. | <li><a href="xv6-rev5.pdf">this pdf</a> is the one with matching line numbers. | ||||||
| It makes the discussion concrete by reading the xv6 | <li>each chapter starts with an introduction to the topic, | ||||||
| source code and watching xv6 execute using the Bochs simulator. | spends most of the text on code, | ||||||
| <br><br> | and then wraps up talking about how xv6 | ||||||
| Reading: x86 MMU handout, | compares to real-world operating systems. | ||||||
| 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>. | </ul> | ||||||
| <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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 5.  Address spaces using page tables</i></b> | <a href="unix.pdf">Chapter 0: Operating system interfaces</a> | ||||||
| <br><br> | <blockquote> | ||||||
| This lecture continues the discussion of address spaces, | The Unix system call interface.  (rev 4) | ||||||
| examining the other x86 virtual memory mechanism: page tables. | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 6.  Interrupts and exceptions</i></b> | <a href="boot.pdf">Chapter 1: Bootstrap</a> | ||||||
| <br><br> | <blockquote> | ||||||
| How does a user program invoke the operating system kernel? | From power on to kernel start. (rev 4) | ||||||
| How does the kernel return to the user program? | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 7.  Multiprocessors and locking</i></b> | <a href="mem.pdf">Chapter 2: Processes</a> | ||||||
| <br><br> | <blockquote> | ||||||
| This lecture introduces the problems of | Memory and process allocation, segments, the first user process. (rev 4) | ||||||
| coordination and synchronization on a | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 8.  Threads, processes and context switching</i></b> | <a href="trap.pdf">Chapter 3: Traps</a> | ||||||
| <br><br> | <blockquote> | ||||||
| The last lecture introduced some of the issues | Low-level trap mechanism, trap handler, system call arguments, sbrk, fork. | ||||||
| in writing threaded programs, using xv6's processes | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 9.  Processes and coordination</i></b> | <a href="lock.pdf">Chapter 4: Locks</a> | ||||||
| <br><br> | <blockquote> | ||||||
| This lecture introduces the idea of sequence coordination | Locks and interrupts. | ||||||
| and then examines the particular solution illustrated by | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 10.  Files and disk I/O</i></b> | <a href="sched.pdf">Chapter 5: Scheduling and coordination</a> | ||||||
| <br><br> | <blockquote> | ||||||
| This is the first of three file system lectures. | Scheduling, sleep and wakeup, pipes, wait and exit. | ||||||
| This lecture introduces the basic file system interface | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 11.  Naming</i></b> | <a href="disk.pdf">Chapter 6: Buffer cache</a> | ||||||
| <br><br> | <blockquote> | ||||||
| The last lecture discussed on-disk file system representation. | Buffer cache and IDE disk driver. | ||||||
| This lecture covers the implementation of | </blockquote> | ||||||
| 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> |  | ||||||
| 
 | 
 | ||||||
| <br><br><b><i>Lecture 12.  High-performance file systems</i></b> | <a href="fsdata.pdf">Chapter 7: File system data</a> | ||||||
| <br><br> | <blockquote> | ||||||
| This lecture is the first of the research paper-based lectures. | Block in use bitmap, block allocation, inode structure, inode contents, | ||||||
| It discusses the “soft updates” paper, | directories, path names. | ||||||
| using xv6 as a concrete example. | </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> | <h2>Feedback</h2> | ||||||
| If you are interested in using xv6 or have used xv6 in a course, | 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 | If there's anything that we can do to make xv6 easier | ||||||
| to adopt, we'd like to hear about it. | to adopt, we'd like to hear about it. | ||||||
| We'd also be interested to hear what worked well and what didn't. | We'd also be interested to hear what worked well and what didn't. | ||||||
| <br><br> | <p> | ||||||
| Russ Cox (rsc@swtch.com)<br> | Russ Cox (rsc@swtch.com)<br> | ||||||
| Frans Kaashoek (kaashoek@mit.edu)<br> | Frans Kaashoek (kaashoek@mit.edu)<br> | ||||||
| Robert Morris (rtm@mit.edu) | Robert Morris (rtm@mit.edu) | ||||||
| <br><br> | <p> | ||||||
| You can reach all of us at 6.828-staff@pdos.csail.mit.edu. | 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; |   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 | static inline void | ||||||
| loadgs(ushort v) | loadgs(ushort v) | ||||||
| { | { | ||||||
|   asm volatile("movw %0, %%gs" : : "r" (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 | static inline void | ||||||
| cli(void) | cli(void) | ||||||
| { | { | ||||||
|  | @ -121,66 +124,56 @@ sti(void) | ||||||
|   asm volatile("sti"); |   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)); |   asm volatile("movl %0,%%cr0" : : "r" (val)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline uint rcr0(void) | static inline uint | ||||||
|  | rcr0(void) | ||||||
| { | { | ||||||
|   uint val; |   uint val; | ||||||
|   asm volatile("movl %%cr0,%0" : "=r" (val)); |   asm volatile("movl %%cr0,%0" : "=r" (val)); | ||||||
|   return val; |   return val; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline uint rcr2(void) | static inline uint | ||||||
|  | rcr2(void) | ||||||
| { | { | ||||||
|   uint val; |   uint val; | ||||||
|   asm volatile("movl %%cr2,%0" : "=r" (val)); |   asm volatile("movl %%cr2,%0" : "=r" (val)); | ||||||
|   return val; |   return val; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void lcr3(uint val)  | static inline void | ||||||
|  | lcr3(uint val)  | ||||||
| { | { | ||||||
|   asm volatile("movl %0,%%cr3" : : "r" (val)); |   asm volatile("movl %0,%%cr3" : : "r" (val)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline uint rcr3(void) | static inline uint | ||||||
|  | rcr3(void) | ||||||
| { | { | ||||||
|   uint val; |   uint val; | ||||||
|   asm volatile("movl %%cr3,%0" : "=r" (val)); |   asm volatile("movl %%cr3,%0" : "=r" (val)); | ||||||
|   return 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
 | //PAGEBREAK: 36
 | ||||||
| // Layout of the trap frame built on the stack by the
 | // Layout of the trap frame built on the stack by the
 | ||||||
| // hardware and by trapasm.S, and passed to trap().
 | // 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