├── .cvsignore ├── .dir-locals.el ├── .gdbinit.tmpl ├── .gitignore ├── BUGS ├── LICENSE ├── Makefile ├── Notes ├── README.md ├── TRICKS ├── asm.h ├── bio.c ├── bootasm.S ├── bootmain.c ├── buf.h ├── cat.c ├── console.c ├── cuth ├── date.h ├── defs.h ├── dot-bochsrc ├── echo.c ├── elf.h ├── entry.S ├── entryother.S ├── exec.c ├── fcntl.h ├── file.c ├── file.h ├── fs.c ├── fs.h ├── gdbutil ├── grep.c ├── ide.c ├── init.c ├── initcode.S ├── ioapic.c ├── kalloc.c ├── kbd.c ├── kbd.h ├── kernel.ld ├── kill.c ├── lapic.c ├── ln.c ├── log.c ├── ls.c ├── main.c ├── memide.c ├── memlayout.h ├── mkdir.c ├── mkfs.c ├── mmu.h ├── mp.c ├── mp.h ├── param.h ├── picirq.c ├── pipe.c ├── pr.pl ├── printf.c ├── printpcs ├── proc.c ├── proc.h ├── rm.c ├── runoff ├── runoff.list ├── runoff.spec ├── runoff1 ├── sh.c ├── show1 ├── sign.pl ├── sleep1.p ├── sleeplock.c ├── sleeplock.h ├── spinlock.c ├── spinlock.h ├── spinp ├── stat.h ├── stressfs.c ├── string.c ├── swtch.S ├── syscall.c ├── syscall.h ├── sysfile.c ├── sysproc.c ├── testthreads.c ├── toc.ftr ├── toc.hdr ├── trap.c ├── trapasm.S ├── traps.h ├── types.h ├── uart.c ├── ulib.c ├── umalloc.c ├── user.h ├── usertests.c ├── usys.S ├── vectors.pl ├── vm.c ├── wc.c ├── x86.h └── zombie.c /.cvsignore: -------------------------------------------------------------------------------- 1 | *.asm 2 | *.d 3 | *.sym 4 | _* 5 | kernel 6 | user1 7 | userfs 8 | usertests 9 | xv6.img 10 | vectors.S 11 | bochsout.txt 12 | bootblock 13 | bootother 14 | bootother.out 15 | parport.out 16 | fmt 17 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((c-mode 2 | (indent-tabs-mode . nil) 3 | (c-file-style . "bsd") 4 | (c-basic-offset . 2))) 5 | -------------------------------------------------------------------------------- /.gdbinit.tmpl: -------------------------------------------------------------------------------- 1 | set $lastcs = -1 2 | 3 | define hook-stop 4 | # There doesn't seem to be a good way to detect if we're in 16- or 5 | # 32-bit mode, but in 32-bit mode we always run with CS == 8 in the 6 | # kernel and CS == 35 in user space 7 | if $cs == 8 || $cs == 35 8 | if $lastcs != 8 && $lastcs != 35 9 | set architecture i386 10 | end 11 | x/i $pc 12 | else 13 | if $lastcs == -1 || $lastcs == 8 || $lastcs == 35 14 | set architecture i8086 15 | end 16 | # Translate the segment:offset into a physical address 17 | printf "[%4x:%4x] ", $cs, $eip 18 | x/i $cs*16+$eip 19 | end 20 | set $lastcs = $cs 21 | end 22 | 23 | echo + target remote localhost:1234\n 24 | target remote localhost:1234 25 | 26 | echo + symbol-file kernel\n 27 | symbol-file kernel 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | _* 3 | *.o 4 | *.d 5 | *.asm 6 | *.sym 7 | *.img 8 | vectors.S 9 | bootblock 10 | entryother 11 | initcode 12 | initcode.out 13 | kernel 14 | kernelmemfs 15 | mkfs 16 | .gdbinit 17 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | formatting: 2 | need to fix PAGEBREAK mechanism 3 | 4 | sh: 5 | can't always runcmd in child -- breaks cd. 6 | maybe should hard-code PATH=/ ? 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The xv6 software is: 2 | 3 | Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox, 4 | Massachusetts Institute of Technology 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS = \ 2 | bio.o\ 3 | console.o\ 4 | exec.o\ 5 | file.o\ 6 | fs.o\ 7 | ide.o\ 8 | ioapic.o\ 9 | kalloc.o\ 10 | kbd.o\ 11 | lapic.o\ 12 | log.o\ 13 | main.o\ 14 | mp.o\ 15 | picirq.o\ 16 | pipe.o\ 17 | proc.o\ 18 | sleeplock.o\ 19 | spinlock.o\ 20 | string.o\ 21 | swtch.o\ 22 | syscall.o\ 23 | sysfile.o\ 24 | sysproc.o\ 25 | trapasm.o\ 26 | trap.o\ 27 | uart.o\ 28 | vectors.o\ 29 | vm.o\ 30 | 31 | # Cross-compiling (e.g., on Mac OS X) 32 | TOOLPREFIX = i386-elf- 33 | 34 | # Using native tools (e.g., on X86 Linux) 35 | #TOOLPREFIX = 36 | 37 | # Try to infer the correct TOOLPREFIX if not set 38 | ifndef TOOLPREFIX 39 | TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ 40 | then echo 'i386-jos-elf-'; \ 41 | elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ 42 | then echo ''; \ 43 | else echo "***" 1>&2; \ 44 | echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ 45 | echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ 46 | echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ 47 | echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ 48 | echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ 49 | echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ 50 | echo "***" 1>&2; exit 1; fi) 51 | endif 52 | 53 | # If the makefile can't find QEMU, specify its path here 54 | # QEMU = qemu-system-i386 55 | 56 | # Try to infer the correct QEMU 57 | ifndef QEMU 58 | QEMU = $(shell if which qemu > /dev/null; \ 59 | then echo qemu; exit; \ 60 | elif which qemu-system-i386 > /dev/null; \ 61 | then echo qemu-system-i386; exit; \ 62 | elif which qemu-system-x86_64 > /dev/null; \ 63 | then echo qemu-system-x86_64; exit; \ 64 | else \ 65 | qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ 66 | if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ 67 | echo "***" 1>&2; \ 68 | echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ 69 | echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ 70 | echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ 71 | echo "***" 1>&2; exit 1) 72 | endif 73 | 74 | CC = $(TOOLPREFIX)gcc 75 | AS = $(TOOLPREFIX)gas 76 | LD = $(TOOLPREFIX)ld 77 | OBJCOPY = $(TOOLPREFIX)objcopy 78 | OBJDUMP = $(TOOLPREFIX)objdump 79 | CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer 80 | CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) 81 | ASFLAGS = -m32 -gdwarf-2 -Wa,-divide 82 | # FreeBSD ld wants ``elf_i386_fbsd'' 83 | LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) 84 | 85 | # Disable PIE when possible (for Ubuntu 16.10 toolchain) 86 | ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) 87 | CFLAGS += -fno-pie -no-pie 88 | endif 89 | ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) 90 | CFLAGS += -fno-pie -nopie 91 | endif 92 | 93 | xv6.img: bootblock kernel 94 | dd if=/dev/zero of=xv6.img count=10000 95 | dd if=bootblock of=xv6.img conv=notrunc 96 | dd if=kernel of=xv6.img seek=1 conv=notrunc 97 | 98 | xv6memfs.img: bootblock kernelmemfs 99 | dd if=/dev/zero of=xv6memfs.img count=10000 100 | dd if=bootblock of=xv6memfs.img conv=notrunc 101 | dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc 102 | 103 | bootblock: bootasm.S bootmain.c 104 | $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c 105 | $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S 106 | $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o 107 | $(OBJDUMP) -S bootblock.o > bootblock.asm 108 | $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock 109 | ./sign.pl bootblock 110 | 111 | entryother: entryother.S 112 | $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S 113 | $(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o 114 | $(OBJCOPY) -S -O binary -j .text bootblockother.o entryother 115 | $(OBJDUMP) -S bootblockother.o > entryother.asm 116 | 117 | initcode: initcode.S 118 | $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S 119 | $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o 120 | $(OBJCOPY) -S -O binary initcode.out initcode 121 | $(OBJDUMP) -S initcode.o > initcode.asm 122 | 123 | kernel: $(OBJS) entry.o entryother initcode kernel.ld 124 | $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother 125 | $(OBJDUMP) -S kernel > kernel.asm 126 | $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym 127 | 128 | # kernelmemfs is a copy of kernel that maintains the 129 | # disk image in memory instead of writing to a disk. 130 | # This is not so useful for testing persistent storage or 131 | # exploring disk buffering implementations, but it is 132 | # great for testing the kernel on real hardware without 133 | # needing a scratch disk. 134 | MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o 135 | kernelmemfs: $(MEMFSOBJS) entry.o entryother initcode kernel.ld fs.img 136 | $(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o $(MEMFSOBJS) -b binary initcode entryother fs.img 137 | $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm 138 | $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym 139 | 140 | tags: $(OBJS) entryother.S _init 141 | etags *.S *.c 142 | 143 | vectors.S: vectors.pl 144 | ./vectors.pl > vectors.S 145 | 146 | ULIB = ulib.o usys.o printf.o umalloc.o 147 | 148 | _%: %.o $(ULIB) 149 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ 150 | $(OBJDUMP) -S $@ > $*.asm 151 | $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym 152 | 153 | mkfs: mkfs.c fs.h 154 | gcc -Werror -Wall -o mkfs mkfs.c 155 | 156 | # Prevent deletion of intermediate files, e.g. cat.o, after first build, so 157 | # that disk image changes after first build are persistent until clean. More 158 | # details: 159 | # http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html 160 | .PRECIOUS: %.o 161 | 162 | UPROGS=\ 163 | _cat\ 164 | _echo\ 165 | _grep\ 166 | _init\ 167 | _kill\ 168 | _ln\ 169 | _ls\ 170 | _mkdir\ 171 | _rm\ 172 | _sh\ 173 | _stressfs\ 174 | _usertests\ 175 | _wc\ 176 | _zombie\ 177 | _testthreads\ 178 | 179 | fs.img: mkfs README $(UPROGS) 180 | ./mkfs fs.img README $(UPROGS) 181 | 182 | -include *.d 183 | 184 | clean: 185 | rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ 186 | *.o *.d *.asm *.sym vectors.S bootblock entryother \ 187 | initcode initcode.out kernel xv6.img fs.img kernelmemfs \ 188 | xv6memfs.img mkfs .gdbinit \ 189 | $(UPROGS) 190 | 191 | # make a printout 192 | FILES = $(shell grep -v '^\#' runoff.list) 193 | PRINT = runoff.list runoff.spec README toc.hdr toc.ftr $(FILES) 194 | 195 | xv6.pdf: $(PRINT) 196 | ./runoff 197 | ls -l xv6.pdf 198 | 199 | print: xv6.pdf 200 | 201 | # run in emulators 202 | 203 | bochs : fs.img xv6.img 204 | if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi 205 | bochs -q 206 | 207 | # try to generate a unique GDB port 208 | GDBPORT = $(shell expr `id -u` % 5000 + 25000) 209 | # QEMU's gdb stub command line changed in 0.11 210 | QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ 211 | then echo "-gdb tcp::$(GDBPORT)"; \ 212 | else echo "-s -p $(GDBPORT)"; fi) 213 | ifndef CPUS 214 | CPUS := 2 215 | endif 216 | QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA) 217 | 218 | qemu: fs.img xv6.img 219 | $(QEMU) -serial mon:stdio $(QEMUOPTS) 220 | 221 | qemu-memfs: xv6memfs.img 222 | $(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256 223 | 224 | qemu-nox: fs.img xv6.img 225 | $(QEMU) -nographic $(QEMUOPTS) 226 | 227 | .gdbinit: .gdbinit.tmpl 228 | sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ 229 | 230 | qemu-gdb: fs.img xv6.img .gdbinit 231 | @echo "*** Now run 'gdb'." 1>&2 232 | $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) 233 | 234 | qemu-nox-gdb: fs.img xv6.img .gdbinit 235 | @echo "*** Now run 'gdb'." 1>&2 236 | $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) 237 | 238 | # CUT HERE 239 | # prepare dist for students 240 | # after running make dist, probably want to 241 | # rename it to rev0 or rev1 or so on and then 242 | # check in that version. 243 | 244 | EXTRA=\ 245 | mkfs.c ulib.c user.h cat.c echo.c grep.c kill.c\ 246 | ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\ 247 | printf.c umalloc.c testthreads.c\ 248 | README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\ 249 | .gdbinit.tmpl gdbutil\ 250 | 251 | dist: 252 | rm -rf dist 253 | mkdir dist 254 | for i in $(FILES); \ 255 | do \ 256 | grep -v PAGEBREAK $$i >dist/$$i; \ 257 | done 258 | sed '/CUT HERE/,$$d' Makefile >dist/Makefile 259 | echo >dist/runoff.spec 260 | cp $(EXTRA) dist 261 | 262 | dist-test: 263 | rm -rf dist 264 | make dist 265 | rm -rf dist-test 266 | mkdir dist-test 267 | cp dist/* dist-test 268 | cd dist-test; $(MAKE) print 269 | cd dist-test; $(MAKE) bochs || true 270 | cd dist-test; $(MAKE) qemu 271 | 272 | # update this rule (change rev#) when it is time to 273 | # make a new revision. 274 | tar: 275 | rm -rf /tmp/xv6 276 | mkdir -p /tmp/xv6 277 | cp dist/* dist/.gdbinit.tmpl /tmp/xv6 278 | (cd /tmp; tar cf - xv6) | gzip >xv6-rev10.tar.gz # the next one will be 10 (9/17) 279 | 280 | .PHONY: dist-test dist 281 | -------------------------------------------------------------------------------- /Notes: -------------------------------------------------------------------------------- 1 | bochs 2.2.6: 2 | ./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae --disable-reset-on-triple-fault 3 | bochs CVS after 2.2.6: 4 | ./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae 5 | 6 | bootmain.c doesn't work right if the ELF sections aren't 7 | sector-aligned. so you can't use ld -N. and the sections may also need 8 | to be non-zero length, only really matters for tiny "kernels". 9 | 10 | kernel loaded at 1 megabyte. stack same place that bootasm.S left it. 11 | 12 | kinit() should find real mem size 13 | and rescue useable memory below 1 meg 14 | 15 | no paging, no use of page table hardware, just segments 16 | 17 | no user area: no magic kernel stack mapping 18 | so no copying of kernel stack during fork 19 | though there is a kernel stack page for each process 20 | 21 | no kernel malloc(), just kalloc() for user core 22 | 23 | user pointers aren't valid in the kernel 24 | 25 | are interrupts turned on in the kernel? yes. 26 | 27 | pass curproc explicitly, or implicit from cpu #? 28 | e.g. argument to newproc()? 29 | hmm, you need a global curproc[cpu] for trap() &c 30 | 31 | no stack expansion 32 | 33 | test running out of memory, process slots 34 | 35 | we can't really use a separate stack segment, since stack addresses 36 | need to work correctly as ordinary pointers. the same may be true of 37 | data vs text. how can we have a gap between data and stack, so that 38 | both can grow, without committing 4GB of physical memory? does this 39 | mean we need paging? 40 | 41 | perhaps have fixed-size stack, put it in the data segment? 42 | 43 | oops, if kernel stack is in contiguous user phys mem, then moving 44 | users' memory (e.g. to expand it) will wreck any pointers into the 45 | kernel stack. 46 | 47 | do we need to set fs and gs? so user processes can't abuse them? 48 | 49 | setupsegs() may modify current segment table, is that legal? 50 | 51 | trap() ought to lgdt on return, since currently only done in swtch() 52 | 53 | protect hardware interrupt vectors from user INT instructions? 54 | 55 | test out-of-fd cases for creating pipe. 56 | test pipe reader closes then write 57 | test two readers, two writers. 58 | test children being inherited by grandparent &c 59 | 60 | some sleep()s should be interruptible by kill() 61 | 62 | locks 63 | init_lock 64 | sequences CPU startup 65 | proc_table_lock 66 | also protects next_pid 67 | per-fd lock *just* protects count read-modify-write 68 | also maybe freeness? 69 | memory allocator 70 | printf 71 | 72 | in general, the table locks protect both free-ness and 73 | public variables of table elements 74 | in many cases you can use table elements w/o a lock 75 | e.g. if you are the process, or you are using an fd 76 | 77 | lock order 78 | per-pipe lock 79 | proc_table_lock fd_table_lock kalloc_lock 80 | console_lock 81 | 82 | do you have to be holding the mutex in order to call wakeup()? yes 83 | 84 | device interrupts don't clear FL_IF 85 | so a recursive timer interrupt is possible 86 | 87 | what does inode->busy mean? 88 | might be held across disk reads 89 | no-one is allowed to do anything to the inode 90 | protected by inode_table_lock 91 | inode->count counts in-memory pointers to the struct 92 | prevents inode[] element from being re-used 93 | protected by inode_table_lock 94 | 95 | blocks and inodes have ad-hoc sleep-locks 96 | provide a single mechanism? 97 | 98 | kalloc() can return 0; do callers handle this right? 99 | 100 | test: one process unlinks a file while another links to it 101 | test: one process opens a file while another deletes it 102 | test: deadlock d/.. vs ../d, two processes. 103 | test: dup() shared fd->off 104 | test: does echo foo > x truncate x? 105 | 106 | sh: ioredirection incorrect now we have pipes 107 | sh: chain of pipes won't work, also ugly that parent closes fdarray entries too 108 | sh: dynamic memory allocation? 109 | sh: should sh support ; () & 110 | sh: stop stdin on ctrl-d (for cat > y) 111 | 112 | really should have bdwrite() for file content 113 | and make some inode updates async 114 | so soft updates make sense 115 | 116 | disk scheduling 117 | echo foo > bar should truncate bar 118 | so O_CREATE should not truncate 119 | but O_TRUNC should 120 | 121 | make it work on a real machine 122 | release before acquire at end of sleep? 123 | check 2nd disk (i.e. if not in .bochsrc) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv6-threads 2 | 3 | This is a copy of the [xv6 repository](https://github.com/mit-pdos/xv6-public) which implements real kernel threads in xv6, including the addition of clone and join functions which create the foundation for a thread library that defines the thread_create, thread_join, lock_init, lock_acquire, and lock_release functions. 4 | 5 | This project was assigned in COSC361 Operating Systems taught by Dr. Micah Beck, as an assignment from the [OSTEP](https://github.com/remzi-arpacidusseau/ostep-projects) textbook. 6 | 7 | ## Changes to implement feature 8 | 9 | ### Makefile 10 | The makefile had to be edited to add the new user programs to test the creation of threads and the concurrent execution of code. The forktest program had to be removed to get rid of errors involving malloc. 11 | 12 | ### defs.h 13 | The declarations for clone and join were created in this file. 14 | 15 | ### proc.c 16 | The code definitions for clone and join were added to this file. The clone function sets up a new process with the given stack arguments, and the join function scans the process table looking for a zombie child and clears them out. 17 | 18 | ### proc.h 19 | A new property was added to the process data structure to mark the address of the thread stack, titled *threadstack. 20 | 21 | ### syscall.c 22 | The declarations for the new functions sys_clone() and sys_join() were added to this file. 23 | 24 | ### syscall.h 25 | This system call numbers were assigned to the new functions sys_clone() and sys_join(). 26 | 27 | ### sysproc.c 28 | This file contains the definitions of the sys_clone() and sys_join() functions which call the definitions in proc.c. 29 | 30 | ### user.h 31 | The declarations for the new system calls clone() and join() were added to this file, as well as the new functions thread_create(), thread_join(), lock_init(), lock_acquire(), and lock_release(). A new structure was created to define a lock. 32 | 33 | ### ulib.c 34 | The definitions for the new functions thread_create(), thread_join(), lock_init(), lock_acquire(), and lock_release() were added to this file. 35 | 36 | ### usys.S 37 | The declarations for the new functions clone() and join() were added to this file. 38 | 39 | ### testthreads.c 40 | This user program tests the creation of three different threads and their usage of locks to ensure concurrency is working and thread safety is achieved via locks. 41 | -------------------------------------------------------------------------------- /TRICKS: -------------------------------------------------------------------------------- 1 | This file lists subtle things that might not be commented 2 | as well as they should be in the source code and that 3 | might be worth pointing out in a longer explanation or in class. 4 | 5 | --- 6 | 7 | [2009/07/12: No longer relevant; forkret1 changed 8 | and this is now cleaner.] 9 | 10 | forkret1 in trapasm.S is called with a tf argument. 11 | In order to use it, forkret1 copies the tf pointer into 12 | %esp and then jumps to trapret, which pops the 13 | register state out of the trap frame. If an interrupt 14 | came in between the mov tf, %esp and the iret that 15 | goes back out to user space, the interrupt stack frame 16 | would end up scribbling over the tf and whatever memory 17 | lay under it. 18 | 19 | Why is this safe? Because forkret1 is only called 20 | the first time a process returns to user space, and 21 | at that point, cp->tf is set to point to a trap frame 22 | constructed at the top of cp's kernel stack. So tf 23 | *is* a valid %esp that can hold interrupt state. 24 | 25 | If other tf's were used in forkret1, we could add 26 | a cli before the mov tf, %esp. 27 | 28 | --- 29 | 30 | In pushcli, must cli() no matter what. It is not safe to do 31 | 32 | if(cpus[cpu()].ncli == 0) 33 | cli(); 34 | cpus[cpu()].ncli++; 35 | 36 | because if interrupts are off then we might call cpu(), get 37 | rescheduled to a different cpu, look at cpus[oldcpu].ncli, 38 | and wrongly decide not to disable interrupts on the new cpu. 39 | 40 | Instead do 41 | 42 | cli(); 43 | cpus[cpu()].ncli++; 44 | 45 | always. 46 | 47 | --- 48 | 49 | There is a (harmless) race in pushcli, which does 50 | 51 | eflags = readeflags(); 52 | cli(); 53 | if(c->ncli++ == 0) 54 | c->intena = eflags & FL_IF; 55 | 56 | Consider a bottom-level pushcli. 57 | If interrupts are disabled already, then the right thing 58 | happens: read_eflags finds that FL_IF is not set, 59 | and intena = 0. If interrupts are enabled, then 60 | it is less clear that the right thing happens: 61 | the readeflags can execute, then the process 62 | can get preempted and rescheduled on another cpu, 63 | and then once it starts running, perhaps with 64 | interrupts disabled (can happen since the scheduler 65 | only enables interrupts once per scheduling loop, 66 | not every time it schedules a process), it will 67 | incorrectly record that interrupts *were* enabled. 68 | This doesn't matter, because if it was safe to be 69 | running with interrupts enabled before the context 70 | switch, it is still safe (and arguably more correct) 71 | to run with them enabled after the context switch too. 72 | 73 | In fact it would be safe if scheduler always set 74 | c->intena = 1; 75 | before calling swtch, and perhaps it should. 76 | 77 | --- 78 | 79 | The x86's processor-ordering memory model 80 | matches spin locks well, so no explicit memory 81 | synchronization instructions are required in 82 | acquire and release. 83 | 84 | Consider two sequences of code on different CPUs: 85 | 86 | CPU0 87 | A; 88 | release(lk); 89 | 90 | and 91 | 92 | CPU1 93 | acquire(lk); 94 | B; 95 | 96 | We want to make sure that: 97 | - all reads in B see the effects of writes in A. 98 | - all reads in A do *not* see the effects of writes in B. 99 | 100 | The x86 guarantees that writes in A will go out 101 | to memory before the write of lk->locked = 0 in 102 | release(lk). It further guarantees that CPU1 103 | will observe CPU0's write of lk->locked = 0 only 104 | after observing the earlier writes by CPU0. 105 | So any reads in B are guaranteed to observe the 106 | effects of writes in A. 107 | 108 | According to the Intel manual behavior spec, the 109 | second condition requires a serialization instruction 110 | in release, to avoid reads in A happening after giving 111 | up lk. No Intel SMP processor in existence actually 112 | moves reads down after writes, but the language in 113 | the spec allows it. There is no telling whether future 114 | processors will need it. 115 | 116 | --- 117 | 118 | The code in fork needs to read np->pid before 119 | setting np->state to RUNNABLE. The following 120 | is not a correct way to do this: 121 | 122 | int 123 | fork(void) 124 | { 125 | ... 126 | np->state = RUNNABLE; 127 | return np->pid; // oops 128 | } 129 | 130 | After setting np->state to RUNNABLE, some other CPU 131 | might run the process, it might exit, and then it might 132 | get reused for a different process (with a new pid), all 133 | before the return statement. So it's not safe to just 134 | "return np->pid". Even saving a copy of np->pid before 135 | setting np->state isn't safe, since the compiler is 136 | allowed to re-order statements. 137 | 138 | The real code saves a copy of np->pid, then acquires a lock 139 | around the write to np->state. The acquire() prevents the 140 | compiler from re-ordering. 141 | -------------------------------------------------------------------------------- /asm.h: -------------------------------------------------------------------------------- 1 | // 2 | // assembler macros to create x86 segments 3 | // 4 | 5 | #define SEG_NULLASM \ 6 | .word 0, 0; \ 7 | .byte 0, 0, 0, 0 8 | 9 | // The 0xC0 means the limit is in 4096-byte units 10 | // and (for executable segments) 32-bit mode. 11 | #define SEG_ASM(type,base,lim) \ 12 | .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ 13 | .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ 14 | (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) 15 | 16 | #define STA_X 0x8 // Executable segment 17 | #define STA_W 0x2 // Writeable (non-executable segments) 18 | #define STA_R 0x2 // Readable (executable segments) 19 | -------------------------------------------------------------------------------- /bio.c: -------------------------------------------------------------------------------- 1 | // Buffer cache. 2 | // 3 | // The buffer cache is a linked list of buf structures holding 4 | // cached copies of disk block contents. Caching disk blocks 5 | // in memory reduces the number of disk reads and also provides 6 | // a synchronization point for disk blocks used by multiple processes. 7 | // 8 | // Interface: 9 | // * To get a buffer for a particular disk block, call bread. 10 | // * After changing buffer data, call bwrite to write it to disk. 11 | // * When done with the buffer, call brelse. 12 | // * Do not use the buffer after calling brelse. 13 | // * Only one process at a time can use a buffer, 14 | // so do not keep them longer than necessary. 15 | // 16 | // The implementation uses two state flags internally: 17 | // * B_VALID: the buffer data has been read from the disk. 18 | // * B_DIRTY: the buffer data has been modified 19 | // and needs to be written to disk. 20 | 21 | #include "types.h" 22 | #include "defs.h" 23 | #include "param.h" 24 | #include "spinlock.h" 25 | #include "sleeplock.h" 26 | #include "fs.h" 27 | #include "buf.h" 28 | 29 | struct { 30 | struct spinlock lock; 31 | struct buf buf[NBUF]; 32 | 33 | // Linked list of all buffers, through prev/next. 34 | // head.next is most recently used. 35 | struct buf head; 36 | } bcache; 37 | 38 | void 39 | binit(void) 40 | { 41 | struct buf *b; 42 | 43 | initlock(&bcache.lock, "bcache"); 44 | 45 | //PAGEBREAK! 46 | // Create linked list of buffers 47 | bcache.head.prev = &bcache.head; 48 | bcache.head.next = &bcache.head; 49 | for(b = bcache.buf; b < bcache.buf+NBUF; b++){ 50 | b->next = bcache.head.next; 51 | b->prev = &bcache.head; 52 | initsleeplock(&b->lock, "buffer"); 53 | bcache.head.next->prev = b; 54 | bcache.head.next = b; 55 | } 56 | } 57 | 58 | // Look through buffer cache for block on device dev. 59 | // If not found, allocate a buffer. 60 | // In either case, return locked buffer. 61 | static struct buf* 62 | bget(uint dev, uint blockno) 63 | { 64 | struct buf *b; 65 | 66 | acquire(&bcache.lock); 67 | 68 | // Is the block already cached? 69 | for(b = bcache.head.next; b != &bcache.head; b = b->next){ 70 | if(b->dev == dev && b->blockno == blockno){ 71 | b->refcnt++; 72 | release(&bcache.lock); 73 | acquiresleep(&b->lock); 74 | return b; 75 | } 76 | } 77 | 78 | // Not cached; recycle an unused buffer. 79 | // Even if refcnt==0, B_DIRTY indicates a buffer is in use 80 | // because log.c has modified it but not yet committed it. 81 | for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ 82 | if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) { 83 | b->dev = dev; 84 | b->blockno = blockno; 85 | b->flags = 0; 86 | b->refcnt = 1; 87 | release(&bcache.lock); 88 | acquiresleep(&b->lock); 89 | return b; 90 | } 91 | } 92 | panic("bget: no buffers"); 93 | } 94 | 95 | // Return a locked buf with the contents of the indicated block. 96 | struct buf* 97 | bread(uint dev, uint blockno) 98 | { 99 | struct buf *b; 100 | 101 | b = bget(dev, blockno); 102 | if((b->flags & B_VALID) == 0) { 103 | iderw(b); 104 | } 105 | return b; 106 | } 107 | 108 | // Write b's contents to disk. Must be locked. 109 | void 110 | bwrite(struct buf *b) 111 | { 112 | if(!holdingsleep(&b->lock)) 113 | panic("bwrite"); 114 | b->flags |= B_DIRTY; 115 | iderw(b); 116 | } 117 | 118 | // Release a locked buffer. 119 | // Move to the head of the MRU list. 120 | void 121 | brelse(struct buf *b) 122 | { 123 | if(!holdingsleep(&b->lock)) 124 | panic("brelse"); 125 | 126 | releasesleep(&b->lock); 127 | 128 | acquire(&bcache.lock); 129 | b->refcnt--; 130 | if (b->refcnt == 0) { 131 | // no one is waiting for it. 132 | b->next->prev = b->prev; 133 | b->prev->next = b->next; 134 | b->next = bcache.head.next; 135 | b->prev = &bcache.head; 136 | bcache.head.next->prev = b; 137 | bcache.head.next = b; 138 | } 139 | 140 | release(&bcache.lock); 141 | } 142 | //PAGEBREAK! 143 | // Blank page. 144 | 145 | -------------------------------------------------------------------------------- /bootasm.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | #include "memlayout.h" 3 | #include "mmu.h" 4 | 5 | # Start the first CPU: switch to 32-bit protected mode, jump into C. 6 | # The BIOS loads this code from the first sector of the hard disk into 7 | # memory at physical address 0x7c00 and starts executing in real mode 8 | # with %cs=0 %ip=7c00. 9 | 10 | .code16 # Assemble for 16-bit mode 11 | .globl start 12 | start: 13 | cli # BIOS enabled interrupts; disable 14 | 15 | # Zero data segment registers DS, ES, and SS. 16 | xorw %ax,%ax # Set %ax to zero 17 | movw %ax,%ds # -> Data Segment 18 | movw %ax,%es # -> Extra Segment 19 | movw %ax,%ss # -> Stack Segment 20 | 21 | # Physical address line A20 is tied to zero so that the first PCs 22 | # with 2 MB would run software that assumed 1 MB. Undo that. 23 | seta20.1: 24 | inb $0x64,%al # Wait for not busy 25 | testb $0x2,%al 26 | jnz seta20.1 27 | 28 | movb $0xd1,%al # 0xd1 -> port 0x64 29 | outb %al,$0x64 30 | 31 | seta20.2: 32 | inb $0x64,%al # Wait for not busy 33 | testb $0x2,%al 34 | jnz seta20.2 35 | 36 | movb $0xdf,%al # 0xdf -> port 0x60 37 | outb %al,$0x60 38 | 39 | # Switch from real to protected mode. Use a bootstrap GDT that makes 40 | # virtual addresses map directly to physical addresses so that the 41 | # effective memory map doesn't change during the transition. 42 | lgdt gdtdesc 43 | movl %cr0, %eax 44 | orl $CR0_PE, %eax 45 | movl %eax, %cr0 46 | 47 | //PAGEBREAK! 48 | # Complete the transition to 32-bit protected mode by using a long jmp 49 | # to reload %cs and %eip. The segment descriptors are set up with no 50 | # translation, so that the mapping is still the identity mapping. 51 | ljmp $(SEG_KCODE<<3), $start32 52 | 53 | .code32 # Tell assembler to generate 32-bit code now. 54 | start32: 55 | # Set up the protected-mode data segment registers 56 | movw $(SEG_KDATA<<3), %ax # Our data segment selector 57 | movw %ax, %ds # -> DS: Data Segment 58 | movw %ax, %es # -> ES: Extra Segment 59 | movw %ax, %ss # -> SS: Stack Segment 60 | movw $0, %ax # Zero segments not ready for use 61 | movw %ax, %fs # -> FS 62 | movw %ax, %gs # -> GS 63 | 64 | # Set up the stack pointer and call into C. 65 | movl $start, %esp 66 | call bootmain 67 | 68 | # If bootmain returns (it shouldn't), trigger a Bochs 69 | # breakpoint if running under Bochs, then loop. 70 | movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 71 | movw %ax, %dx 72 | outw %ax, %dx 73 | movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 74 | outw %ax, %dx 75 | spin: 76 | jmp spin 77 | 78 | # Bootstrap GDT 79 | .p2align 2 # force 4 byte alignment 80 | gdt: 81 | SEG_NULLASM # null seg 82 | SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 83 | SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg 84 | 85 | gdtdesc: 86 | .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 87 | .long gdt # address gdt 88 | 89 | -------------------------------------------------------------------------------- /bootmain.c: -------------------------------------------------------------------------------- 1 | // Boot loader. 2 | // 3 | // Part of the boot block, along with bootasm.S, which calls bootmain(). 4 | // bootasm.S has put the processor into protected 32-bit mode. 5 | // bootmain() loads an ELF kernel image from the disk starting at 6 | // sector 1 and then jumps to the kernel entry routine. 7 | 8 | #include "types.h" 9 | #include "elf.h" 10 | #include "x86.h" 11 | #include "memlayout.h" 12 | 13 | #define SECTSIZE 512 14 | 15 | void readseg(uchar*, uint, uint); 16 | 17 | void 18 | bootmain(void) 19 | { 20 | struct elfhdr *elf; 21 | struct proghdr *ph, *eph; 22 | void (*entry)(void); 23 | uchar* pa; 24 | 25 | elf = (struct elfhdr*)0x10000; // scratch space 26 | 27 | // Read 1st page off disk 28 | readseg((uchar*)elf, 4096, 0); 29 | 30 | // Is this an ELF executable? 31 | if(elf->magic != ELF_MAGIC) 32 | return; // let bootasm.S handle error 33 | 34 | // Load each program segment (ignores ph flags). 35 | ph = (struct proghdr*)((uchar*)elf + elf->phoff); 36 | eph = ph + elf->phnum; 37 | for(; ph < eph; ph++){ 38 | pa = (uchar*)ph->paddr; 39 | readseg(pa, ph->filesz, ph->off); 40 | if(ph->memsz > ph->filesz) 41 | stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz); 42 | } 43 | 44 | // Call the entry point from the ELF header. 45 | // Does not return! 46 | entry = (void(*)(void))(elf->entry); 47 | entry(); 48 | } 49 | 50 | void 51 | waitdisk(void) 52 | { 53 | // Wait for disk ready. 54 | while((inb(0x1F7) & 0xC0) != 0x40) 55 | ; 56 | } 57 | 58 | // Read a single sector at offset into dst. 59 | void 60 | readsect(void *dst, uint offset) 61 | { 62 | // Issue command. 63 | waitdisk(); 64 | outb(0x1F2, 1); // count = 1 65 | outb(0x1F3, offset); 66 | outb(0x1F4, offset >> 8); 67 | outb(0x1F5, offset >> 16); 68 | outb(0x1F6, (offset >> 24) | 0xE0); 69 | outb(0x1F7, 0x20); // cmd 0x20 - read sectors 70 | 71 | // Read data. 72 | waitdisk(); 73 | insl(0x1F0, dst, SECTSIZE/4); 74 | } 75 | 76 | // Read 'count' bytes at 'offset' from kernel into physical address 'pa'. 77 | // Might copy more than asked. 78 | void 79 | readseg(uchar* pa, uint count, uint offset) 80 | { 81 | uchar* epa; 82 | 83 | epa = pa + count; 84 | 85 | // Round down to sector boundary. 86 | pa -= offset % SECTSIZE; 87 | 88 | // Translate from bytes to sectors; kernel starts at sector 1. 89 | offset = (offset / SECTSIZE) + 1; 90 | 91 | // If this is too slow, we could read lots of sectors at a time. 92 | // We'd write more to memory than asked, but it doesn't matter -- 93 | // we load in increasing order. 94 | for(; pa < epa; pa += SECTSIZE, offset++) 95 | readsect(pa, offset); 96 | } 97 | -------------------------------------------------------------------------------- /buf.h: -------------------------------------------------------------------------------- 1 | struct buf { 2 | int flags; 3 | uint dev; 4 | uint blockno; 5 | struct sleeplock lock; 6 | uint refcnt; 7 | struct buf *prev; // LRU cache list 8 | struct buf *next; 9 | struct buf *qnext; // disk queue 10 | uchar data[BSIZE]; 11 | }; 12 | #define B_VALID 0x2 // buffer has been read from disk 13 | #define B_DIRTY 0x4 // buffer needs to be written to disk 14 | 15 | -------------------------------------------------------------------------------- /cat.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | char buf[512]; 6 | 7 | void 8 | cat(int fd) 9 | { 10 | int n; 11 | 12 | while((n = read(fd, buf, sizeof(buf))) > 0) { 13 | if (write(1, buf, n) != n) { 14 | printf(1, "cat: write error\n"); 15 | exit(); 16 | } 17 | } 18 | if(n < 0){ 19 | printf(1, "cat: read error\n"); 20 | exit(); 21 | } 22 | } 23 | 24 | int 25 | main(int argc, char *argv[]) 26 | { 27 | int fd, i; 28 | 29 | if(argc <= 1){ 30 | cat(0); 31 | exit(); 32 | } 33 | 34 | for(i = 1; i < argc; i++){ 35 | if((fd = open(argv[i], 0)) < 0){ 36 | printf(1, "cat: cannot open %s\n", argv[i]); 37 | exit(); 38 | } 39 | cat(fd); 40 | close(fd); 41 | } 42 | exit(); 43 | } 44 | -------------------------------------------------------------------------------- /console.c: -------------------------------------------------------------------------------- 1 | // Console input and output. 2 | // Input is from the keyboard or serial port. 3 | // Output is written to the screen and serial port. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "traps.h" 9 | #include "spinlock.h" 10 | #include "sleeplock.h" 11 | #include "fs.h" 12 | #include "file.h" 13 | #include "memlayout.h" 14 | #include "mmu.h" 15 | #include "proc.h" 16 | #include "x86.h" 17 | 18 | static void consputc(int); 19 | 20 | static int panicked = 0; 21 | 22 | static struct { 23 | struct spinlock lock; 24 | int locking; 25 | } cons; 26 | 27 | static void 28 | printint(int xx, int base, int sign) 29 | { 30 | static char digits[] = "0123456789abcdef"; 31 | char buf[16]; 32 | int i; 33 | uint x; 34 | 35 | if(sign && (sign = xx < 0)) 36 | x = -xx; 37 | else 38 | x = xx; 39 | 40 | i = 0; 41 | do{ 42 | buf[i++] = digits[x % base]; 43 | }while((x /= base) != 0); 44 | 45 | if(sign) 46 | buf[i++] = '-'; 47 | 48 | while(--i >= 0) 49 | consputc(buf[i]); 50 | } 51 | //PAGEBREAK: 50 52 | 53 | // Print to the console. only understands %d, %x, %p, %s. 54 | void 55 | cprintf(char *fmt, ...) 56 | { 57 | int i, c, locking; 58 | uint *argp; 59 | char *s; 60 | 61 | locking = cons.locking; 62 | if(locking) 63 | acquire(&cons.lock); 64 | 65 | if (fmt == 0) 66 | panic("null fmt"); 67 | 68 | argp = (uint*)(void*)(&fmt + 1); 69 | for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ 70 | if(c != '%'){ 71 | consputc(c); 72 | continue; 73 | } 74 | c = fmt[++i] & 0xff; 75 | if(c == 0) 76 | break; 77 | switch(c){ 78 | case 'd': 79 | printint(*argp++, 10, 1); 80 | break; 81 | case 'x': 82 | case 'p': 83 | printint(*argp++, 16, 0); 84 | break; 85 | case 's': 86 | if((s = (char*)*argp++) == 0) 87 | s = "(null)"; 88 | for(; *s; s++) 89 | consputc(*s); 90 | break; 91 | case '%': 92 | consputc('%'); 93 | break; 94 | default: 95 | // Print unknown % sequence to draw attention. 96 | consputc('%'); 97 | consputc(c); 98 | break; 99 | } 100 | } 101 | 102 | if(locking) 103 | release(&cons.lock); 104 | } 105 | 106 | void 107 | panic(char *s) 108 | { 109 | int i; 110 | uint pcs[10]; 111 | 112 | cli(); 113 | cons.locking = 0; 114 | // use lapiccpunum so that we can call panic from mycpu() 115 | cprintf("lapicid %d: panic: ", lapicid()); 116 | cprintf(s); 117 | cprintf("\n"); 118 | getcallerpcs(&s, pcs); 119 | for(i=0; i<10; i++) 120 | cprintf(" %p", pcs[i]); 121 | panicked = 1; // freeze other CPU 122 | for(;;) 123 | ; 124 | } 125 | 126 | //PAGEBREAK: 50 127 | #define BACKSPACE 0x100 128 | #define CRTPORT 0x3d4 129 | static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory 130 | 131 | static void 132 | cgaputc(int c) 133 | { 134 | int pos; 135 | 136 | // Cursor position: col + 80*row. 137 | outb(CRTPORT, 14); 138 | pos = inb(CRTPORT+1) << 8; 139 | outb(CRTPORT, 15); 140 | pos |= inb(CRTPORT+1); 141 | 142 | if(c == '\n') 143 | pos += 80 - pos%80; 144 | else if(c == BACKSPACE){ 145 | if(pos > 0) --pos; 146 | } else 147 | crt[pos++] = (c&0xff) | 0x0700; // black on white 148 | 149 | if(pos < 0 || pos > 25*80) 150 | panic("pos under/overflow"); 151 | 152 | if((pos/80) >= 24){ // Scroll up. 153 | memmove(crt, crt+80, sizeof(crt[0])*23*80); 154 | pos -= 80; 155 | memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos)); 156 | } 157 | 158 | outb(CRTPORT, 14); 159 | outb(CRTPORT+1, pos>>8); 160 | outb(CRTPORT, 15); 161 | outb(CRTPORT+1, pos); 162 | crt[pos] = ' ' | 0x0700; 163 | } 164 | 165 | void 166 | consputc(int c) 167 | { 168 | if(panicked){ 169 | cli(); 170 | for(;;) 171 | ; 172 | } 173 | 174 | if(c == BACKSPACE){ 175 | uartputc('\b'); uartputc(' '); uartputc('\b'); 176 | } else 177 | uartputc(c); 178 | cgaputc(c); 179 | } 180 | 181 | #define INPUT_BUF 128 182 | struct { 183 | char buf[INPUT_BUF]; 184 | uint r; // Read index 185 | uint w; // Write index 186 | uint e; // Edit index 187 | } input; 188 | 189 | #define C(x) ((x)-'@') // Control-x 190 | 191 | void 192 | consoleintr(int (*getc)(void)) 193 | { 194 | int c, doprocdump = 0; 195 | 196 | acquire(&cons.lock); 197 | while((c = getc()) >= 0){ 198 | switch(c){ 199 | case C('P'): // Process listing. 200 | // procdump() locks cons.lock indirectly; invoke later 201 | doprocdump = 1; 202 | break; 203 | case C('U'): // Kill line. 204 | while(input.e != input.w && 205 | input.buf[(input.e-1) % INPUT_BUF] != '\n'){ 206 | input.e--; 207 | consputc(BACKSPACE); 208 | } 209 | break; 210 | case C('H'): case '\x7f': // Backspace 211 | if(input.e != input.w){ 212 | input.e--; 213 | consputc(BACKSPACE); 214 | } 215 | break; 216 | default: 217 | if(c != 0 && input.e-input.r < INPUT_BUF){ 218 | c = (c == '\r') ? '\n' : c; 219 | input.buf[input.e++ % INPUT_BUF] = c; 220 | consputc(c); 221 | if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ 222 | input.w = input.e; 223 | wakeup(&input.r); 224 | } 225 | } 226 | break; 227 | } 228 | } 229 | release(&cons.lock); 230 | if(doprocdump) { 231 | procdump(); // now call procdump() wo. cons.lock held 232 | } 233 | } 234 | 235 | int 236 | consoleread(struct inode *ip, char *dst, int n) 237 | { 238 | uint target; 239 | int c; 240 | 241 | iunlock(ip); 242 | target = n; 243 | acquire(&cons.lock); 244 | while(n > 0){ 245 | while(input.r == input.w){ 246 | if(myproc()->killed){ 247 | release(&cons.lock); 248 | ilock(ip); 249 | return -1; 250 | } 251 | sleep(&input.r, &cons.lock); 252 | } 253 | c = input.buf[input.r++ % INPUT_BUF]; 254 | if(c == C('D')){ // EOF 255 | if(n < target){ 256 | // Save ^D for next time, to make sure 257 | // caller gets a 0-byte result. 258 | input.r--; 259 | } 260 | break; 261 | } 262 | *dst++ = c; 263 | --n; 264 | if(c == '\n') 265 | break; 266 | } 267 | release(&cons.lock); 268 | ilock(ip); 269 | 270 | return target - n; 271 | } 272 | 273 | int 274 | consolewrite(struct inode *ip, char *buf, int n) 275 | { 276 | int i; 277 | 278 | iunlock(ip); 279 | acquire(&cons.lock); 280 | for(i = 0; i < n; i++) 281 | consputc(buf[i] & 0xff); 282 | release(&cons.lock); 283 | ilock(ip); 284 | 285 | return n; 286 | } 287 | 288 | void 289 | consoleinit(void) 290 | { 291 | initlock(&cons.lock, "console"); 292 | 293 | devsw[CONSOLE].write = consolewrite; 294 | devsw[CONSOLE].read = consoleread; 295 | cons.locking = 1; 296 | 297 | ioapicenable(IRQ_KBD, 0); 298 | } 299 | 300 | -------------------------------------------------------------------------------- /cuth: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | $| = 1; 4 | 5 | sub writefile($@){ 6 | my ($file, @lines) = @_; 7 | 8 | sleep(1); 9 | open(F, ">$file") || die "open >$file: $!"; 10 | print F @lines; 11 | close(F); 12 | } 13 | 14 | # Cut out #include lines that don't contribute anything. 15 | for($i=0; $i<@ARGV; $i++){ 16 | $file = $ARGV[$i]; 17 | if(!open(F, $file)){ 18 | print STDERR "open $file: $!\n"; 19 | next; 20 | } 21 | @lines = ; 22 | close(F); 23 | 24 | $obj = "$file.o"; 25 | $obj =~ s/\.c\.o$/.o/; 26 | system("touch $file"); 27 | 28 | if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){ 29 | print STDERR "make $obj failed: $rv\n"; 30 | next; 31 | } 32 | 33 | system("cp $file =$file"); 34 | for($j=@lines-1; $j>=0; $j--){ 35 | if($lines[$j] =~ /^#include/){ 36 | $old = $lines[$j]; 37 | $lines[$j] = "/* CUT-H */\n"; 38 | writefile($file, @lines); 39 | if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){ 40 | $lines[$j] = $old; 41 | }else{ 42 | print STDERR "$file $old"; 43 | } 44 | } 45 | } 46 | writefile($file, grep {!/CUT-H/} @lines); 47 | system("rm =$file"); 48 | } 49 | -------------------------------------------------------------------------------- /date.h: -------------------------------------------------------------------------------- 1 | struct rtcdate { 2 | uint second; 3 | uint minute; 4 | uint hour; 5 | uint day; 6 | uint month; 7 | uint year; 8 | }; 9 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | struct buf; 2 | struct context; 3 | struct file; 4 | struct inode; 5 | struct pipe; 6 | struct proc; 7 | struct rtcdate; 8 | struct spinlock; 9 | struct sleeplock; 10 | struct stat; 11 | struct superblock; 12 | 13 | // bio.c 14 | void binit(void); 15 | struct buf* bread(uint, uint); 16 | void brelse(struct buf*); 17 | void bwrite(struct buf*); 18 | 19 | // console.c 20 | void consoleinit(void); 21 | void cprintf(char*, ...); 22 | void consoleintr(int(*)(void)); 23 | void panic(char*) __attribute__((noreturn)); 24 | 25 | // exec.c 26 | int exec(char*, char**); 27 | 28 | // file.c 29 | struct file* filealloc(void); 30 | void fileclose(struct file*); 31 | struct file* filedup(struct file*); 32 | void fileinit(void); 33 | int fileread(struct file*, char*, int n); 34 | int filestat(struct file*, struct stat*); 35 | int filewrite(struct file*, char*, int n); 36 | 37 | // fs.c 38 | void readsb(int dev, struct superblock *sb); 39 | int dirlink(struct inode*, char*, uint); 40 | struct inode* dirlookup(struct inode*, char*, uint*); 41 | struct inode* ialloc(uint, short); 42 | struct inode* idup(struct inode*); 43 | void iinit(int dev); 44 | void ilock(struct inode*); 45 | void iput(struct inode*); 46 | void iunlock(struct inode*); 47 | void iunlockput(struct inode*); 48 | void iupdate(struct inode*); 49 | int namecmp(const char*, const char*); 50 | struct inode* namei(char*); 51 | struct inode* nameiparent(char*, char*); 52 | int readi(struct inode*, char*, uint, uint); 53 | void stati(struct inode*, struct stat*); 54 | int writei(struct inode*, char*, uint, uint); 55 | 56 | // ide.c 57 | void ideinit(void); 58 | void ideintr(void); 59 | void iderw(struct buf*); 60 | 61 | // ioapic.c 62 | void ioapicenable(int irq, int cpu); 63 | extern uchar ioapicid; 64 | void ioapicinit(void); 65 | 66 | // kalloc.c 67 | char* kalloc(void); 68 | void kfree(char*); 69 | void kinit1(void*, void*); 70 | void kinit2(void*, void*); 71 | 72 | // kbd.c 73 | void kbdintr(void); 74 | 75 | // lapic.c 76 | void cmostime(struct rtcdate *r); 77 | int lapicid(void); 78 | extern volatile uint* lapic; 79 | void lapiceoi(void); 80 | void lapicinit(void); 81 | void lapicstartap(uchar, uint); 82 | void microdelay(int); 83 | 84 | // log.c 85 | void initlog(int dev); 86 | void log_write(struct buf*); 87 | void begin_op(); 88 | void end_op(); 89 | 90 | // mp.c 91 | extern int ismp; 92 | void mpinit(void); 93 | 94 | // picirq.c 95 | void picenable(int); 96 | void picinit(void); 97 | 98 | // pipe.c 99 | int pipealloc(struct file**, struct file**); 100 | void pipeclose(struct pipe*, int); 101 | int piperead(struct pipe*, char*, int); 102 | int pipewrite(struct pipe*, char*, int); 103 | 104 | //PAGEBREAK: 16 105 | // proc.c 106 | int cpuid(void); 107 | void exit(void); 108 | int fork(void); 109 | int growproc(int); 110 | int kill(int); 111 | struct cpu* mycpu(void); 112 | struct proc* myproc(); 113 | void pinit(void); 114 | void procdump(void); 115 | void scheduler(void) __attribute__((noreturn)); 116 | void sched(void); 117 | void setproc(struct proc*); 118 | void sleep(void*, struct spinlock*); 119 | void userinit(void); 120 | int wait(void); 121 | void wakeup(void*); 122 | void yield(void); 123 | 124 | // swtch.S 125 | void swtch(struct context**, struct context*); 126 | 127 | // spinlock.c 128 | void acquire(struct spinlock*); 129 | void getcallerpcs(void*, uint*); 130 | int holding(struct spinlock*); 131 | void initlock(struct spinlock*, char*); 132 | void release(struct spinlock*); 133 | void pushcli(void); 134 | void popcli(void); 135 | 136 | // sleeplock.c 137 | void acquiresleep(struct sleeplock*); 138 | void releasesleep(struct sleeplock*); 139 | int holdingsleep(struct sleeplock*); 140 | void initsleeplock(struct sleeplock*, char*); 141 | 142 | // string.c 143 | int memcmp(const void*, const void*, uint); 144 | void* memmove(void*, const void*, uint); 145 | void* memset(void*, int, uint); 146 | char* safestrcpy(char*, const char*, int); 147 | int strlen(const char*); 148 | int strncmp(const char*, const char*, uint); 149 | char* strncpy(char*, const char*, int); 150 | 151 | // syscall.c 152 | int argint(int, int*); 153 | int argptr(int, char**, int); 154 | int argstr(int, char**); 155 | int fetchint(uint, int*); 156 | int fetchstr(uint, char**); 157 | void syscall(void); 158 | int clone(void(*fcn)(void*, void*), void*, void*, void*); 159 | int join(void**); 160 | 161 | // timer.c 162 | void timerinit(void); 163 | 164 | // trap.c 165 | void idtinit(void); 166 | extern uint ticks; 167 | void tvinit(void); 168 | extern struct spinlock tickslock; 169 | 170 | // uart.c 171 | void uartinit(void); 172 | void uartintr(void); 173 | void uartputc(int); 174 | 175 | // vm.c 176 | void seginit(void); 177 | void kvmalloc(void); 178 | pde_t* setupkvm(void); 179 | char* uva2ka(pde_t*, char*); 180 | int allocuvm(pde_t*, uint, uint); 181 | int deallocuvm(pde_t*, uint, uint); 182 | void freevm(pde_t*); 183 | void inituvm(pde_t*, char*, uint); 184 | int loaduvm(pde_t*, char*, struct inode*, uint, uint); 185 | pde_t* copyuvm(pde_t*, uint); 186 | void switchuvm(struct proc*); 187 | void switchkvm(void); 188 | int copyout(pde_t*, uint, void*, uint); 189 | void clearpteu(pde_t *pgdir, char *uva); 190 | 191 | // number of elements in fixed-size array 192 | #define NELEM(x) (sizeof(x)/sizeof((x)[0])) 193 | -------------------------------------------------------------------------------- /echo.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | for(i = 1; i < argc; i++) 11 | printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); 12 | exit(); 13 | } 14 | -------------------------------------------------------------------------------- /elf.h: -------------------------------------------------------------------------------- 1 | // Format of an ELF executable file 2 | 3 | #define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian 4 | 5 | // File header 6 | struct elfhdr { 7 | uint magic; // must equal ELF_MAGIC 8 | uchar elf[12]; 9 | ushort type; 10 | ushort machine; 11 | uint version; 12 | uint entry; 13 | uint phoff; 14 | uint shoff; 15 | uint flags; 16 | ushort ehsize; 17 | ushort phentsize; 18 | ushort phnum; 19 | ushort shentsize; 20 | ushort shnum; 21 | ushort shstrndx; 22 | }; 23 | 24 | // Program section header 25 | struct proghdr { 26 | uint type; 27 | uint off; 28 | uint vaddr; 29 | uint paddr; 30 | uint filesz; 31 | uint memsz; 32 | uint flags; 33 | uint align; 34 | }; 35 | 36 | // Values for Proghdr type 37 | #define ELF_PROG_LOAD 1 38 | 39 | // Flag bits for Proghdr flags 40 | #define ELF_PROG_FLAG_EXEC 1 41 | #define ELF_PROG_FLAG_WRITE 2 42 | #define ELF_PROG_FLAG_READ 4 43 | -------------------------------------------------------------------------------- /entry.S: -------------------------------------------------------------------------------- 1 | # The xv6 kernel starts executing in this file. This file is linked with 2 | # the kernel C code, so it can refer to kernel symbols such as main(). 3 | # The boot block (bootasm.S and bootmain.c) jumps to entry below. 4 | 5 | # Multiboot header, for multiboot boot loaders like GNU Grub. 6 | # http://www.gnu.org/software/grub/manual/multiboot/multiboot.html 7 | # 8 | # Using GRUB 2, you can boot xv6 from a file stored in a 9 | # Linux file system by copying kernel or kernelmemfs to /boot 10 | # and then adding this menu entry: 11 | # 12 | # menuentry "xv6" { 13 | # insmod ext2 14 | # set root='(hd0,msdos1)' 15 | # set kernel='/boot/kernel' 16 | # echo "Loading ${kernel}..." 17 | # multiboot ${kernel} ${kernel} 18 | # boot 19 | # } 20 | 21 | #include "asm.h" 22 | #include "memlayout.h" 23 | #include "mmu.h" 24 | #include "param.h" 25 | 26 | # Multiboot header. Data to direct multiboot loader. 27 | .p2align 2 28 | .text 29 | .globl multiboot_header 30 | multiboot_header: 31 | #define magic 0x1badb002 32 | #define flags 0 33 | .long magic 34 | .long flags 35 | .long (-magic-flags) 36 | 37 | # By convention, the _start symbol specifies the ELF entry point. 38 | # Since we haven't set up virtual memory yet, our entry point is 39 | # the physical address of 'entry'. 40 | .globl _start 41 | _start = V2P_WO(entry) 42 | 43 | # Entering xv6 on boot processor, with paging off. 44 | .globl entry 45 | entry: 46 | # Turn on page size extension for 4Mbyte pages 47 | movl %cr4, %eax 48 | orl $(CR4_PSE), %eax 49 | movl %eax, %cr4 50 | # Set page directory 51 | movl $(V2P_WO(entrypgdir)), %eax 52 | movl %eax, %cr3 53 | # Turn on paging. 54 | movl %cr0, %eax 55 | orl $(CR0_PG|CR0_WP), %eax 56 | movl %eax, %cr0 57 | 58 | # Set up the stack pointer. 59 | movl $(stack + KSTACKSIZE), %esp 60 | 61 | # Jump to main(), and switch to executing at 62 | # high addresses. The indirect call is needed because 63 | # the assembler produces a PC-relative instruction 64 | # for a direct jump. 65 | mov $main, %eax 66 | jmp *%eax 67 | 68 | .comm stack, KSTACKSIZE 69 | -------------------------------------------------------------------------------- /entryother.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | #include "memlayout.h" 3 | #include "mmu.h" 4 | 5 | # Each non-boot CPU ("AP") is started up in response to a STARTUP 6 | # IPI from the boot CPU. Section B.4.2 of the Multi-Processor 7 | # Specification says that the AP will start in real mode with CS:IP 8 | # set to XY00:0000, where XY is an 8-bit value sent with the 9 | # STARTUP. Thus this code must start at a 4096-byte boundary. 10 | # 11 | # Because this code sets DS to zero, it must sit 12 | # at an address in the low 2^16 bytes. 13 | # 14 | # Startothers (in main.c) sends the STARTUPs one at a time. 15 | # It copies this code (start) at 0x7000. It puts the address of 16 | # a newly allocated per-core stack in start-4,the address of the 17 | # place to jump to (mpenter) in start-8, and the physical address 18 | # of entrypgdir in start-12. 19 | # 20 | # This code combines elements of bootasm.S and entry.S. 21 | 22 | .code16 23 | .globl start 24 | start: 25 | cli 26 | 27 | # Zero data segment registers DS, ES, and SS. 28 | xorw %ax,%ax 29 | movw %ax,%ds 30 | movw %ax,%es 31 | movw %ax,%ss 32 | 33 | # Switch from real to protected mode. Use a bootstrap GDT that makes 34 | # virtual addresses map directly to physical addresses so that the 35 | # effective memory map doesn't change during the transition. 36 | lgdt gdtdesc 37 | movl %cr0, %eax 38 | orl $CR0_PE, %eax 39 | movl %eax, %cr0 40 | 41 | # Complete the transition to 32-bit protected mode by using a long jmp 42 | # to reload %cs and %eip. The segment descriptors are set up with no 43 | # translation, so that the mapping is still the identity mapping. 44 | ljmpl $(SEG_KCODE<<3), $(start32) 45 | 46 | //PAGEBREAK! 47 | .code32 # Tell assembler to generate 32-bit code now. 48 | start32: 49 | # Set up the protected-mode data segment registers 50 | movw $(SEG_KDATA<<3), %ax # Our data segment selector 51 | movw %ax, %ds # -> DS: Data Segment 52 | movw %ax, %es # -> ES: Extra Segment 53 | movw %ax, %ss # -> SS: Stack Segment 54 | movw $0, %ax # Zero segments not ready for use 55 | movw %ax, %fs # -> FS 56 | movw %ax, %gs # -> GS 57 | 58 | # Turn on page size extension for 4Mbyte pages 59 | movl %cr4, %eax 60 | orl $(CR4_PSE), %eax 61 | movl %eax, %cr4 62 | # Use entrypgdir as our initial page table 63 | movl (start-12), %eax 64 | movl %eax, %cr3 65 | # Turn on paging. 66 | movl %cr0, %eax 67 | orl $(CR0_PE|CR0_PG|CR0_WP), %eax 68 | movl %eax, %cr0 69 | 70 | # Switch to the stack allocated by startothers() 71 | movl (start-4), %esp 72 | # Call mpenter() 73 | call *(start-8) 74 | 75 | movw $0x8a00, %ax 76 | movw %ax, %dx 77 | outw %ax, %dx 78 | movw $0x8ae0, %ax 79 | outw %ax, %dx 80 | spin: 81 | jmp spin 82 | 83 | .p2align 2 84 | gdt: 85 | SEG_NULLASM 86 | SEG_ASM(STA_X|STA_R, 0, 0xffffffff) 87 | SEG_ASM(STA_W, 0, 0xffffffff) 88 | 89 | 90 | gdtdesc: 91 | .word (gdtdesc - gdt - 1) 92 | .long gdt 93 | 94 | -------------------------------------------------------------------------------- /exec.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "mmu.h" 5 | #include "proc.h" 6 | #include "defs.h" 7 | #include "x86.h" 8 | #include "elf.h" 9 | 10 | int 11 | exec(char *path, char **argv) 12 | { 13 | char *s, *last; 14 | int i, off; 15 | uint argc, sz, sp, ustack[3+MAXARG+1]; 16 | struct elfhdr elf; 17 | struct inode *ip; 18 | struct proghdr ph; 19 | pde_t *pgdir, *oldpgdir; 20 | struct proc *curproc = myproc(); 21 | 22 | begin_op(); 23 | 24 | if((ip = namei(path)) == 0){ 25 | end_op(); 26 | cprintf("exec: fail\n"); 27 | return -1; 28 | } 29 | ilock(ip); 30 | pgdir = 0; 31 | 32 | // Check ELF header 33 | if(readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf)) 34 | goto bad; 35 | if(elf.magic != ELF_MAGIC) 36 | goto bad; 37 | 38 | if((pgdir = setupkvm()) == 0) 39 | goto bad; 40 | 41 | // Load program into memory. 42 | sz = 0; 43 | for(i=0, off=elf.phoff; i= MAXARG) 74 | goto bad; 75 | sp = (sp - (strlen(argv[argc]) + 1)) & ~3; 76 | if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) 77 | goto bad; 78 | ustack[3+argc] = sp; 79 | } 80 | ustack[3+argc] = 0; 81 | 82 | ustack[0] = 0xffffffff; // fake return PC 83 | ustack[1] = argc; 84 | ustack[2] = sp - (argc+1)*4; // argv pointer 85 | 86 | sp -= (3+argc+1) * 4; 87 | if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) 88 | goto bad; 89 | 90 | // Save program name for debugging. 91 | for(last=s=path; *s; s++) 92 | if(*s == '/') 93 | last = s+1; 94 | safestrcpy(curproc->name, last, sizeof(curproc->name)); 95 | 96 | // Commit to the user image. 97 | oldpgdir = curproc->pgdir; 98 | curproc->pgdir = pgdir; 99 | curproc->sz = sz; 100 | curproc->tf->eip = elf.entry; // main 101 | curproc->tf->esp = sp; 102 | switchuvm(curproc); 103 | freevm(oldpgdir); 104 | return 0; 105 | 106 | bad: 107 | if(pgdir) 108 | freevm(pgdir); 109 | if(ip){ 110 | iunlockput(ip); 111 | end_op(); 112 | } 113 | return -1; 114 | } 115 | -------------------------------------------------------------------------------- /fcntl.h: -------------------------------------------------------------------------------- 1 | #define O_RDONLY 0x000 2 | #define O_WRONLY 0x001 3 | #define O_RDWR 0x002 4 | #define O_CREATE 0x200 5 | -------------------------------------------------------------------------------- /file.c: -------------------------------------------------------------------------------- 1 | // 2 | // File descriptors 3 | // 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "fs.h" 9 | #include "spinlock.h" 10 | #include "sleeplock.h" 11 | #include "file.h" 12 | 13 | struct devsw devsw[NDEV]; 14 | struct { 15 | struct spinlock lock; 16 | struct file file[NFILE]; 17 | } ftable; 18 | 19 | void 20 | fileinit(void) 21 | { 22 | initlock(&ftable.lock, "ftable"); 23 | } 24 | 25 | // Allocate a file structure. 26 | struct file* 27 | filealloc(void) 28 | { 29 | struct file *f; 30 | 31 | acquire(&ftable.lock); 32 | for(f = ftable.file; f < ftable.file + NFILE; f++){ 33 | if(f->ref == 0){ 34 | f->ref = 1; 35 | release(&ftable.lock); 36 | return f; 37 | } 38 | } 39 | release(&ftable.lock); 40 | return 0; 41 | } 42 | 43 | // Increment ref count for file f. 44 | struct file* 45 | filedup(struct file *f) 46 | { 47 | acquire(&ftable.lock); 48 | if(f->ref < 1) 49 | panic("filedup"); 50 | f->ref++; 51 | release(&ftable.lock); 52 | return f; 53 | } 54 | 55 | // Close file f. (Decrement ref count, close when reaches 0.) 56 | void 57 | fileclose(struct file *f) 58 | { 59 | struct file ff; 60 | 61 | acquire(&ftable.lock); 62 | if(f->ref < 1) 63 | panic("fileclose"); 64 | if(--f->ref > 0){ 65 | release(&ftable.lock); 66 | return; 67 | } 68 | ff = *f; 69 | f->ref = 0; 70 | f->type = FD_NONE; 71 | release(&ftable.lock); 72 | 73 | if(ff.type == FD_PIPE) 74 | pipeclose(ff.pipe, ff.writable); 75 | else if(ff.type == FD_INODE){ 76 | begin_op(); 77 | iput(ff.ip); 78 | end_op(); 79 | } 80 | } 81 | 82 | // Get metadata about file f. 83 | int 84 | filestat(struct file *f, struct stat *st) 85 | { 86 | if(f->type == FD_INODE){ 87 | ilock(f->ip); 88 | stati(f->ip, st); 89 | iunlock(f->ip); 90 | return 0; 91 | } 92 | return -1; 93 | } 94 | 95 | // Read from file f. 96 | int 97 | fileread(struct file *f, char *addr, int n) 98 | { 99 | int r; 100 | 101 | if(f->readable == 0) 102 | return -1; 103 | if(f->type == FD_PIPE) 104 | return piperead(f->pipe, addr, n); 105 | if(f->type == FD_INODE){ 106 | ilock(f->ip); 107 | if((r = readi(f->ip, addr, f->off, n)) > 0) 108 | f->off += r; 109 | iunlock(f->ip); 110 | return r; 111 | } 112 | panic("fileread"); 113 | } 114 | 115 | //PAGEBREAK! 116 | // Write to file f. 117 | int 118 | filewrite(struct file *f, char *addr, int n) 119 | { 120 | int r; 121 | 122 | if(f->writable == 0) 123 | return -1; 124 | if(f->type == FD_PIPE) 125 | return pipewrite(f->pipe, addr, n); 126 | if(f->type == FD_INODE){ 127 | // write a few blocks at a time to avoid exceeding 128 | // the maximum log transaction size, including 129 | // i-node, indirect block, allocation blocks, 130 | // and 2 blocks of slop for non-aligned writes. 131 | // this really belongs lower down, since writei() 132 | // might be writing a device like the console. 133 | int max = ((MAXOPBLOCKS-1-1-2) / 2) * 512; 134 | int i = 0; 135 | while(i < n){ 136 | int n1 = n - i; 137 | if(n1 > max) 138 | n1 = max; 139 | 140 | begin_op(); 141 | ilock(f->ip); 142 | if ((r = writei(f->ip, addr + i, f->off, n1)) > 0) 143 | f->off += r; 144 | iunlock(f->ip); 145 | end_op(); 146 | 147 | if(r < 0) 148 | break; 149 | if(r != n1) 150 | panic("short filewrite"); 151 | i += r; 152 | } 153 | return i == n ? n : -1; 154 | } 155 | panic("filewrite"); 156 | } 157 | 158 | -------------------------------------------------------------------------------- /file.h: -------------------------------------------------------------------------------- 1 | struct file { 2 | enum { FD_NONE, FD_PIPE, FD_INODE } type; 3 | int ref; // reference count 4 | char readable; 5 | char writable; 6 | struct pipe *pipe; 7 | struct inode *ip; 8 | uint off; 9 | }; 10 | 11 | 12 | // in-memory copy of an inode 13 | struct inode { 14 | uint dev; // Device number 15 | uint inum; // Inode number 16 | int ref; // Reference count 17 | struct sleeplock lock; // protects everything below here 18 | int valid; // inode has been read from disk? 19 | 20 | short type; // copy of disk inode 21 | short major; 22 | short minor; 23 | short nlink; 24 | uint size; 25 | uint addrs[NDIRECT+1]; 26 | }; 27 | 28 | // table mapping major device number to 29 | // device functions 30 | struct devsw { 31 | int (*read)(struct inode*, char*, int); 32 | int (*write)(struct inode*, char*, int); 33 | }; 34 | 35 | extern struct devsw devsw[]; 36 | 37 | #define CONSOLE 1 38 | -------------------------------------------------------------------------------- /fs.h: -------------------------------------------------------------------------------- 1 | // On-disk file system format. 2 | // Both the kernel and user programs use this header file. 3 | 4 | 5 | #define ROOTINO 1 // root i-number 6 | #define BSIZE 512 // block size 7 | 8 | // Disk layout: 9 | // [ boot block | super block | log | inode blocks | 10 | // free bit map | data blocks] 11 | // 12 | // mkfs computes the super block and builds an initial file system. The 13 | // super block describes the disk layout: 14 | struct superblock { 15 | uint size; // Size of file system image (blocks) 16 | uint nblocks; // Number of data blocks 17 | uint ninodes; // Number of inodes. 18 | uint nlog; // Number of log blocks 19 | uint logstart; // Block number of first log block 20 | uint inodestart; // Block number of first inode block 21 | uint bmapstart; // Block number of first free map block 22 | }; 23 | 24 | #define NDIRECT 12 25 | #define NINDIRECT (BSIZE / sizeof(uint)) 26 | #define MAXFILE (NDIRECT + NINDIRECT) 27 | 28 | // On-disk inode structure 29 | struct dinode { 30 | short type; // File type 31 | short major; // Major device number (T_DEV only) 32 | short minor; // Minor device number (T_DEV only) 33 | short nlink; // Number of links to inode in file system 34 | uint size; // Size of file (bytes) 35 | uint addrs[NDIRECT+1]; // Data block addresses 36 | }; 37 | 38 | // Inodes per block. 39 | #define IPB (BSIZE / sizeof(struct dinode)) 40 | 41 | // Block containing inode i 42 | #define IBLOCK(i, sb) ((i) / IPB + sb.inodestart) 43 | 44 | // Bitmap bits per block 45 | #define BPB (BSIZE*8) 46 | 47 | // Block of free map containing bit for block b 48 | #define BBLOCK(b, sb) (b/BPB + sb.bmapstart) 49 | 50 | // Directory is a file containing a sequence of dirent structures. 51 | #define DIRSIZ 14 52 | 53 | struct dirent { 54 | ushort inum; 55 | char name[DIRSIZ]; 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /gdbutil: -------------------------------------------------------------------------------- 1 | # -*- gdb-script -*- 2 | 3 | # Utility functions to pretty-print x86 segment/interrupt descriptors. 4 | # To load this file, run "source gdbutil" in gdb. 5 | # printdesc and printdescs are the main entry points. 6 | 7 | # IA32 2007, Volume 3A, Table 3-2 8 | set $STS_T16A = 0x1 9 | set $STS_LDT = 0x2 10 | set $STS_T16B = 0x3 11 | set $STS_CG16 = 0x4 12 | set $STS_TG = 0x5 13 | set $STS_IG16 = 0x6 14 | set $STS_TG16 = 0x7 15 | set $STS_T32A = 0x9 16 | set $STS_T32B = 0xB 17 | set $STS_CG32 = 0xC 18 | set $STS_IG32 = 0xE 19 | set $STS_TG32 = 0xF 20 | 21 | define outputsts 22 | while 1 23 | if $arg0 == $STS_T16A 24 | echo STS_T16A 25 | loop_break 26 | end 27 | if $arg0 == $STS_LDT 28 | echo STS_LDT\ 29 | loop_break 30 | end 31 | if $arg0 == $STS_T16B 32 | echo STS_T16B 33 | loop_break 34 | end 35 | if $arg0 == $STS_CG16 36 | echo STS_CG16 37 | loop_break 38 | end 39 | if $arg0 == $STS_TG 40 | echo STS_TG\ \ 41 | loop_break 42 | end 43 | if $arg0 == $STS_IG16 44 | echo STS_IG16 45 | loop_break 46 | end 47 | if $arg0 == $STS_TG16 48 | echo STS_TG16 49 | loop_break 50 | end 51 | if $arg0 == $STS_T32A 52 | echo STS_T32A 53 | loop_break 54 | end 55 | if $arg0 == $STS_T32B 56 | echo STS_T32B 57 | loop_break 58 | end 59 | if $arg0 == $STS_CG32 60 | echo STS_CG32 61 | loop_break 62 | end 63 | if $arg0 == $STS_IG32 64 | echo STS_IG32 65 | loop_break 66 | end 67 | if $arg0 == $STS_TG32 68 | echo STS_TG32 69 | loop_break 70 | end 71 | echo Reserved 72 | loop_break 73 | end 74 | end 75 | 76 | # IA32 2007, Volume 3A, Table 3-1 77 | set $STA_X = 0x8 78 | set $STA_E = 0x4 79 | set $STA_C = 0x4 80 | set $STA_W = 0x2 81 | set $STA_R = 0x2 82 | set $STA_A = 0x1 83 | 84 | define outputsta 85 | if $arg0 & $STA_X 86 | # Code segment 87 | echo code 88 | if $arg0 & $STA_C 89 | echo |STA_C 90 | end 91 | if $arg0 & $STA_R 92 | echo |STA_R 93 | end 94 | else 95 | # Data segment 96 | echo data 97 | if $arg0 & $STA_E 98 | echo |STA_E 99 | end 100 | if $arg0 & $STA_W 101 | echo |STA_W 102 | end 103 | end 104 | if $arg0 & $STA_A 105 | echo |STA_A 106 | else 107 | printf " " 108 | end 109 | end 110 | 111 | # xv6-specific 112 | set $SEG_KCODE = 1 113 | set $SEG_KDATA = 2 114 | set $SEG_KCPU = 3 115 | set $SEG_UCODE = 4 116 | set $SEG_UDATA = 5 117 | set $SEG_TSS = 6 118 | 119 | define outputcs 120 | if ($arg0 & 4) == 0 121 | if $arg0 >> 3 == $SEG_KCODE 122 | printf "SEG_KCODE<<3" 123 | end 124 | if $arg0 >> 3 == $SEG_KDATA 125 | printf "SEG_KDATA<<3" 126 | end 127 | if $arg0 >> 3 == $SEG_KCPU 128 | printf "SEG_KCPU<<3" 129 | end 130 | if $arg0 >> 3 == $SEG_UCODE 131 | printf "SEG_UCODE<<3" 132 | end 133 | if $arg0 >> 3 == $SEG_UDATA 134 | printf "SEG_UDATA<<3" 135 | end 136 | if $arg0 >> 3 == $SEG_TSS 137 | printf "SEG_TSS<<3" 138 | end 139 | if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6) 140 | printf "GDT[%d]", $arg0 >> 3 141 | end 142 | else 143 | printf "LDT[%d]", $arg0 >> 3 144 | end 145 | if ($arg0 & 3) > 0 146 | printf "|" 147 | outputdpl ($arg0&3) 148 | end 149 | end 150 | 151 | define outputdpl 152 | if $arg0 == 0 153 | printf "DPL_KERN" 154 | else 155 | if $arg0 == 3 156 | printf "DPL_USER" 157 | else 158 | printf "DPL%d", $arg0 159 | end 160 | end 161 | end 162 | 163 | define printdesc 164 | if $argc != 1 165 | echo Usage: printdesc expr 166 | else 167 | _printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1] 168 | printf "\n" 169 | end 170 | end 171 | 172 | document printdesc 173 | Print an x86 segment or gate descriptor. 174 | printdesc EXPR 175 | EXPR must evaluate to a descriptor value. It can be of any C type. 176 | end 177 | 178 | define _printdesc 179 | _printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15) 180 | end 181 | 182 | define _printdesc1 183 | # 2:P 3:DPL 4:S 5:Type 184 | if $arg2 == 0 185 | printf "P = 0 (Not present)" 186 | else 187 | printf "type = " 188 | if $arg4 == 0 189 | # System segment 190 | outputsts $arg5 191 | printf " (0x%x) ", $arg5 192 | _printsysdesc $arg0 $arg1 $arg5 193 | else 194 | # Code/data segment 195 | outputsta $arg5 196 | printf " " 197 | _printsegdesc $arg0 $arg1 198 | end 199 | 200 | printf " DPL = " 201 | outputdpl $arg3 202 | printf " (%d)", $arg3 203 | end 204 | end 205 | 206 | define _printsysdesc 207 | # 2:Type 208 | # GDB's || is buggy 209 | if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16) 210 | # Gate descriptor 211 | _printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16) 212 | else 213 | # System segment descriptor 214 | _printsegdesc $arg0 $arg1 215 | end 216 | end 217 | 218 | define _printgate 219 | # IA32 2007, Voume 3A, Figure 5-2 220 | # 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16 221 | printf "CS = " 222 | outputcs $arg1 223 | printf " (%d)", $arg1 224 | 225 | if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16) 226 | printf " Offset = " 227 | output/a $arg3 << 16 | $arg2 228 | end 229 | end 230 | 231 | define _printsegdesc 232 | # IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1 233 | _printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1) 234 | if ($arg1>>12&1) == 1 235 | printf " AVL = %d", $arg1>>20&1 236 | if ($arg1>>11&1) == 0 237 | # Data segment 238 | if ($arg1>>22&1) == 0 239 | printf " B = small (0) " 240 | else 241 | printf " B = big (1) " 242 | end 243 | else 244 | # Code segment 245 | printf " D = " 246 | if ($arg1>>22&1) == 0 247 | printf "16-bit (0)" 248 | else 249 | printf "32-bit (1)" 250 | end 251 | end 252 | end 253 | end 254 | 255 | define _printsegdesc1 256 | # 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G 257 | printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24) 258 | printf " limit = 0x" 259 | if $arg5 == 0 260 | printf "%08x", $arg3 | ($arg4<<16) 261 | else 262 | printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF 263 | end 264 | end 265 | 266 | define printdescs 267 | if $argc < 1 || $argc > 2 268 | echo Usage: printdescs expr [count] 269 | else 270 | if $argc == 1 271 | _printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0])) 272 | else 273 | _printdescs ($arg0) ($arg1) 274 | end 275 | end 276 | end 277 | 278 | document printdescs 279 | Print an array of x86 segment or gate descriptors. 280 | printdescs EXPR [COUNT] 281 | EXPR must evaluate to an array of descriptors. 282 | end 283 | 284 | define _printdescs 285 | set $i = 0 286 | while $i < $arg1 287 | printf "[%d] ", $i 288 | printdesc $arg0[$i] 289 | set $i = $i + 1 290 | end 291 | end 292 | -------------------------------------------------------------------------------- /grep.c: -------------------------------------------------------------------------------- 1 | // Simple grep. Only supports ^ . * $ operators. 2 | 3 | #include "types.h" 4 | #include "stat.h" 5 | #include "user.h" 6 | 7 | char buf[1024]; 8 | int match(char*, char*); 9 | 10 | void 11 | grep(char *pattern, int fd) 12 | { 13 | int n, m; 14 | char *p, *q; 15 | 16 | m = 0; 17 | while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ 18 | m += n; 19 | buf[m] = '\0'; 20 | p = buf; 21 | while((q = strchr(p, '\n')) != 0){ 22 | *q = 0; 23 | if(match(pattern, p)){ 24 | *q = '\n'; 25 | write(1, p, q+1 - p); 26 | } 27 | p = q+1; 28 | } 29 | if(p == buf) 30 | m = 0; 31 | if(m > 0){ 32 | m -= p - buf; 33 | memmove(buf, p, m); 34 | } 35 | } 36 | } 37 | 38 | int 39 | main(int argc, char *argv[]) 40 | { 41 | int fd, i; 42 | char *pattern; 43 | 44 | if(argc <= 1){ 45 | printf(2, "usage: grep pattern [file ...]\n"); 46 | exit(); 47 | } 48 | pattern = argv[1]; 49 | 50 | if(argc <= 2){ 51 | grep(pattern, 0); 52 | exit(); 53 | } 54 | 55 | for(i = 2; i < argc; i++){ 56 | if((fd = open(argv[i], 0)) < 0){ 57 | printf(1, "grep: cannot open %s\n", argv[i]); 58 | exit(); 59 | } 60 | grep(pattern, fd); 61 | close(fd); 62 | } 63 | exit(); 64 | } 65 | 66 | // Regexp matcher from Kernighan & Pike, 67 | // The Practice of Programming, Chapter 9. 68 | 69 | int matchhere(char*, char*); 70 | int matchstar(int, char*, char*); 71 | 72 | int 73 | match(char *re, char *text) 74 | { 75 | if(re[0] == '^') 76 | return matchhere(re+1, text); 77 | do{ // must look at empty string 78 | if(matchhere(re, text)) 79 | return 1; 80 | }while(*text++ != '\0'); 81 | return 0; 82 | } 83 | 84 | // matchhere: search for re at beginning of text 85 | int matchhere(char *re, char *text) 86 | { 87 | if(re[0] == '\0') 88 | return 1; 89 | if(re[1] == '*') 90 | return matchstar(re[0], re+2, text); 91 | if(re[0] == '$' && re[1] == '\0') 92 | return *text == '\0'; 93 | if(*text!='\0' && (re[0]=='.' || re[0]==*text)) 94 | return matchhere(re+1, text+1); 95 | return 0; 96 | } 97 | 98 | // matchstar: search for c*re at beginning of text 99 | int matchstar(int c, char *re, char *text) 100 | { 101 | do{ // a * matches zero or more instances 102 | if(matchhere(re, text)) 103 | return 1; 104 | }while(*text!='\0' && (*text++==c || c=='.')); 105 | return 0; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /ide.c: -------------------------------------------------------------------------------- 1 | // Simple PIO-based (non-DMA) IDE driver code. 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "memlayout.h" 7 | #include "mmu.h" 8 | #include "proc.h" 9 | #include "x86.h" 10 | #include "traps.h" 11 | #include "spinlock.h" 12 | #include "sleeplock.h" 13 | #include "fs.h" 14 | #include "buf.h" 15 | 16 | #define SECTOR_SIZE 512 17 | #define IDE_BSY 0x80 18 | #define IDE_DRDY 0x40 19 | #define IDE_DF 0x20 20 | #define IDE_ERR 0x01 21 | 22 | #define IDE_CMD_READ 0x20 23 | #define IDE_CMD_WRITE 0x30 24 | #define IDE_CMD_RDMUL 0xc4 25 | #define IDE_CMD_WRMUL 0xc5 26 | 27 | // idequeue points to the buf now being read/written to the disk. 28 | // idequeue->qnext points to the next buf to be processed. 29 | // You must hold idelock while manipulating queue. 30 | 31 | static struct spinlock idelock; 32 | static struct buf *idequeue; 33 | 34 | static int havedisk1; 35 | static void idestart(struct buf*); 36 | 37 | // Wait for IDE disk to become ready. 38 | static int 39 | idewait(int checkerr) 40 | { 41 | int r; 42 | 43 | while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 44 | ; 45 | if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0) 46 | return -1; 47 | return 0; 48 | } 49 | 50 | void 51 | ideinit(void) 52 | { 53 | int i; 54 | 55 | initlock(&idelock, "ide"); 56 | ioapicenable(IRQ_IDE, ncpu - 1); 57 | idewait(0); 58 | 59 | // Check if disk 1 is present 60 | outb(0x1f6, 0xe0 | (1<<4)); 61 | for(i=0; i<1000; i++){ 62 | if(inb(0x1f7) != 0){ 63 | havedisk1 = 1; 64 | break; 65 | } 66 | } 67 | 68 | // Switch back to disk 0. 69 | outb(0x1f6, 0xe0 | (0<<4)); 70 | } 71 | 72 | // Start the request for b. Caller must hold idelock. 73 | static void 74 | idestart(struct buf *b) 75 | { 76 | if(b == 0) 77 | panic("idestart"); 78 | if(b->blockno >= FSSIZE) 79 | panic("incorrect blockno"); 80 | int sector_per_block = BSIZE/SECTOR_SIZE; 81 | int sector = b->blockno * sector_per_block; 82 | int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL; 83 | int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL; 84 | 85 | if (sector_per_block > 7) panic("idestart"); 86 | 87 | idewait(0); 88 | outb(0x3f6, 0); // generate interrupt 89 | outb(0x1f2, sector_per_block); // number of sectors 90 | outb(0x1f3, sector & 0xff); 91 | outb(0x1f4, (sector >> 8) & 0xff); 92 | outb(0x1f5, (sector >> 16) & 0xff); 93 | outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((sector>>24)&0x0f)); 94 | if(b->flags & B_DIRTY){ 95 | outb(0x1f7, write_cmd); 96 | outsl(0x1f0, b->data, BSIZE/4); 97 | } else { 98 | outb(0x1f7, read_cmd); 99 | } 100 | } 101 | 102 | // Interrupt handler. 103 | void 104 | ideintr(void) 105 | { 106 | struct buf *b; 107 | 108 | // First queued buffer is the active request. 109 | acquire(&idelock); 110 | 111 | if((b = idequeue) == 0){ 112 | release(&idelock); 113 | return; 114 | } 115 | idequeue = b->qnext; 116 | 117 | // Read data if needed. 118 | if(!(b->flags & B_DIRTY) && idewait(1) >= 0) 119 | insl(0x1f0, b->data, BSIZE/4); 120 | 121 | // Wake process waiting for this buf. 122 | b->flags |= B_VALID; 123 | b->flags &= ~B_DIRTY; 124 | wakeup(b); 125 | 126 | // Start disk on next buf in queue. 127 | if(idequeue != 0) 128 | idestart(idequeue); 129 | 130 | release(&idelock); 131 | } 132 | 133 | //PAGEBREAK! 134 | // Sync buf with disk. 135 | // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 136 | // Else if B_VALID is not set, read buf from disk, set B_VALID. 137 | void 138 | iderw(struct buf *b) 139 | { 140 | struct buf **pp; 141 | 142 | if(!holdingsleep(&b->lock)) 143 | panic("iderw: buf not locked"); 144 | if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 145 | panic("iderw: nothing to do"); 146 | if(b->dev != 0 && !havedisk1) 147 | panic("iderw: ide disk 1 not present"); 148 | 149 | acquire(&idelock); //DOC:acquire-lock 150 | 151 | // Append b to idequeue. 152 | b->qnext = 0; 153 | for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue 154 | ; 155 | *pp = b; 156 | 157 | // Start disk if necessary. 158 | if(idequeue == b) 159 | idestart(b); 160 | 161 | // Wait for request to finish. 162 | while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ 163 | sleep(b, &idelock); 164 | } 165 | 166 | 167 | release(&idelock); 168 | } 169 | -------------------------------------------------------------------------------- /init.c: -------------------------------------------------------------------------------- 1 | // init: The initial user-level program 2 | 3 | #include "types.h" 4 | #include "stat.h" 5 | #include "user.h" 6 | #include "fcntl.h" 7 | 8 | char *argv[] = { "sh", 0 }; 9 | 10 | int 11 | main(void) 12 | { 13 | int pid, wpid; 14 | 15 | if(open("console", O_RDWR) < 0){ 16 | mknod("console", 1, 1); 17 | open("console", O_RDWR); 18 | } 19 | dup(0); // stdout 20 | dup(0); // stderr 21 | 22 | for(;;){ 23 | printf(1, "init: starting sh\n"); 24 | pid = fork(); 25 | if(pid < 0){ 26 | printf(1, "init: fork failed\n"); 27 | exit(); 28 | } 29 | if(pid == 0){ 30 | exec("sh", argv); 31 | printf(1, "init: exec sh failed\n"); 32 | exit(); 33 | } 34 | while((wpid=wait()) >= 0 && wpid != pid) 35 | printf(1, "zombie!\n"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /initcode.S: -------------------------------------------------------------------------------- 1 | # Initial process execs /init. 2 | # This code runs in user space. 3 | 4 | #include "syscall.h" 5 | #include "traps.h" 6 | 7 | 8 | # exec(init, argv) 9 | .globl start 10 | start: 11 | pushl $argv 12 | pushl $init 13 | pushl $0 // where caller pc would be 14 | movl $SYS_exec, %eax 15 | int $T_SYSCALL 16 | 17 | # for(;;) exit(); 18 | exit: 19 | movl $SYS_exit, %eax 20 | int $T_SYSCALL 21 | jmp exit 22 | 23 | # char init[] = "/init\0"; 24 | init: 25 | .string "/init\0" 26 | 27 | # char *argv[] = { init, 0 }; 28 | .p2align 2 29 | argv: 30 | .long init 31 | .long 0 32 | 33 | -------------------------------------------------------------------------------- /ioapic.c: -------------------------------------------------------------------------------- 1 | // The I/O APIC manages hardware interrupts for an SMP system. 2 | // http://www.intel.com/design/chipsets/datashts/29056601.pdf 3 | // See also picirq.c. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "traps.h" 8 | 9 | #define IOAPIC 0xFEC00000 // Default physical address of IO APIC 10 | 11 | #define REG_ID 0x00 // Register index: ID 12 | #define REG_VER 0x01 // Register index: version 13 | #define REG_TABLE 0x10 // Redirection table base 14 | 15 | // The redirection table starts at REG_TABLE and uses 16 | // two registers to configure each interrupt. 17 | // The first (low) register in a pair contains configuration bits. 18 | // The second (high) register contains a bitmask telling which 19 | // CPUs can serve that interrupt. 20 | #define INT_DISABLED 0x00010000 // Interrupt disabled 21 | #define INT_LEVEL 0x00008000 // Level-triggered (vs edge-) 22 | #define INT_ACTIVELOW 0x00002000 // Active low (vs high) 23 | #define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID) 24 | 25 | volatile struct ioapic *ioapic; 26 | 27 | // IO APIC MMIO structure: write reg, then read or write data. 28 | struct ioapic { 29 | uint reg; 30 | uint pad[3]; 31 | uint data; 32 | }; 33 | 34 | static uint 35 | ioapicread(int reg) 36 | { 37 | ioapic->reg = reg; 38 | return ioapic->data; 39 | } 40 | 41 | static void 42 | ioapicwrite(int reg, uint data) 43 | { 44 | ioapic->reg = reg; 45 | ioapic->data = data; 46 | } 47 | 48 | void 49 | ioapicinit(void) 50 | { 51 | int i, id, maxintr; 52 | 53 | ioapic = (volatile struct ioapic*)IOAPIC; 54 | maxintr = (ioapicread(REG_VER) >> 16) & 0xFF; 55 | id = ioapicread(REG_ID) >> 24; 56 | if(id != ioapicid) 57 | cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n"); 58 | 59 | // Mark all interrupts edge-triggered, active high, disabled, 60 | // and not routed to any CPUs. 61 | for(i = 0; i <= maxintr; i++){ 62 | ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); 63 | ioapicwrite(REG_TABLE+2*i+1, 0); 64 | } 65 | } 66 | 67 | void 68 | ioapicenable(int irq, int cpunum) 69 | { 70 | // Mark interrupt edge-triggered, active high, 71 | // enabled, and routed to the given cpunum, 72 | // which happens to be that cpu's APIC ID. 73 | ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq); 74 | ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24); 75 | } 76 | -------------------------------------------------------------------------------- /kalloc.c: -------------------------------------------------------------------------------- 1 | // Physical memory allocator, intended to allocate 2 | // memory for user processes, kernel stacks, page table pages, 3 | // and pipe buffers. Allocates 4096-byte pages. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "memlayout.h" 9 | #include "mmu.h" 10 | #include "spinlock.h" 11 | 12 | void freerange(void *vstart, void *vend); 13 | extern char end[]; // first address after kernel loaded from ELF file 14 | // defined by the kernel linker script in kernel.ld 15 | 16 | struct run { 17 | struct run *next; 18 | }; 19 | 20 | struct { 21 | struct spinlock lock; 22 | int use_lock; 23 | struct run *freelist; 24 | } kmem; 25 | 26 | // Initialization happens in two phases. 27 | // 1. main() calls kinit1() while still using entrypgdir to place just 28 | // the pages mapped by entrypgdir on free list. 29 | // 2. main() calls kinit2() with the rest of the physical pages 30 | // after installing a full page table that maps them on all cores. 31 | void 32 | kinit1(void *vstart, void *vend) 33 | { 34 | initlock(&kmem.lock, "kmem"); 35 | kmem.use_lock = 0; 36 | freerange(vstart, vend); 37 | } 38 | 39 | void 40 | kinit2(void *vstart, void *vend) 41 | { 42 | freerange(vstart, vend); 43 | kmem.use_lock = 1; 44 | } 45 | 46 | void 47 | freerange(void *vstart, void *vend) 48 | { 49 | char *p; 50 | p = (char*)PGROUNDUP((uint)vstart); 51 | for(; p + PGSIZE <= (char*)vend; p += PGSIZE) 52 | kfree(p); 53 | } 54 | //PAGEBREAK: 21 55 | // Free the page of physical memory pointed at by v, 56 | // which normally should have been returned by a 57 | // call to kalloc(). (The exception is when 58 | // initializing the allocator; see kinit above.) 59 | void 60 | kfree(char *v) 61 | { 62 | struct run *r; 63 | 64 | if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP) 65 | panic("kfree"); 66 | 67 | // Fill with junk to catch dangling refs. 68 | memset(v, 1, PGSIZE); 69 | 70 | if(kmem.use_lock) 71 | acquire(&kmem.lock); 72 | r = (struct run*)v; 73 | r->next = kmem.freelist; 74 | kmem.freelist = r; 75 | if(kmem.use_lock) 76 | release(&kmem.lock); 77 | } 78 | 79 | // Allocate one 4096-byte page of physical memory. 80 | // Returns a pointer that the kernel can use. 81 | // Returns 0 if the memory cannot be allocated. 82 | char* 83 | kalloc(void) 84 | { 85 | struct run *r; 86 | 87 | if(kmem.use_lock) 88 | acquire(&kmem.lock); 89 | r = kmem.freelist; 90 | if(r) 91 | kmem.freelist = r->next; 92 | if(kmem.use_lock) 93 | release(&kmem.lock); 94 | return (char*)r; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /kbd.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | #include "defs.h" 4 | #include "kbd.h" 5 | 6 | int 7 | kbdgetc(void) 8 | { 9 | static uint shift; 10 | static uchar *charcode[4] = { 11 | normalmap, shiftmap, ctlmap, ctlmap 12 | }; 13 | uint st, data, c; 14 | 15 | st = inb(KBSTATP); 16 | if((st & KBS_DIB) == 0) 17 | return -1; 18 | data = inb(KBDATAP); 19 | 20 | if(data == 0xE0){ 21 | shift |= E0ESC; 22 | return 0; 23 | } else if(data & 0x80){ 24 | // Key released 25 | data = (shift & E0ESC ? data : data & 0x7F); 26 | shift &= ~(shiftcode[data] | E0ESC); 27 | return 0; 28 | } else if(shift & E0ESC){ 29 | // Last character was an E0 escape; or with 0x80 30 | data |= 0x80; 31 | shift &= ~E0ESC; 32 | } 33 | 34 | shift |= shiftcode[data]; 35 | shift ^= togglecode[data]; 36 | c = charcode[shift & (CTL | SHIFT)][data]; 37 | if(shift & CAPSLOCK){ 38 | if('a' <= c && c <= 'z') 39 | c += 'A' - 'a'; 40 | else if('A' <= c && c <= 'Z') 41 | c += 'a' - 'A'; 42 | } 43 | return c; 44 | } 45 | 46 | void 47 | kbdintr(void) 48 | { 49 | consoleintr(kbdgetc); 50 | } 51 | -------------------------------------------------------------------------------- /kbd.h: -------------------------------------------------------------------------------- 1 | // PC keyboard interface constants 2 | 3 | #define KBSTATP 0x64 // kbd controller status port(I) 4 | #define KBS_DIB 0x01 // kbd data in buffer 5 | #define KBDATAP 0x60 // kbd data port(I) 6 | 7 | #define NO 0 8 | 9 | #define SHIFT (1<<0) 10 | #define CTL (1<<1) 11 | #define ALT (1<<2) 12 | 13 | #define CAPSLOCK (1<<3) 14 | #define NUMLOCK (1<<4) 15 | #define SCROLLLOCK (1<<5) 16 | 17 | #define E0ESC (1<<6) 18 | 19 | // Special keycodes 20 | #define KEY_HOME 0xE0 21 | #define KEY_END 0xE1 22 | #define KEY_UP 0xE2 23 | #define KEY_DN 0xE3 24 | #define KEY_LF 0xE4 25 | #define KEY_RT 0xE5 26 | #define KEY_PGUP 0xE6 27 | #define KEY_PGDN 0xE7 28 | #define KEY_INS 0xE8 29 | #define KEY_DEL 0xE9 30 | 31 | // C('A') == Control-A 32 | #define C(x) (x - '@') 33 | 34 | static uchar shiftcode[256] = 35 | { 36 | [0x1D] CTL, 37 | [0x2A] SHIFT, 38 | [0x36] SHIFT, 39 | [0x38] ALT, 40 | [0x9D] CTL, 41 | [0xB8] ALT 42 | }; 43 | 44 | static uchar togglecode[256] = 45 | { 46 | [0x3A] CAPSLOCK, 47 | [0x45] NUMLOCK, 48 | [0x46] SCROLLLOCK 49 | }; 50 | 51 | static uchar normalmap[256] = 52 | { 53 | NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 54 | '7', '8', '9', '0', '-', '=', '\b', '\t', 55 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 56 | 'o', 'p', '[', ']', '\n', NO, 'a', 's', 57 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 58 | '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', 59 | 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 60 | NO, ' ', NO, NO, NO, NO, NO, NO, 61 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 62 | '8', '9', '-', '4', '5', '6', '+', '1', 63 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 64 | [0x9C] '\n', // KP_Enter 65 | [0xB5] '/', // KP_Div 66 | [0xC8] KEY_UP, [0xD0] KEY_DN, 67 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 68 | [0xCB] KEY_LF, [0xCD] KEY_RT, 69 | [0x97] KEY_HOME, [0xCF] KEY_END, 70 | [0xD2] KEY_INS, [0xD3] KEY_DEL 71 | }; 72 | 73 | static uchar shiftmap[256] = 74 | { 75 | NO, 033, '!', '@', '#', '$', '%', '^', // 0x00 76 | '&', '*', '(', ')', '_', '+', '\b', '\t', 77 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 78 | 'O', 'P', '{', '}', '\n', NO, 'A', 'S', 79 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 80 | '"', '~', NO, '|', 'Z', 'X', 'C', 'V', 81 | 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 82 | NO, ' ', NO, NO, NO, NO, NO, NO, 83 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 84 | '8', '9', '-', '4', '5', '6', '+', '1', 85 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 86 | [0x9C] '\n', // KP_Enter 87 | [0xB5] '/', // KP_Div 88 | [0xC8] KEY_UP, [0xD0] KEY_DN, 89 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 90 | [0xCB] KEY_LF, [0xCD] KEY_RT, 91 | [0x97] KEY_HOME, [0xCF] KEY_END, 92 | [0xD2] KEY_INS, [0xD3] KEY_DEL 93 | }; 94 | 95 | static uchar ctlmap[256] = 96 | { 97 | NO, NO, NO, NO, NO, NO, NO, NO, 98 | NO, NO, NO, NO, NO, NO, NO, NO, 99 | C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), 100 | C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'), 101 | C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO, 102 | NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'), 103 | C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO, 104 | [0x9C] '\r', // KP_Enter 105 | [0xB5] C('/'), // KP_Div 106 | [0xC8] KEY_UP, [0xD0] KEY_DN, 107 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 108 | [0xCB] KEY_LF, [0xCD] KEY_RT, 109 | [0x97] KEY_HOME, [0xCF] KEY_END, 110 | [0xD2] KEY_INS, [0xD3] KEY_DEL 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /kernel.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for the JOS kernel. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 5 | OUTPUT_ARCH(i386) 6 | ENTRY(_start) 7 | 8 | SECTIONS 9 | { 10 | /* Link the kernel at this address: "." means the current address */ 11 | /* Must be equal to KERNLINK */ 12 | . = 0x80100000; 13 | 14 | .text : AT(0x100000) { 15 | *(.text .stub .text.* .gnu.linkonce.t.*) 16 | } 17 | 18 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 19 | 20 | .rodata : { 21 | *(.rodata .rodata.* .gnu.linkonce.r.*) 22 | } 23 | 24 | /* Include debugging information in kernel memory */ 25 | .stab : { 26 | PROVIDE(__STAB_BEGIN__ = .); 27 | *(.stab); 28 | PROVIDE(__STAB_END__ = .); 29 | } 30 | 31 | .stabstr : { 32 | PROVIDE(__STABSTR_BEGIN__ = .); 33 | *(.stabstr); 34 | PROVIDE(__STABSTR_END__ = .); 35 | } 36 | 37 | /* Adjust the address for the data segment to the next page */ 38 | . = ALIGN(0x1000); 39 | 40 | /* Conventionally, Unix linkers provide pseudo-symbols 41 | * etext, edata, and end, at the end of the text, data, and bss. 42 | * For the kernel mapping, we need the address at the beginning 43 | * of the data section, but that's not one of the conventional 44 | * symbols, because the convention started before there was a 45 | * read-only rodata section between text and data. */ 46 | PROVIDE(data = .); 47 | 48 | /* The data segment */ 49 | .data : { 50 | *(.data) 51 | } 52 | 53 | PROVIDE(edata = .); 54 | 55 | .bss : { 56 | *(.bss) 57 | } 58 | 59 | PROVIDE(end = .); 60 | 61 | /DISCARD/ : { 62 | *(.eh_frame .note.GNU-stack) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kill.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int i; 9 | 10 | if(argc < 2){ 11 | printf(2, "usage: kill pid...\n"); 12 | exit(); 13 | } 14 | for(i=1; i>16) & 0xFF) >= 4) 78 | lapicw(PCINT, MASKED); 79 | 80 | // Map error interrupt to IRQ_ERROR. 81 | lapicw(ERROR, T_IRQ0 + IRQ_ERROR); 82 | 83 | // Clear error status register (requires back-to-back writes). 84 | lapicw(ESR, 0); 85 | lapicw(ESR, 0); 86 | 87 | // Ack any outstanding interrupts. 88 | lapicw(EOI, 0); 89 | 90 | // Send an Init Level De-Assert to synchronise arbitration ID's. 91 | lapicw(ICRHI, 0); 92 | lapicw(ICRLO, BCAST | INIT | LEVEL); 93 | while(lapic[ICRLO] & DELIVS) 94 | ; 95 | 96 | // Enable interrupts on the APIC (but not on the processor). 97 | lapicw(TPR, 0); 98 | } 99 | 100 | int 101 | lapicid(void) 102 | { 103 | if (!lapic) 104 | return 0; 105 | return lapic[ID] >> 24; 106 | } 107 | 108 | // Acknowledge interrupt. 109 | void 110 | lapiceoi(void) 111 | { 112 | if(lapic) 113 | lapicw(EOI, 0); 114 | } 115 | 116 | // Spin for a given number of microseconds. 117 | // On real hardware would want to tune this dynamically. 118 | void 119 | microdelay(int us) 120 | { 121 | } 122 | 123 | #define CMOS_PORT 0x70 124 | #define CMOS_RETURN 0x71 125 | 126 | // Start additional processor running entry code at addr. 127 | // See Appendix B of MultiProcessor Specification. 128 | void 129 | lapicstartap(uchar apicid, uint addr) 130 | { 131 | int i; 132 | ushort *wrv; 133 | 134 | // "The BSP must initialize CMOS shutdown code to 0AH 135 | // and the warm reset vector (DWORD based at 40:67) to point at 136 | // the AP startup code prior to the [universal startup algorithm]." 137 | outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code 138 | outb(CMOS_PORT+1, 0x0A); 139 | wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector 140 | wrv[0] = 0; 141 | wrv[1] = addr >> 4; 142 | 143 | // "Universal startup algorithm." 144 | // Send INIT (level-triggered) interrupt to reset other CPU. 145 | lapicw(ICRHI, apicid<<24); 146 | lapicw(ICRLO, INIT | LEVEL | ASSERT); 147 | microdelay(200); 148 | lapicw(ICRLO, INIT | LEVEL); 149 | microdelay(100); // should be 10ms, but too slow in Bochs! 150 | 151 | // Send startup IPI (twice!) to enter code. 152 | // Regular hardware is supposed to only accept a STARTUP 153 | // when it is in the halted state due to an INIT. So the second 154 | // should be ignored, but it is part of the official Intel algorithm. 155 | // Bochs complains about the second one. Too bad for Bochs. 156 | for(i = 0; i < 2; i++){ 157 | lapicw(ICRHI, apicid<<24); 158 | lapicw(ICRLO, STARTUP | (addr>>12)); 159 | microdelay(200); 160 | } 161 | } 162 | 163 | #define CMOS_STATA 0x0a 164 | #define CMOS_STATB 0x0b 165 | #define CMOS_UIP (1 << 7) // RTC update in progress 166 | 167 | #define SECS 0x00 168 | #define MINS 0x02 169 | #define HOURS 0x04 170 | #define DAY 0x07 171 | #define MONTH 0x08 172 | #define YEAR 0x09 173 | 174 | static uint 175 | cmos_read(uint reg) 176 | { 177 | outb(CMOS_PORT, reg); 178 | microdelay(200); 179 | 180 | return inb(CMOS_RETURN); 181 | } 182 | 183 | static void 184 | fill_rtcdate(struct rtcdate *r) 185 | { 186 | r->second = cmos_read(SECS); 187 | r->minute = cmos_read(MINS); 188 | r->hour = cmos_read(HOURS); 189 | r->day = cmos_read(DAY); 190 | r->month = cmos_read(MONTH); 191 | r->year = cmos_read(YEAR); 192 | } 193 | 194 | // qemu seems to use 24-hour GWT and the values are BCD encoded 195 | void 196 | cmostime(struct rtcdate *r) 197 | { 198 | struct rtcdate t1, t2; 199 | int sb, bcd; 200 | 201 | sb = cmos_read(CMOS_STATB); 202 | 203 | bcd = (sb & (1 << 2)) == 0; 204 | 205 | // make sure CMOS doesn't modify time while we read it 206 | for(;;) { 207 | fill_rtcdate(&t1); 208 | if(cmos_read(CMOS_STATA) & CMOS_UIP) 209 | continue; 210 | fill_rtcdate(&t2); 211 | if(memcmp(&t1, &t2, sizeof(t1)) == 0) 212 | break; 213 | } 214 | 215 | // convert 216 | if(bcd) { 217 | #define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf)) 218 | CONV(second); 219 | CONV(minute); 220 | CONV(hour ); 221 | CONV(day ); 222 | CONV(month ); 223 | CONV(year ); 224 | #undef CONV 225 | } 226 | 227 | *r = t1; 228 | r->year += 2000; 229 | } 230 | -------------------------------------------------------------------------------- /ln.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | if(argc != 3){ 9 | printf(2, "Usage: ln old new\n"); 10 | exit(); 11 | } 12 | if(link(argv[1], argv[2]) < 0) 13 | printf(2, "link %s %s: failed\n", argv[1], argv[2]); 14 | exit(); 15 | } 16 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "spinlock.h" 5 | #include "sleeplock.h" 6 | #include "fs.h" 7 | #include "buf.h" 8 | 9 | // Simple logging that allows concurrent FS system calls. 10 | // 11 | // A log transaction contains the updates of multiple FS system 12 | // calls. The logging system only commits when there are 13 | // no FS system calls active. Thus there is never 14 | // any reasoning required about whether a commit might 15 | // write an uncommitted system call's updates to disk. 16 | // 17 | // A system call should call begin_op()/end_op() to mark 18 | // its start and end. Usually begin_op() just increments 19 | // the count of in-progress FS system calls and returns. 20 | // But if it thinks the log is close to running out, it 21 | // sleeps until the last outstanding end_op() commits. 22 | // 23 | // The log is a physical re-do log containing disk blocks. 24 | // The on-disk log format: 25 | // header block, containing block #s for block A, B, C, ... 26 | // block A 27 | // block B 28 | // block C 29 | // ... 30 | // Log appends are synchronous. 31 | 32 | // Contents of the header block, used for both the on-disk header block 33 | // and to keep track in memory of logged block# before commit. 34 | struct logheader { 35 | int n; 36 | int block[LOGSIZE]; 37 | }; 38 | 39 | struct log { 40 | struct spinlock lock; 41 | int start; 42 | int size; 43 | int outstanding; // how many FS sys calls are executing. 44 | int committing; // in commit(), please wait. 45 | int dev; 46 | struct logheader lh; 47 | }; 48 | struct log log; 49 | 50 | static void recover_from_log(void); 51 | static void commit(); 52 | 53 | void 54 | initlog(int dev) 55 | { 56 | if (sizeof(struct logheader) >= BSIZE) 57 | panic("initlog: too big logheader"); 58 | 59 | struct superblock sb; 60 | initlock(&log.lock, "log"); 61 | readsb(dev, &sb); 62 | log.start = sb.logstart; 63 | log.size = sb.nlog; 64 | log.dev = dev; 65 | recover_from_log(); 66 | } 67 | 68 | // Copy committed blocks from log to their home location 69 | static void 70 | install_trans(void) 71 | { 72 | int tail; 73 | 74 | for (tail = 0; tail < log.lh.n; tail++) { 75 | struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block 76 | struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst 77 | memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst 78 | bwrite(dbuf); // write dst to disk 79 | brelse(lbuf); 80 | brelse(dbuf); 81 | } 82 | } 83 | 84 | // Read the log header from disk into the in-memory log header 85 | static void 86 | read_head(void) 87 | { 88 | struct buf *buf = bread(log.dev, log.start); 89 | struct logheader *lh = (struct logheader *) (buf->data); 90 | int i; 91 | log.lh.n = lh->n; 92 | for (i = 0; i < log.lh.n; i++) { 93 | log.lh.block[i] = lh->block[i]; 94 | } 95 | brelse(buf); 96 | } 97 | 98 | // Write in-memory log header to disk. 99 | // This is the true point at which the 100 | // current transaction commits. 101 | static void 102 | write_head(void) 103 | { 104 | struct buf *buf = bread(log.dev, log.start); 105 | struct logheader *hb = (struct logheader *) (buf->data); 106 | int i; 107 | hb->n = log.lh.n; 108 | for (i = 0; i < log.lh.n; i++) { 109 | hb->block[i] = log.lh.block[i]; 110 | } 111 | bwrite(buf); 112 | brelse(buf); 113 | } 114 | 115 | static void 116 | recover_from_log(void) 117 | { 118 | read_head(); 119 | install_trans(); // if committed, copy from log to disk 120 | log.lh.n = 0; 121 | write_head(); // clear the log 122 | } 123 | 124 | // called at the start of each FS system call. 125 | void 126 | begin_op(void) 127 | { 128 | acquire(&log.lock); 129 | while(1){ 130 | if(log.committing){ 131 | sleep(&log, &log.lock); 132 | } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){ 133 | // this op might exhaust log space; wait for commit. 134 | sleep(&log, &log.lock); 135 | } else { 136 | log.outstanding += 1; 137 | release(&log.lock); 138 | break; 139 | } 140 | } 141 | } 142 | 143 | // called at the end of each FS system call. 144 | // commits if this was the last outstanding operation. 145 | void 146 | end_op(void) 147 | { 148 | int do_commit = 0; 149 | 150 | acquire(&log.lock); 151 | log.outstanding -= 1; 152 | if(log.committing) 153 | panic("log.committing"); 154 | if(log.outstanding == 0){ 155 | do_commit = 1; 156 | log.committing = 1; 157 | } else { 158 | // begin_op() may be waiting for log space, 159 | // and decrementing log.outstanding has decreased 160 | // the amount of reserved space. 161 | wakeup(&log); 162 | } 163 | release(&log.lock); 164 | 165 | if(do_commit){ 166 | // call commit w/o holding locks, since not allowed 167 | // to sleep with locks. 168 | commit(); 169 | acquire(&log.lock); 170 | log.committing = 0; 171 | wakeup(&log); 172 | release(&log.lock); 173 | } 174 | } 175 | 176 | // Copy modified blocks from cache to log. 177 | static void 178 | write_log(void) 179 | { 180 | int tail; 181 | 182 | for (tail = 0; tail < log.lh.n; tail++) { 183 | struct buf *to = bread(log.dev, log.start+tail+1); // log block 184 | struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block 185 | memmove(to->data, from->data, BSIZE); 186 | bwrite(to); // write the log 187 | brelse(from); 188 | brelse(to); 189 | } 190 | } 191 | 192 | static void 193 | commit() 194 | { 195 | if (log.lh.n > 0) { 196 | write_log(); // Write modified blocks from cache to log 197 | write_head(); // Write header to disk -- the real commit 198 | install_trans(); // Now install writes to home locations 199 | log.lh.n = 0; 200 | write_head(); // Erase the transaction from the log 201 | } 202 | } 203 | 204 | // Caller has modified b->data and is done with the buffer. 205 | // Record the block number and pin in the cache with B_DIRTY. 206 | // commit()/write_log() will do the disk write. 207 | // 208 | // log_write() replaces bwrite(); a typical use is: 209 | // bp = bread(...) 210 | // modify bp->data[] 211 | // log_write(bp) 212 | // brelse(bp) 213 | void 214 | log_write(struct buf *b) 215 | { 216 | int i; 217 | 218 | if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) 219 | panic("too big a transaction"); 220 | if (log.outstanding < 1) 221 | panic("log_write outside of trans"); 222 | 223 | acquire(&log.lock); 224 | for (i = 0; i < log.lh.n; i++) { 225 | if (log.lh.block[i] == b->blockno) // log absorbtion 226 | break; 227 | } 228 | log.lh.block[i] = b->blockno; 229 | if (i == log.lh.n) 230 | log.lh.n++; 231 | b->flags |= B_DIRTY; // prevent eviction 232 | release(&log.lock); 233 | } 234 | 235 | -------------------------------------------------------------------------------- /ls.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | #include "fs.h" 5 | 6 | char* 7 | fmtname(char *path) 8 | { 9 | static char buf[DIRSIZ+1]; 10 | char *p; 11 | 12 | // Find first character after last slash. 13 | for(p=path+strlen(path); p >= path && *p != '/'; p--) 14 | ; 15 | p++; 16 | 17 | // Return blank-padded name. 18 | if(strlen(p) >= DIRSIZ) 19 | return p; 20 | memmove(buf, p, strlen(p)); 21 | memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); 22 | return buf; 23 | } 24 | 25 | void 26 | ls(char *path) 27 | { 28 | char buf[512], *p; 29 | int fd; 30 | struct dirent de; 31 | struct stat st; 32 | 33 | if((fd = open(path, 0)) < 0){ 34 | printf(2, "ls: cannot open %s\n", path); 35 | return; 36 | } 37 | 38 | if(fstat(fd, &st) < 0){ 39 | printf(2, "ls: cannot stat %s\n", path); 40 | close(fd); 41 | return; 42 | } 43 | 44 | switch(st.type){ 45 | case T_FILE: 46 | printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); 47 | break; 48 | 49 | case T_DIR: 50 | if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ 51 | printf(1, "ls: path too long\n"); 52 | break; 53 | } 54 | strcpy(buf, path); 55 | p = buf+strlen(buf); 56 | *p++ = '/'; 57 | while(read(fd, &de, sizeof(de)) == sizeof(de)){ 58 | if(de.inum == 0) 59 | continue; 60 | memmove(p, de.name, DIRSIZ); 61 | p[DIRSIZ] = 0; 62 | if(stat(buf, &st) < 0){ 63 | printf(1, "ls: cannot stat %s\n", buf); 64 | continue; 65 | } 66 | printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); 67 | } 68 | break; 69 | } 70 | close(fd); 71 | } 72 | 73 | int 74 | main(int argc, char *argv[]) 75 | { 76 | int i; 77 | 78 | if(argc < 2){ 79 | ls("."); 80 | exit(); 81 | } 82 | for(i=1; istarted), 1); // tell startothers() we're up 57 | scheduler(); // start running processes 58 | } 59 | 60 | pde_t entrypgdir[]; // For entry.S 61 | 62 | // Start the non-boot (AP) processors. 63 | static void 64 | startothers(void) 65 | { 66 | extern uchar _binary_entryother_start[], _binary_entryother_size[]; 67 | uchar *code; 68 | struct cpu *c; 69 | char *stack; 70 | 71 | // Write entry code to unused memory at 0x7000. 72 | // The linker has placed the image of entryother.S in 73 | // _binary_entryother_start. 74 | code = P2V(0x7000); 75 | memmove(code, _binary_entryother_start, (uint)_binary_entryother_size); 76 | 77 | for(c = cpus; c < cpus+ncpu; c++){ 78 | if(c == mycpu()) // We've started already. 79 | continue; 80 | 81 | // Tell entryother.S what stack to use, where to enter, and what 82 | // pgdir to use. We cannot use kpgdir yet, because the AP processor 83 | // is running in low memory, so we use entrypgdir for the APs too. 84 | stack = kalloc(); 85 | *(void**)(code-4) = stack + KSTACKSIZE; 86 | *(void(**)(void))(code-8) = mpenter; 87 | *(int**)(code-12) = (void *) V2P(entrypgdir); 88 | 89 | lapicstartap(c->apicid, V2P(code)); 90 | 91 | // wait for cpu to finish mpmain() 92 | while(c->started == 0) 93 | ; 94 | } 95 | } 96 | 97 | // The boot page table used in entry.S and entryother.S. 98 | // Page directories (and page tables) must start on page boundaries, 99 | // hence the __aligned__ attribute. 100 | // PTE_PS in a page directory entry enables 4Mbyte pages. 101 | 102 | __attribute__((__aligned__(PGSIZE))) 103 | pde_t entrypgdir[NPDENTRIES] = { 104 | // Map VA's [0, 4MB) to PA's [0, 4MB) 105 | [0] = (0) | PTE_P | PTE_W | PTE_PS, 106 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 107 | [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS, 108 | }; 109 | 110 | //PAGEBREAK! 111 | // Blank page. 112 | //PAGEBREAK! 113 | // Blank page. 114 | //PAGEBREAK! 115 | // Blank page. 116 | 117 | -------------------------------------------------------------------------------- /memide.c: -------------------------------------------------------------------------------- 1 | // Fake IDE disk; stores blocks in memory. 2 | // Useful for running kernel without scratch disk. 3 | 4 | #include "types.h" 5 | #include "defs.h" 6 | #include "param.h" 7 | #include "mmu.h" 8 | #include "proc.h" 9 | #include "x86.h" 10 | #include "traps.h" 11 | #include "spinlock.h" 12 | #include "sleeplock.h" 13 | #include "fs.h" 14 | #include "buf.h" 15 | 16 | extern uchar _binary_fs_img_start[], _binary_fs_img_size[]; 17 | 18 | static int disksize; 19 | static uchar *memdisk; 20 | 21 | void 22 | ideinit(void) 23 | { 24 | memdisk = _binary_fs_img_start; 25 | disksize = (uint)_binary_fs_img_size/BSIZE; 26 | } 27 | 28 | // Interrupt handler. 29 | void 30 | ideintr(void) 31 | { 32 | // no-op 33 | } 34 | 35 | // Sync buf with disk. 36 | // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 37 | // Else if B_VALID is not set, read buf from disk, set B_VALID. 38 | void 39 | iderw(struct buf *b) 40 | { 41 | uchar *p; 42 | 43 | if(!holdingsleep(&b->lock)) 44 | panic("iderw: buf not locked"); 45 | if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 46 | panic("iderw: nothing to do"); 47 | if(b->dev != 1) 48 | panic("iderw: request not for disk 1"); 49 | if(b->blockno >= disksize) 50 | panic("iderw: block out of range"); 51 | 52 | p = memdisk + b->blockno*BSIZE; 53 | 54 | if(b->flags & B_DIRTY){ 55 | b->flags &= ~B_DIRTY; 56 | memmove(p, b->data, BSIZE); 57 | } else 58 | memmove(b->data, p, BSIZE); 59 | b->flags |= B_VALID; 60 | } 61 | -------------------------------------------------------------------------------- /memlayout.h: -------------------------------------------------------------------------------- 1 | // Memory layout 2 | 3 | #define EXTMEM 0x100000 // Start of extended memory 4 | #define PHYSTOP 0xE000000 // Top physical memory 5 | #define DEVSPACE 0xFE000000 // Other devices are at high addresses 6 | 7 | // Key addresses for address space layout (see kmap in vm.c for layout) 8 | #define KERNBASE 0x80000000 // First kernel virtual address 9 | #define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked 10 | 11 | #define V2P(a) (((uint) (a)) - KERNBASE) 12 | #define P2V(a) ((void *)(((char *) (a)) + KERNBASE)) 13 | 14 | #define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts 15 | #define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts 16 | -------------------------------------------------------------------------------- /mkdir.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | if(argc < 2){ 11 | printf(2, "Usage: mkdir files...\n"); 12 | exit(); 13 | } 14 | 15 | for(i = 1; i < argc; i++){ 16 | if(mkdir(argv[i]) < 0){ 17 | printf(2, "mkdir: %s failed to create\n", argv[i]); 18 | break; 19 | } 20 | } 21 | 22 | exit(); 23 | } 24 | -------------------------------------------------------------------------------- /mkfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define stat xv6_stat // avoid clash with host struct stat 9 | #include "types.h" 10 | #include "fs.h" 11 | #include "stat.h" 12 | #include "param.h" 13 | 14 | #ifndef static_assert 15 | #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) 16 | #endif 17 | 18 | #define NINODES 200 19 | 20 | // Disk layout: 21 | // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] 22 | 23 | int nbitmap = FSSIZE/(BSIZE*8) + 1; 24 | int ninodeblocks = NINODES / IPB + 1; 25 | int nlog = LOGSIZE; 26 | int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) 27 | int nblocks; // Number of data blocks 28 | 29 | int fsfd; 30 | struct superblock sb; 31 | char zeroes[BSIZE]; 32 | uint freeinode = 1; 33 | uint freeblock; 34 | 35 | 36 | void balloc(int); 37 | void wsect(uint, void*); 38 | void winode(uint, struct dinode*); 39 | void rinode(uint inum, struct dinode *ip); 40 | void rsect(uint sec, void *buf); 41 | uint ialloc(ushort type); 42 | void iappend(uint inum, void *p, int n); 43 | 44 | // convert to intel byte order 45 | ushort 46 | xshort(ushort x) 47 | { 48 | ushort y; 49 | uchar *a = (uchar*)&y; 50 | a[0] = x; 51 | a[1] = x >> 8; 52 | return y; 53 | } 54 | 55 | uint 56 | xint(uint x) 57 | { 58 | uint y; 59 | uchar *a = (uchar*)&y; 60 | a[0] = x; 61 | a[1] = x >> 8; 62 | a[2] = x >> 16; 63 | a[3] = x >> 24; 64 | return y; 65 | } 66 | 67 | int 68 | main(int argc, char *argv[]) 69 | { 70 | int i, cc, fd; 71 | uint rootino, inum, off; 72 | struct dirent de; 73 | char buf[BSIZE]; 74 | struct dinode din; 75 | 76 | 77 | static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); 78 | 79 | if(argc < 2){ 80 | fprintf(stderr, "Usage: mkfs fs.img files...\n"); 81 | exit(1); 82 | } 83 | 84 | assert((BSIZE % sizeof(struct dinode)) == 0); 85 | assert((BSIZE % sizeof(struct dirent)) == 0); 86 | 87 | fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); 88 | if(fsfd < 0){ 89 | perror(argv[1]); 90 | exit(1); 91 | } 92 | 93 | // 1 fs block = 1 disk sector 94 | nmeta = 2 + nlog + ninodeblocks + nbitmap; 95 | nblocks = FSSIZE - nmeta; 96 | 97 | sb.size = xint(FSSIZE); 98 | sb.nblocks = xint(nblocks); 99 | sb.ninodes = xint(NINODES); 100 | sb.nlog = xint(nlog); 101 | sb.logstart = xint(2); 102 | sb.inodestart = xint(2+nlog); 103 | sb.bmapstart = xint(2+nlog+ninodeblocks); 104 | 105 | printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", 106 | nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); 107 | 108 | freeblock = nmeta; // the first free block that we can allocate 109 | 110 | for(i = 0; i < FSSIZE; i++) 111 | wsect(i, zeroes); 112 | 113 | memset(buf, 0, sizeof(buf)); 114 | memmove(buf, &sb, sizeof(sb)); 115 | wsect(1, buf); 116 | 117 | rootino = ialloc(T_DIR); 118 | assert(rootino == ROOTINO); 119 | 120 | bzero(&de, sizeof(de)); 121 | de.inum = xshort(rootino); 122 | strcpy(de.name, "."); 123 | iappend(rootino, &de, sizeof(de)); 124 | 125 | bzero(&de, sizeof(de)); 126 | de.inum = xshort(rootino); 127 | strcpy(de.name, ".."); 128 | iappend(rootino, &de, sizeof(de)); 129 | 130 | for(i = 2; i < argc; i++){ 131 | assert(index(argv[i], '/') == 0); 132 | 133 | if((fd = open(argv[i], 0)) < 0){ 134 | perror(argv[i]); 135 | exit(1); 136 | } 137 | 138 | // Skip leading _ in name when writing to file system. 139 | // The binaries are named _rm, _cat, etc. to keep the 140 | // build operating system from trying to execute them 141 | // in place of system binaries like rm and cat. 142 | if(argv[i][0] == '_') 143 | ++argv[i]; 144 | 145 | inum = ialloc(T_FILE); 146 | 147 | bzero(&de, sizeof(de)); 148 | de.inum = xshort(inum); 149 | strncpy(de.name, argv[i], DIRSIZ); 150 | iappend(rootino, &de, sizeof(de)); 151 | 152 | while((cc = read(fd, buf, sizeof(buf))) > 0) 153 | iappend(inum, buf, cc); 154 | 155 | close(fd); 156 | } 157 | 158 | // fix size of root inode dir 159 | rinode(rootino, &din); 160 | off = xint(din.size); 161 | off = ((off/BSIZE) + 1) * BSIZE; 162 | din.size = xint(off); 163 | winode(rootino, &din); 164 | 165 | balloc(freeblock); 166 | 167 | exit(0); 168 | } 169 | 170 | void 171 | wsect(uint sec, void *buf) 172 | { 173 | if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ 174 | perror("lseek"); 175 | exit(1); 176 | } 177 | if(write(fsfd, buf, BSIZE) != BSIZE){ 178 | perror("write"); 179 | exit(1); 180 | } 181 | } 182 | 183 | void 184 | winode(uint inum, struct dinode *ip) 185 | { 186 | char buf[BSIZE]; 187 | uint bn; 188 | struct dinode *dip; 189 | 190 | bn = IBLOCK(inum, sb); 191 | rsect(bn, buf); 192 | dip = ((struct dinode*)buf) + (inum % IPB); 193 | *dip = *ip; 194 | wsect(bn, buf); 195 | } 196 | 197 | void 198 | rinode(uint inum, struct dinode *ip) 199 | { 200 | char buf[BSIZE]; 201 | uint bn; 202 | struct dinode *dip; 203 | 204 | bn = IBLOCK(inum, sb); 205 | rsect(bn, buf); 206 | dip = ((struct dinode*)buf) + (inum % IPB); 207 | *ip = *dip; 208 | } 209 | 210 | void 211 | rsect(uint sec, void *buf) 212 | { 213 | if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ 214 | perror("lseek"); 215 | exit(1); 216 | } 217 | if(read(fsfd, buf, BSIZE) != BSIZE){ 218 | perror("read"); 219 | exit(1); 220 | } 221 | } 222 | 223 | uint 224 | ialloc(ushort type) 225 | { 226 | uint inum = freeinode++; 227 | struct dinode din; 228 | 229 | bzero(&din, sizeof(din)); 230 | din.type = xshort(type); 231 | din.nlink = xshort(1); 232 | din.size = xint(0); 233 | winode(inum, &din); 234 | return inum; 235 | } 236 | 237 | void 238 | balloc(int used) 239 | { 240 | uchar buf[BSIZE]; 241 | int i; 242 | 243 | printf("balloc: first %d blocks have been allocated\n", used); 244 | assert(used < BSIZE*8); 245 | bzero(buf, BSIZE); 246 | for(i = 0; i < used; i++){ 247 | buf[i/8] = buf[i/8] | (0x1 << (i%8)); 248 | } 249 | printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); 250 | wsect(sb.bmapstart, buf); 251 | } 252 | 253 | #define min(a, b) ((a) < (b) ? (a) : (b)) 254 | 255 | void 256 | iappend(uint inum, void *xp, int n) 257 | { 258 | char *p = (char*)xp; 259 | uint fbn, off, n1; 260 | struct dinode din; 261 | char buf[BSIZE]; 262 | uint indirect[NINDIRECT]; 263 | uint x; 264 | 265 | rinode(inum, &din); 266 | off = xint(din.size); 267 | // printf("append inum %d at off %d sz %d\n", inum, off, n); 268 | while(n > 0){ 269 | fbn = off / BSIZE; 270 | assert(fbn < MAXFILE); 271 | if(fbn < NDIRECT){ 272 | if(xint(din.addrs[fbn]) == 0){ 273 | din.addrs[fbn] = xint(freeblock++); 274 | } 275 | x = xint(din.addrs[fbn]); 276 | } else { 277 | if(xint(din.addrs[NDIRECT]) == 0){ 278 | din.addrs[NDIRECT] = xint(freeblock++); 279 | } 280 | rsect(xint(din.addrs[NDIRECT]), (char*)indirect); 281 | if(indirect[fbn - NDIRECT] == 0){ 282 | indirect[fbn - NDIRECT] = xint(freeblock++); 283 | wsect(xint(din.addrs[NDIRECT]), (char*)indirect); 284 | } 285 | x = xint(indirect[fbn-NDIRECT]); 286 | } 287 | n1 = min(n, (fbn + 1) * BSIZE - off); 288 | rsect(x, buf); 289 | bcopy(p, buf + off - (fbn * BSIZE), n1); 290 | wsect(x, buf); 291 | n -= n1; 292 | off += n1; 293 | p += n1; 294 | } 295 | din.size = xint(off); 296 | winode(inum, &din); 297 | } 298 | -------------------------------------------------------------------------------- /mmu.h: -------------------------------------------------------------------------------- 1 | // This file contains definitions for the 2 | // x86 memory management unit (MMU). 3 | 4 | // Eflags register 5 | #define FL_IF 0x00000200 // Interrupt Enable 6 | 7 | // Control Register flags 8 | #define CR0_PE 0x00000001 // Protection Enable 9 | #define CR0_WP 0x00010000 // Write Protect 10 | #define CR0_PG 0x80000000 // Paging 11 | 12 | #define CR4_PSE 0x00000010 // Page size extension 13 | 14 | // various segment selectors. 15 | #define SEG_KCODE 1 // kernel code 16 | #define SEG_KDATA 2 // kernel data+stack 17 | #define SEG_UCODE 3 // user code 18 | #define SEG_UDATA 4 // user data+stack 19 | #define SEG_TSS 5 // this process's task state 20 | 21 | // cpu->gdt[NSEGS] holds the above segments. 22 | #define NSEGS 6 23 | 24 | #ifndef __ASSEMBLER__ 25 | // Segment Descriptor 26 | struct segdesc { 27 | uint lim_15_0 : 16; // Low bits of segment limit 28 | uint base_15_0 : 16; // Low bits of segment base address 29 | uint base_23_16 : 8; // Middle bits of segment base address 30 | uint type : 4; // Segment type (see STS_ constants) 31 | uint s : 1; // 0 = system, 1 = application 32 | uint dpl : 2; // Descriptor Privilege Level 33 | uint p : 1; // Present 34 | uint lim_19_16 : 4; // High bits of segment limit 35 | uint avl : 1; // Unused (available for software use) 36 | uint rsv1 : 1; // Reserved 37 | uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment 38 | uint g : 1; // Granularity: limit scaled by 4K when set 39 | uint base_31_24 : 8; // High bits of segment base address 40 | }; 41 | 42 | // Normal segment 43 | #define SEG(type, base, lim, dpl) (struct segdesc) \ 44 | { ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ 45 | ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ 46 | (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } 47 | #define SEG16(type, base, lim, dpl) (struct segdesc) \ 48 | { (lim) & 0xffff, (uint)(base) & 0xffff, \ 49 | ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ 50 | (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 } 51 | #endif 52 | 53 | #define DPL_USER 0x3 // User DPL 54 | 55 | // Application segment type bits 56 | #define STA_X 0x8 // Executable segment 57 | #define STA_W 0x2 // Writeable (non-executable segments) 58 | #define STA_R 0x2 // Readable (executable segments) 59 | 60 | // System segment type bits 61 | #define STS_T32A 0x9 // Available 32-bit TSS 62 | #define STS_IG32 0xE // 32-bit Interrupt Gate 63 | #define STS_TG32 0xF // 32-bit Trap Gate 64 | 65 | // A virtual address 'la' has a three-part structure as follows: 66 | // 67 | // +--------10------+-------10-------+---------12----------+ 68 | // | Page Directory | Page Table | Offset within Page | 69 | // | Index | Index | | 70 | // +----------------+----------------+---------------------+ 71 | // \--- PDX(va) --/ \--- PTX(va) --/ 72 | 73 | // page directory index 74 | #define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF) 75 | 76 | // page table index 77 | #define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF) 78 | 79 | // construct virtual address from indexes and offset 80 | #define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) 81 | 82 | // Page directory and page table constants. 83 | #define NPDENTRIES 1024 // # directory entries per page directory 84 | #define NPTENTRIES 1024 // # PTEs per page table 85 | #define PGSIZE 4096 // bytes mapped by a page 86 | 87 | #define PTXSHIFT 12 // offset of PTX in a linear address 88 | #define PDXSHIFT 22 // offset of PDX in a linear address 89 | 90 | #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) 91 | #define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) 92 | 93 | // Page table/directory entry flags. 94 | #define PTE_P 0x001 // Present 95 | #define PTE_W 0x002 // Writeable 96 | #define PTE_U 0x004 // User 97 | #define PTE_PS 0x080 // Page Size 98 | 99 | // Address in page table or page directory entry 100 | #define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF) 101 | #define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF) 102 | 103 | #ifndef __ASSEMBLER__ 104 | typedef uint pte_t; 105 | 106 | // Task state segment format 107 | struct taskstate { 108 | uint link; // Old ts selector 109 | uint esp0; // Stack pointers and segment selectors 110 | ushort ss0; // after an increase in privilege level 111 | ushort padding1; 112 | uint *esp1; 113 | ushort ss1; 114 | ushort padding2; 115 | uint *esp2; 116 | ushort ss2; 117 | ushort padding3; 118 | void *cr3; // Page directory base 119 | uint *eip; // Saved state from last task switch 120 | uint eflags; 121 | uint eax; // More saved state (registers) 122 | uint ecx; 123 | uint edx; 124 | uint ebx; 125 | uint *esp; 126 | uint *ebp; 127 | uint esi; 128 | uint edi; 129 | ushort es; // Even more saved state (segment selectors) 130 | ushort padding4; 131 | ushort cs; 132 | ushort padding5; 133 | ushort ss; 134 | ushort padding6; 135 | ushort ds; 136 | ushort padding7; 137 | ushort fs; 138 | ushort padding8; 139 | ushort gs; 140 | ushort padding9; 141 | ushort ldt; 142 | ushort padding10; 143 | ushort t; // Trap on task switch 144 | ushort iomb; // I/O map base address 145 | }; 146 | 147 | // Gate descriptors for interrupts and traps 148 | struct gatedesc { 149 | uint off_15_0 : 16; // low 16 bits of offset in segment 150 | uint cs : 16; // code segment selector 151 | uint args : 5; // # args, 0 for interrupt/trap gates 152 | uint rsv1 : 3; // reserved(should be zero I guess) 153 | uint type : 4; // type(STS_{IG32,TG32}) 154 | uint s : 1; // must be 0 (system) 155 | uint dpl : 2; // descriptor(meaning new) privilege level 156 | uint p : 1; // Present 157 | uint off_31_16 : 16; // high bits of offset in segment 158 | }; 159 | 160 | // Set up a normal interrupt/trap gate descriptor. 161 | // - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate. 162 | // interrupt gate clears FL_IF, trap gate leaves FL_IF alone 163 | // - sel: Code segment selector for interrupt/trap handler 164 | // - off: Offset in code segment for interrupt/trap handler 165 | // - dpl: Descriptor Privilege Level - 166 | // the privilege level required for software to invoke 167 | // this interrupt/trap gate explicitly using an int instruction. 168 | #define SETGATE(gate, istrap, sel, off, d) \ 169 | { \ 170 | (gate).off_15_0 = (uint)(off) & 0xffff; \ 171 | (gate).cs = (sel); \ 172 | (gate).args = 0; \ 173 | (gate).rsv1 = 0; \ 174 | (gate).type = (istrap) ? STS_TG32 : STS_IG32; \ 175 | (gate).s = 0; \ 176 | (gate).dpl = (d); \ 177 | (gate).p = 1; \ 178 | (gate).off_31_16 = (uint)(off) >> 16; \ 179 | } 180 | 181 | #endif 182 | -------------------------------------------------------------------------------- /mp.c: -------------------------------------------------------------------------------- 1 | // Multiprocessor support 2 | // Search memory for MP description structures. 3 | // http://developer.intel.com/design/pentium/datashts/24201606.pdf 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "memlayout.h" 9 | #include "mp.h" 10 | #include "x86.h" 11 | #include "mmu.h" 12 | #include "proc.h" 13 | 14 | struct cpu cpus[NCPU]; 15 | int ncpu; 16 | uchar ioapicid; 17 | 18 | static uchar 19 | sum(uchar *addr, int len) 20 | { 21 | int i, sum; 22 | 23 | sum = 0; 24 | for(i=0; iphysaddr == 0) 79 | return 0; 80 | conf = (struct mpconf*) P2V((uint) mp->physaddr); 81 | if(memcmp(conf, "PCMP", 4) != 0) 82 | return 0; 83 | if(conf->version != 1 && conf->version != 4) 84 | return 0; 85 | if(sum((uchar*)conf, conf->length) != 0) 86 | return 0; 87 | *pmp = mp; 88 | return conf; 89 | } 90 | 91 | void 92 | mpinit(void) 93 | { 94 | uchar *p, *e; 95 | int ismp; 96 | struct mp *mp; 97 | struct mpconf *conf; 98 | struct mpproc *proc; 99 | struct mpioapic *ioapic; 100 | 101 | if((conf = mpconfig(&mp)) == 0) 102 | panic("Expect to run on an SMP"); 103 | ismp = 1; 104 | lapic = (uint*)conf->lapicaddr; 105 | for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; papicid; // apicid may differ from ncpu 111 | ncpu++; 112 | } 113 | p += sizeof(struct mpproc); 114 | continue; 115 | case MPIOAPIC: 116 | ioapic = (struct mpioapic*)p; 117 | ioapicid = ioapic->apicno; 118 | p += sizeof(struct mpioapic); 119 | continue; 120 | case MPBUS: 121 | case MPIOINTR: 122 | case MPLINTR: 123 | p += 8; 124 | continue; 125 | default: 126 | ismp = 0; 127 | break; 128 | } 129 | } 130 | if(!ismp) 131 | panic("Didn't find a suitable machine"); 132 | 133 | if(mp->imcrp){ 134 | // Bochs doesn't support IMCR, so this doesn't run on Bochs. 135 | // But it would on real hardware. 136 | outb(0x22, 0x70); // Select IMCR 137 | outb(0x23, inb(0x23) | 1); // Mask external interrupts. 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /mp.h: -------------------------------------------------------------------------------- 1 | // See MultiProcessor Specification Version 1.[14] 2 | 3 | struct mp { // floating pointer 4 | uchar signature[4]; // "_MP_" 5 | void *physaddr; // phys addr of MP config table 6 | uchar length; // 1 7 | uchar specrev; // [14] 8 | uchar checksum; // all bytes must add up to 0 9 | uchar type; // MP system config type 10 | uchar imcrp; 11 | uchar reserved[3]; 12 | }; 13 | 14 | struct mpconf { // configuration table header 15 | uchar signature[4]; // "PCMP" 16 | ushort length; // total table length 17 | uchar version; // [14] 18 | uchar checksum; // all bytes must add up to 0 19 | uchar product[20]; // product id 20 | uint *oemtable; // OEM table pointer 21 | ushort oemlength; // OEM table length 22 | ushort entry; // entry count 23 | uint *lapicaddr; // address of local APIC 24 | ushort xlength; // extended table length 25 | uchar xchecksum; // extended table checksum 26 | uchar reserved; 27 | }; 28 | 29 | struct mpproc { // processor table entry 30 | uchar type; // entry type (0) 31 | uchar apicid; // local APIC id 32 | uchar version; // local APIC verison 33 | uchar flags; // CPU flags 34 | #define MPBOOT 0x02 // This proc is the bootstrap processor. 35 | uchar signature[4]; // CPU signature 36 | uint feature; // feature flags from CPUID instruction 37 | uchar reserved[8]; 38 | }; 39 | 40 | struct mpioapic { // I/O APIC table entry 41 | uchar type; // entry type (2) 42 | uchar apicno; // I/O APIC id 43 | uchar version; // I/O APIC version 44 | uchar flags; // I/O APIC flags 45 | uint *addr; // I/O APIC address 46 | }; 47 | 48 | // Table entry types 49 | #define MPPROC 0x00 // One per processor 50 | #define MPBUS 0x01 // One per bus 51 | #define MPIOAPIC 0x02 // One per I/O APIC 52 | #define MPIOINTR 0x03 // One per bus interrupt source 53 | #define MPLINTR 0x04 // One per system interrupt source 54 | 55 | //PAGEBREAK! 56 | // Blank page. 57 | -------------------------------------------------------------------------------- /param.h: -------------------------------------------------------------------------------- 1 | #define NPROC 64 // maximum number of processes 2 | #define KSTACKSIZE 4096 // size of per-process kernel stack 3 | #define NCPU 8 // maximum number of CPUs 4 | #define NOFILE 16 // open files per process 5 | #define NFILE 100 // open files per system 6 | #define NINODE 50 // maximum number of active i-nodes 7 | #define NDEV 10 // maximum major device number 8 | #define ROOTDEV 1 // device number of file system root disk 9 | #define MAXARG 32 // max exec arguments 10 | #define MAXOPBLOCKS 10 // max # of blocks any FS op writes 11 | #define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log 12 | #define NBUF (MAXOPBLOCKS*3) // size of disk block cache 13 | #define FSSIZE 1000 // size of file system in blocks 14 | 15 | -------------------------------------------------------------------------------- /picirq.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | #include "traps.h" 4 | 5 | // I/O Addresses of the two programmable interrupt controllers 6 | #define IO_PIC1 0x20 // Master (IRQs 0-7) 7 | #define IO_PIC2 0xA0 // Slave (IRQs 8-15) 8 | 9 | // Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware. 10 | void 11 | picinit(void) 12 | { 13 | // mask all interrupts 14 | outb(IO_PIC1+1, 0xFF); 15 | outb(IO_PIC2+1, 0xFF); 16 | } 17 | 18 | //PAGEBREAK! 19 | // Blank page. 20 | -------------------------------------------------------------------------------- /pipe.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "mmu.h" 5 | #include "proc.h" 6 | #include "fs.h" 7 | #include "spinlock.h" 8 | #include "sleeplock.h" 9 | #include "file.h" 10 | 11 | #define PIPESIZE 512 12 | 13 | struct pipe { 14 | struct spinlock lock; 15 | char data[PIPESIZE]; 16 | uint nread; // number of bytes read 17 | uint nwrite; // number of bytes written 18 | int readopen; // read fd is still open 19 | int writeopen; // write fd is still open 20 | }; 21 | 22 | int 23 | pipealloc(struct file **f0, struct file **f1) 24 | { 25 | struct pipe *p; 26 | 27 | p = 0; 28 | *f0 = *f1 = 0; 29 | if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) 30 | goto bad; 31 | if((p = (struct pipe*)kalloc()) == 0) 32 | goto bad; 33 | p->readopen = 1; 34 | p->writeopen = 1; 35 | p->nwrite = 0; 36 | p->nread = 0; 37 | initlock(&p->lock, "pipe"); 38 | (*f0)->type = FD_PIPE; 39 | (*f0)->readable = 1; 40 | (*f0)->writable = 0; 41 | (*f0)->pipe = p; 42 | (*f1)->type = FD_PIPE; 43 | (*f1)->readable = 0; 44 | (*f1)->writable = 1; 45 | (*f1)->pipe = p; 46 | return 0; 47 | 48 | //PAGEBREAK: 20 49 | bad: 50 | if(p) 51 | kfree((char*)p); 52 | if(*f0) 53 | fileclose(*f0); 54 | if(*f1) 55 | fileclose(*f1); 56 | return -1; 57 | } 58 | 59 | void 60 | pipeclose(struct pipe *p, int writable) 61 | { 62 | acquire(&p->lock); 63 | if(writable){ 64 | p->writeopen = 0; 65 | wakeup(&p->nread); 66 | } else { 67 | p->readopen = 0; 68 | wakeup(&p->nwrite); 69 | } 70 | if(p->readopen == 0 && p->writeopen == 0){ 71 | release(&p->lock); 72 | kfree((char*)p); 73 | } else 74 | release(&p->lock); 75 | } 76 | 77 | //PAGEBREAK: 40 78 | int 79 | pipewrite(struct pipe *p, char *addr, int n) 80 | { 81 | int i; 82 | 83 | acquire(&p->lock); 84 | for(i = 0; i < n; i++){ 85 | while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full 86 | if(p->readopen == 0 || myproc()->killed){ 87 | release(&p->lock); 88 | return -1; 89 | } 90 | wakeup(&p->nread); 91 | sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep 92 | } 93 | p->data[p->nwrite++ % PIPESIZE] = addr[i]; 94 | } 95 | wakeup(&p->nread); //DOC: pipewrite-wakeup1 96 | release(&p->lock); 97 | return n; 98 | } 99 | 100 | int 101 | piperead(struct pipe *p, char *addr, int n) 102 | { 103 | int i; 104 | 105 | acquire(&p->lock); 106 | while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty 107 | if(myproc()->killed){ 108 | release(&p->lock); 109 | return -1; 110 | } 111 | sleep(&p->nread, &p->lock); //DOC: piperead-sleep 112 | } 113 | for(i = 0; i < n; i++){ //DOC: piperead-copy 114 | if(p->nread == p->nwrite) 115 | break; 116 | addr[i] = p->data[p->nread++ % PIPESIZE]; 117 | } 118 | wakeup(&p->nwrite); //DOC: piperead-wakeup 119 | release(&p->lock); 120 | return i; 121 | } 122 | -------------------------------------------------------------------------------- /pr.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use POSIX qw(strftime); 4 | 5 | if($ARGV[0] eq "-h"){ 6 | shift @ARGV; 7 | $h = $ARGV[0]; 8 | shift @ARGV; 9 | }else{ 10 | $h = $ARGV[0]; 11 | } 12 | 13 | $page = 0; 14 | $now = strftime "%b %e %H:%M %Y", localtime; 15 | 16 | @lines = <>; 17 | for($i=0; $i<@lines; $i+=50){ 18 | print "\n\n"; 19 | ++$page; 20 | print "$now $h Page $page\n"; 21 | print "\n\n"; 22 | for($j=$i; $j<@lines && $j<$i +50; $j++){ 23 | $lines[$j] =~ s!//DOC.*!!; 24 | print $lines[$j]; 25 | } 26 | for(; $j<$i+50; $j++){ 27 | print "\n"; 28 | } 29 | $sheet = ""; 30 | if($lines[$i] =~ /^([0-9][0-9])[0-9][0-9] /){ 31 | $sheet = "Sheet $1"; 32 | } 33 | print "\n\n"; 34 | print "$sheet\n"; 35 | print "\n\n"; 36 | } 37 | -------------------------------------------------------------------------------- /printf.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | static void 6 | putc(int fd, char c) 7 | { 8 | write(fd, &c, 1); 9 | } 10 | 11 | static void 12 | printint(int fd, int xx, int base, int sgn) 13 | { 14 | static char digits[] = "0123456789ABCDEF"; 15 | char buf[16]; 16 | int i, neg; 17 | uint x; 18 | 19 | neg = 0; 20 | if(sgn && xx < 0){ 21 | neg = 1; 22 | x = -xx; 23 | } else { 24 | x = xx; 25 | } 26 | 27 | i = 0; 28 | do{ 29 | buf[i++] = digits[x % base]; 30 | }while((x /= base) != 0); 31 | if(neg) 32 | buf[i++] = '-'; 33 | 34 | while(--i >= 0) 35 | putc(fd, buf[i]); 36 | } 37 | 38 | // Print to the given fd. Only understands %d, %x, %p, %s. 39 | void 40 | printf(int fd, const char *fmt, ...) 41 | { 42 | char *s; 43 | int c, i, state; 44 | uint *ap; 45 | 46 | state = 0; 47 | ap = (uint*)(void*)&fmt + 1; 48 | for(i = 0; fmt[i]; i++){ 49 | c = fmt[i] & 0xff; 50 | if(state == 0){ 51 | if(c == '%'){ 52 | state = '%'; 53 | } else { 54 | putc(fd, c); 55 | } 56 | } else if(state == '%'){ 57 | if(c == 'd'){ 58 | printint(fd, *ap, 10, 1); 59 | ap++; 60 | } else if(c == 'x' || c == 'p'){ 61 | printint(fd, *ap, 16, 0); 62 | ap++; 63 | } else if(c == 's'){ 64 | s = (char*)*ap; 65 | ap++; 66 | if(s == 0) 67 | s = "(null)"; 68 | while(*s != 0){ 69 | putc(fd, *s); 70 | s++; 71 | } 72 | } else if(c == 'c'){ 73 | putc(fd, *ap); 74 | ap++; 75 | } else if(c == '%'){ 76 | putc(fd, c); 77 | } else { 78 | // Unknown % sequence. Print it to draw attention. 79 | putc(fd, '%'); 80 | putc(fd, c); 81 | } 82 | state = 0; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /printpcs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Decode the symbols from a panic EIP list 4 | 5 | # Find a working addr2line 6 | for p in i386-jos-elf-addr2line addr2line; do 7 | if which $p 2>&1 >/dev/null && \ 8 | $p -h 2>&1 | grep -q '\belf32-i386\b'; then 9 | break 10 | fi 11 | done 12 | 13 | # Enable as much pretty-printing as this addr2line can do 14 | $p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@" 15 | -------------------------------------------------------------------------------- /proc.h: -------------------------------------------------------------------------------- 1 | // Per-CPU state 2 | struct cpu { 3 | uchar apicid; // Local APIC ID 4 | struct context *scheduler; // swtch() here to enter scheduler 5 | struct taskstate ts; // Used by x86 to find stack for interrupt 6 | struct segdesc gdt[NSEGS]; // x86 global descriptor table 7 | volatile uint started; // Has the CPU started? 8 | int ncli; // Depth of pushcli nesting. 9 | int intena; // Were interrupts enabled before pushcli? 10 | struct proc *proc; // The process running on this cpu or null 11 | }; 12 | 13 | extern struct cpu cpus[NCPU]; 14 | extern int ncpu; 15 | 16 | //PAGEBREAK: 17 17 | // Saved registers for kernel context switches. 18 | // Don't need to save all the segment registers (%cs, etc), 19 | // because they are constant across kernel contexts. 20 | // Don't need to save %eax, %ecx, %edx, because the 21 | // x86 convention is that the caller has saved them. 22 | // Contexts are stored at the bottom of the stack they 23 | // describe; the stack pointer is the address of the context. 24 | // The layout of the context matches the layout of the stack in swtch.S 25 | // at the "Switch stacks" comment. Switch doesn't save eip explicitly, 26 | // but it is on the stack and allocproc() manipulates it. 27 | struct context { 28 | uint edi; 29 | uint esi; 30 | uint ebx; 31 | uint ebp; 32 | uint eip; 33 | }; 34 | 35 | enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; 36 | 37 | // Per-process state 38 | struct proc { 39 | uint sz; // Size of process memory (bytes) 40 | pde_t* pgdir; // Page table 41 | char *kstack; // Bottom of kernel stack for this process 42 | void *threadstack; // Address of thread stack to be freed 43 | enum procstate state; // Process state 44 | int pid; // Process ID 45 | struct proc *parent; // Parent process 46 | struct trapframe *tf; // Trap frame for current syscall 47 | struct context *context; // swtch() here to run process 48 | void *chan; // If non-zero, sleeping on chan 49 | int killed; // If non-zero, have been killed 50 | struct file *ofile[NOFILE]; // Open files 51 | struct inode *cwd; // Current directory 52 | char name[16]; // Process name (debugging) 53 | }; 54 | 55 | // Process memory is laid out contiguously, low addresses first: 56 | // text 57 | // original data and bss 58 | // fixed-size stack 59 | // expandable heap 60 | -------------------------------------------------------------------------------- /rm.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | if(argc < 2){ 11 | printf(2, "Usage: rm files...\n"); 12 | exit(); 13 | } 14 | 15 | for(i = 1; i < argc; i++){ 16 | if(unlink(argv[i]) < 0){ 17 | printf(2, "rm: %s failed to delete\n", argv[i]); 18 | break; 19 | } 20 | } 21 | 22 | exit(); 23 | } 24 | -------------------------------------------------------------------------------- /runoff: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo This script takes a minute to run. Be patient. 1>&2 4 | 5 | LC_CTYPE=C export LC_CTYPE 6 | 7 | # pad stdin to multiple of 120 lines 8 | pad() 9 | { 10 | awk '{print} END{for(; NR%120!=0; NR++) print ""}' 11 | } 12 | 13 | # create formatted (numbered) files 14 | mkdir -p fmt 15 | rm -f fmt/* 16 | cp README fmt 17 | echo > fmt/blank 18 | files=`grep -v '^#' runoff.list | awk '{print $1}'` 19 | n=99 20 | for i in $files 21 | do 22 | ./runoff1 -n $n $i >fmt/$i 23 | nn=`tail -1 fmt/$i | sed 's/ .*//; s/^0*//'` 24 | if [ "x$nn" != x ]; then 25 | n=$nn 26 | fi 27 | done 28 | 29 | # create table of contents 30 | cat toc.hdr >fmt/toc 31 | pr -e8 -t runoff.list | awk ' 32 | /^[a-z0-9]/ { 33 | s=$0 34 | f="fmt/"$1 35 | getline"fmt/tocdata" 40 | next 41 | } 42 | { 43 | print 44 | }' | pr -3 -t >>fmt/toc 45 | cat toc.ftr >>fmt/toc 46 | 47 | # check for bad alignments 48 | perl -e ' 49 | $leftwarn = 0; 50 | while(<>){ 51 | chomp; 52 | s!#.*!!; 53 | s!\s+! !g; 54 | s! +$!!; 55 | next if /^$/; 56 | 57 | if(/TOC: (\d+) (.*)/){ 58 | $toc{$2} = $1; 59 | next; 60 | } 61 | 62 | if(/sheet1: (left|right)$/){ 63 | print STDERR "assuming that sheet 1 is a $1 page. double-check!\n"; 64 | $left = $1 eq "left" ? "13579" : "02468"; 65 | $right = $1 eq "left" ? "02468" : "13579"; 66 | next; 67 | } 68 | 69 | if(/even: (.*)/){ 70 | $file = $1; 71 | if(!defined($toc{$file})){ 72 | print STDERR "Have no toc for $file\n"; 73 | next; 74 | } 75 | if($toc{$file} =~ /^\d\d[^0]/){ 76 | print STDERR "$file does not start on a fresh page.\n"; 77 | } 78 | next; 79 | } 80 | 81 | if(/odd: (.*)/){ 82 | $file = $1; 83 | if(!defined($toc{$file})){ 84 | print STDERR "Have no toc for $file\n"; 85 | next; 86 | } 87 | if($toc{$file} !~ /^\d\d5/){ 88 | print STDERR "$file does not start on a second half page.\n"; 89 | } 90 | next; 91 | } 92 | 93 | if(/(left|right): (.*)/){ 94 | $what = $1; 95 | $file = $2; 96 | if(!defined($toc{$file})){ 97 | print STDERR "Have no toc for $file\n"; 98 | next; 99 | } 100 | if($what eq "left" && !($toc{$file} =~ /^\d[$left][05]/)){ 101 | print STDERR "$file does not start on a left page [$toc{$file}]\n"; 102 | } 103 | # why does this not work if I inline $x in the if? 104 | $x = ($toc{$file} =~ /^\d[$right][05]/); 105 | if($what eq "right" && !$x){ 106 | print STDERR "$file does not start on a right page [$toc{$file}] [$x]\n"; 107 | } 108 | next; 109 | } 110 | 111 | print STDERR "Unknown spec: $_\n"; 112 | } 113 | ' fmt/tocdata runoff.spec 114 | 115 | # make definition list 116 | cd fmt 117 | perl -e ' 118 | while(<>) { 119 | chomp; 120 | 121 | s!//.*!!; 122 | s!/\*([^*]|[*][^/])*\*/!!g; 123 | s!\s! !g; 124 | s! +$!!; 125 | 126 | # look for declarations like char* x; 127 | if (/^[0-9]+ typedef .* u(int|short|long|char);/) { 128 | next; 129 | } 130 | if (/^[0-9]+ extern/) { 131 | next; 132 | } 133 | if (/^[0-9]+ struct [a-zA-Z0-9_]+;/) { 134 | next; 135 | } 136 | if (/^([0-9]+) #define +([A-za-z0-9_]+) +?\(.*/) { 137 | print "$1 $2\n" 138 | } 139 | elsif (/^([0-9]+) #define +([A-Za-z0-9_]+) +([^ ]+)/) { 140 | print "$1 $2 $3\n"; 141 | } 142 | elsif (/^([0-9]+) #define +([A-Za-z0-9_]+)/) { 143 | print "$1 $2\n"; 144 | } 145 | 146 | if(/^^([0-9]+) \.globl ([a-zA-Z0-9_]+)/){ 147 | $isglobl{$2} = 1; 148 | } 149 | if(/^^([0-9]+) ([a-zA-Z0-9_]+):$/ && $isglobl{$2}){ 150 | print "$1 $2\n"; 151 | } 152 | 153 | if (/\(/) { 154 | next; 155 | } 156 | 157 | if (/^([0-9]+) (((static|struct|extern|union|enum) +)*([A-Za-z0-9_]+))( .*)? +([A-Za-z_][A-Za-z0-9_]*)(,|;|=| =)/) { 158 | print "$1 $7\n"; 159 | } 160 | 161 | elsif(/^([0-9]+) (enum|struct|union) +([A-Za-z0-9_]+) +{/){ 162 | print "$1 $3\n"; 163 | } 164 | # TODO: enum members 165 | } 166 | ' $files >defs 167 | 168 | (for i in $files 169 | do 170 | case "$i" in 171 | *.S) 172 | cat $i | sed 's;#.*;;; s;//.*;;;' 173 | ;; 174 | *) 175 | cat $i | sed 's;//.*;;; s;"([^"\\]|\\.)*";;;' 176 | esac 177 | done 178 | ) >alltext 179 | 180 | perl -n -e 'print if s/^([0-9]+ [a-zA-Z0-9_]+)\(.*$/\1/;' alltext | 181 | egrep -v ' (STUB|usage|main|if|for)$' >>defs 182 | #perl -n -e 'print if s/^([0-9]+) STUB\(([a-zA-Z0-9_]+)\)$/\1 \2/;' alltext \ 183 | # >>defs 184 | ( 185 | >s.defs 186 | 187 | # make reference list 188 | for i in `awk '{print $2}' defs | sort -f | uniq` 189 | do 190 | defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'` 191 | echo $i $defs >>s.defs 192 | uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' alltext | awk '{print $1}'` 193 | if [ "x$defs" != "x$uses" ]; then 194 | echo $i $defs 195 | echo $uses |fmt -29 | sed 's/^/ /' 196 | # else 197 | # echo $i defined but not used >&2 198 | fi 199 | done 200 | ) >refs 201 | 202 | # build defs list 203 | awk ' 204 | { 205 | printf("%04d %s\n", $2, $1); 206 | for(i=3; i<=NF; i++) 207 | printf("%04d \" \n", $i); 208 | } 209 | ' s.defs > t.defs 210 | 211 | # format the whole thing 212 | ( 213 | ../pr.pl README 214 | ../pr.pl -h "table of contents" toc 215 | # pr -t -2 t.defs | ../pr.pl -h "definitions" | pad 216 | pr -t -l50 -2 refs | ../pr.pl -h "cross-references" | pad 217 | # pr.pl -h "definitions" -2 t.defs | pad 218 | # pr.pl -h "cross-references" -2 refs | pad 219 | ../pr.pl blank # make sheet 1 start on left page 220 | ../pr.pl blank 221 | for i in $files 222 | do 223 | ../pr.pl -h "xv6/$i" $i 224 | done 225 | ) | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >all.ps 226 | grep Pages: all.ps 227 | 228 | # if we have the nice font, use it 229 | nicefont=LucidaSans-Typewriter83 230 | if [ ! -f ../$nicefont ] 231 | then 232 | if git cat-file blob font:$nicefont > ../$nicefont~; then 233 | mv ../$nicefont~ ../$nicefont 234 | fi 235 | fi 236 | if [ -f ../$nicefont ] 237 | then 238 | echo nicefont 239 | (sed 1q all.ps; cat ../$nicefont; sed "1d; s/Courier/$nicefont/" all.ps) >allf.ps 240 | else 241 | echo ugly font! 242 | cp all.ps allf.ps 243 | fi 244 | ps2pdf allf.ps ../xv6.pdf 245 | # cd .. 246 | # pdftops xv6.pdf xv6.ps 247 | -------------------------------------------------------------------------------- /runoff.list: -------------------------------------------------------------------------------- 1 | # basic headers 2 | types.h 3 | param.h 4 | memlayout.h 5 | defs.h 6 | x86.h 7 | asm.h 8 | mmu.h 9 | elf.h 10 | date.h 11 | 12 | # entering xv6 13 | entry.S 14 | entryother.S 15 | main.c 16 | 17 | # locks 18 | spinlock.h 19 | spinlock.c 20 | 21 | # processes 22 | vm.c 23 | proc.h 24 | proc.c 25 | swtch.S 26 | kalloc.c 27 | 28 | # system calls 29 | traps.h 30 | vectors.pl 31 | trapasm.S 32 | trap.c 33 | syscall.h 34 | syscall.c 35 | sysproc.c 36 | 37 | # file system 38 | buf.h 39 | sleeplock.h 40 | fcntl.h 41 | stat.h 42 | fs.h 43 | file.h 44 | ide.c 45 | bio.c 46 | sleeplock.c 47 | log.c 48 | fs.c 49 | file.c 50 | sysfile.c 51 | exec.c 52 | 53 | # pipes 54 | pipe.c 55 | 56 | # string operations 57 | string.c 58 | 59 | # low-level hardware 60 | mp.h 61 | mp.c 62 | lapic.c 63 | ioapic.c 64 | kbd.h 65 | kbd.c 66 | console.c 67 | uart.c 68 | 69 | # user-level 70 | initcode.S 71 | usys.S 72 | init.c 73 | sh.c 74 | 75 | # bootloader 76 | bootasm.S 77 | bootmain.c 78 | 79 | # link 80 | kernel.ld 81 | -------------------------------------------------------------------------------- /runoff.spec: -------------------------------------------------------------------------------- 1 | # Is sheet 01 (after the TOC) a left sheet or a right sheet? 2 | sheet1: left 3 | 4 | # "left" and "right" specify which page of a two-page spread a file 5 | # must start on. "left" means that a file must start on the first of 6 | # the two pages. "right" means it must start on the second of the two 7 | # pages. The file may start in either column. 8 | # 9 | # "even" and "odd" specify which column a file must start on. "even" 10 | # means it must start in the left of the two columns (00). "odd" means it 11 | # must start in the right of the two columns (50). 12 | # 13 | # You'd think these would be the other way around. 14 | 15 | # types.h either 16 | # param.h either 17 | # defs.h either 18 | # x86.h either 19 | # asm.h either 20 | # mmu.h either 21 | # elf.h either 22 | # mp.h either 23 | 24 | even: entry.S # mild preference 25 | even: entryother.S # mild preference 26 | even: main.c 27 | # mp.c don't care at all 28 | # even: initcode.S 29 | # odd: init.c 30 | 31 | left: spinlock.h 32 | even: spinlock.h 33 | 34 | # This gets struct proc and allocproc on the same spread 35 | left: proc.h 36 | even: proc.h 37 | 38 | # goal is to have two action-packed 2-page spreads, 39 | # one with 40 | # userinit growproc fork exit wait 41 | # and another with 42 | # scheduler sched yield forkret sleep wakeup1 wakeup 43 | right: proc.c # VERY important 44 | even: proc.c # VERY important 45 | 46 | # A few more action packed spreads 47 | # page table creation and process loading 48 | # walkpgdir mappages setupkvm switch[ku]vm inituvm (loaduvm) 49 | # process memory management 50 | # allocuvm deallocuvm freevm 51 | left: vm.c 52 | 53 | even: kalloc.c # mild preference 54 | 55 | # syscall.h either 56 | # trapasm.S either 57 | # traps.h either 58 | # even: trap.c 59 | # vectors.pl either 60 | # syscall.c either 61 | # sysproc.c either 62 | 63 | # buf.h either 64 | # dev.h either 65 | # fcntl.h either 66 | # stat.h either 67 | # file.h either 68 | # fs.h either 69 | # fsvar.h either 70 | # left: ide.c # mild preference 71 | even: ide.c 72 | # odd: bio.c 73 | 74 | # log.c fits nicely in a spread 75 | even: log.c 76 | left: log.c 77 | 78 | # with fs.c starting on 2nd column of a left page, we get these 2-page spreads: 79 | # ialloc iupdate iget idup ilock iunlock iput iunlockput 80 | # bmap itrunc stati readi writei 81 | # namecmp dirlookup dirlink skipelem namex namei 82 | # fileinit filealloc filedup fileclose filestat fileread filewrite 83 | # starting on 2nd column of a right page is not terrible either 84 | odd: fs.c # VERY important 85 | left: fs.c # mild preference 86 | # file.c either 87 | # exec.c either 88 | # sysfile.c either 89 | 90 | # Mild preference, but makes spreads of mp.c, lapic.c, and ioapic.c+picirq.c 91 | even: mp.c 92 | left: mp.c 93 | 94 | # even: pipe.c # mild preference 95 | # string.c either 96 | # left: kbd.h # mild preference 97 | even: kbd.h 98 | even: console.c 99 | odd: sh.c 100 | 101 | even: bootasm.S # mild preference 102 | even: bootmain.c # mild preference 103 | -------------------------------------------------------------------------------- /runoff1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | $n = 0; 4 | $v = 0; 5 | if($ARGV[0] eq "-v") { 6 | $v = 1; 7 | shift @ARGV; 8 | } 9 | if($ARGV[0] eq "-n") { 10 | $n = $ARGV[1]; 11 | shift @ARGV; 12 | shift @ARGV; 13 | } 14 | $n = int(($n+49)/50)*50 - 1; 15 | 16 | $file = $ARGV[0]; 17 | @lines = <>; 18 | $linenum = 0; 19 | foreach (@lines) { 20 | $linenum++; 21 | chomp; 22 | s/\s+$//; 23 | if(length() >= 75){ 24 | print STDERR "$file:$linenum: line too long\n"; 25 | } 26 | } 27 | @outlines = (); 28 | $nextout = 0; 29 | 30 | for($i=0; $i<@lines; ){ 31 | # Skip leading blank lines. 32 | $i++ while $i<@lines && $lines[$i] =~ /^$/; 33 | last if $i>=@lines; 34 | 35 | # If the rest of the file fits, use the whole thing. 36 | if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){ 37 | $breakbefore = @lines; 38 | }else{ 39 | # Find a good next page break; 40 | # Hope for end of function. 41 | # but settle for a blank line (but not first blank line 42 | # in function, which comes after variable declarations). 43 | $breakbefore = $i; 44 | $lastblank = $i; 45 | $sawbrace = 0; 46 | $breaksize = 15; # 15 lines to get to function 47 | for($j=$i; $j<$i+50 && $j < @lines; $j++){ 48 | if($lines[$j] =~ /PAGEBREAK!/){ 49 | $lines[$j] = ""; 50 | $breakbefore = $j; 51 | $breaksize = 100; 52 | last; 53 | } 54 | if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){ 55 | $breaksize = $1; 56 | $breakbefore = $j; 57 | $lines[$j] = ""; 58 | } 59 | if($lines[$j] =~ /^};?$/){ 60 | $breakbefore = $j+1; 61 | $breaksize = 15; 62 | } 63 | if($lines[$j] =~ /^{$/){ 64 | $sawbrace = 1; 65 | } 66 | if($lines[$j] =~ /^$/){ 67 | if($sawbrace){ 68 | $sawbrace = 0; 69 | }else{ 70 | $lastblank = $j; 71 | } 72 | } 73 | } 74 | if($j<@lines && $lines[$j] =~ /^$/){ 75 | $lastblank = $j; 76 | } 77 | 78 | # If we are not putting enough on a page, try a blank line. 79 | if($breakbefore - $i < 50 - $breaksize && $lastblank > $breakbefore && $lastblank >= $i+50 - 5){ 80 | if($v){ 81 | print STDERR "breakbefore $breakbefore i $i breaksize $breaksize\n"; 82 | } 83 | $breakbefore = $lastblank; 84 | $breaksize = 5; # only 5 lines to get to blank line 85 | } 86 | 87 | # If we are not putting enough on a page, force a full page. 88 | if($breakbefore - $i < 50 - $breaksize && $breakbefore != @lines){ 89 | $breakbefore = $i + 50; 90 | $breakbefore = @lines if @lines < $breakbefore; 91 | } 92 | 93 | if($breakbefore < $i+2){ 94 | $breakbefore = $i+2; 95 | } 96 | } 97 | 98 | # Emit the page. 99 | $i50 = $i + 50; 100 | for(; $i<$breakbefore; $i++){ 101 | printf "%04d %s\n", ++$n, $lines[$i]; 102 | } 103 | 104 | # Finish page 105 | for($j=$i; $j<$i50; $j++){ 106 | printf "%04d \n", ++$n; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /show1: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps 4 | -------------------------------------------------------------------------------- /sign.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!"; 4 | 5 | $n = sysread(SIG, $buf, 1000); 6 | 7 | if($n > 510){ 8 | print STDERR "boot block too large: $n bytes (max 510)\n"; 9 | exit 1; 10 | } 11 | 12 | print STDERR "boot block is $n bytes (max 510)\n"; 13 | 14 | $buf .= "\0" x (510-$n); 15 | $buf .= "\x55\xAA"; 16 | 17 | open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; 18 | print SIG $buf; 19 | close SIG; 20 | -------------------------------------------------------------------------------- /sleep1.p: -------------------------------------------------------------------------------- 1 | /* 2 | This file defines a Promela model for xv6's 3 | acquire, release, sleep, and wakeup, along with 4 | a model of a simple producer/consumer queue. 5 | 6 | To run: 7 | spinp sleep1.p 8 | 9 | (You may need to install Spin, available at http://spinroot.com/.) 10 | 11 | After a successful run spin prints something like: 12 | 13 | unreached in proctype consumer 14 | (0 of 37 states) 15 | unreached in proctype producer 16 | (0 of 23 states) 17 | 18 | After an unsuccessful run, the spinp script prints 19 | an execution trace that causes a deadlock. 20 | 21 | The safe body of producer reads: 22 | 23 | acquire(lk); 24 | x = value; value = x + 1; x = 0; 25 | wakeup(0); 26 | release(lk); 27 | i = i + 1; 28 | 29 | If this is changed to: 30 | 31 | x = value; value = x + 1; x = 0; 32 | acquire(lk); 33 | wakeup(0); 34 | release(lk); 35 | i = i + 1; 36 | 37 | then a deadlock can happen, because the non-atomic 38 | increment of value conflicts with the non-atomic 39 | decrement in consumer, causing value to have a bad value. 40 | Try this. 41 | 42 | If it is changed to: 43 | 44 | acquire(lk); 45 | x = value; value = x + 1; x = 0; 46 | release(lk); 47 | wakeup(0); 48 | i = i + 1; 49 | 50 | then nothing bad happens: it is okay to wakeup after release 51 | instead of before, although it seems morally wrong. 52 | */ 53 | 54 | #define ITER 4 55 | #define N 2 56 | 57 | bit lk; 58 | byte value; 59 | bit sleeping[N]; 60 | 61 | inline acquire(x) 62 | { 63 | atomic { x == 0; x = 1 } 64 | } 65 | 66 | inline release(x) 67 | { 68 | assert x==1; 69 | x = 0 70 | } 71 | 72 | inline sleep(cond, lk) 73 | { 74 | assert !sleeping[_pid]; 75 | if 76 | :: cond -> 77 | skip 78 | :: else -> 79 | atomic { release(lk); sleeping[_pid] = 1 }; 80 | sleeping[_pid] == 0; 81 | acquire(lk) 82 | fi 83 | } 84 | 85 | inline wakeup() 86 | { 87 | w = 0; 88 | do 89 | :: w < N -> 90 | sleeping[w] = 0; 91 | w = w + 1 92 | :: else -> 93 | break 94 | od 95 | } 96 | 97 | active[N] proctype consumer() 98 | { 99 | byte i, x; 100 | 101 | i = 0; 102 | do 103 | :: i < ITER -> 104 | acquire(lk); 105 | sleep(value > 0, lk); 106 | x = value; value = x - 1; x = 0; 107 | release(lk); 108 | i = i + 1; 109 | :: else -> 110 | break 111 | od; 112 | i = 0; 113 | skip 114 | } 115 | 116 | active[N] proctype producer() 117 | { 118 | byte i, x, w; 119 | 120 | i = 0; 121 | do 122 | :: i < ITER -> 123 | acquire(lk); 124 | x = value; value = x + 1; x = 0; 125 | release(lk); 126 | wakeup(); 127 | i = i + 1; 128 | :: else -> 129 | break 130 | od; 131 | i = 0; 132 | skip 133 | } 134 | 135 | -------------------------------------------------------------------------------- /sleeplock.c: -------------------------------------------------------------------------------- 1 | // Sleeping locks 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "x86.h" 7 | #include "memlayout.h" 8 | #include "mmu.h" 9 | #include "proc.h" 10 | #include "spinlock.h" 11 | #include "sleeplock.h" 12 | 13 | void 14 | initsleeplock(struct sleeplock *lk, char *name) 15 | { 16 | initlock(&lk->lk, "sleep lock"); 17 | lk->name = name; 18 | lk->locked = 0; 19 | lk->pid = 0; 20 | } 21 | 22 | void 23 | acquiresleep(struct sleeplock *lk) 24 | { 25 | acquire(&lk->lk); 26 | while (lk->locked) { 27 | sleep(lk, &lk->lk); 28 | } 29 | lk->locked = 1; 30 | lk->pid = myproc()->pid; 31 | release(&lk->lk); 32 | } 33 | 34 | void 35 | releasesleep(struct sleeplock *lk) 36 | { 37 | acquire(&lk->lk); 38 | lk->locked = 0; 39 | lk->pid = 0; 40 | wakeup(lk); 41 | release(&lk->lk); 42 | } 43 | 44 | int 45 | holdingsleep(struct sleeplock *lk) 46 | { 47 | int r; 48 | 49 | acquire(&lk->lk); 50 | r = lk->locked && (lk->pid == myproc()->pid); 51 | release(&lk->lk); 52 | return r; 53 | } 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /sleeplock.h: -------------------------------------------------------------------------------- 1 | // Long-term locks for processes 2 | struct sleeplock { 3 | uint locked; // Is the lock held? 4 | struct spinlock lk; // spinlock protecting this sleep lock 5 | 6 | // For debugging: 7 | char *name; // Name of lock. 8 | int pid; // Process holding lock 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /spinlock.c: -------------------------------------------------------------------------------- 1 | // Mutual exclusion spin locks. 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "x86.h" 7 | #include "memlayout.h" 8 | #include "mmu.h" 9 | #include "proc.h" 10 | #include "spinlock.h" 11 | 12 | void 13 | initlock(struct spinlock *lk, char *name) 14 | { 15 | lk->name = name; 16 | lk->locked = 0; 17 | lk->cpu = 0; 18 | } 19 | 20 | // Acquire the lock. 21 | // Loops (spins) until the lock is acquired. 22 | // Holding a lock for a long time may cause 23 | // other CPUs to waste time spinning to acquire it. 24 | void 25 | acquire(struct spinlock *lk) 26 | { 27 | pushcli(); // disable interrupts to avoid deadlock. 28 | if(holding(lk)) 29 | panic("acquire"); 30 | 31 | // The xchg is atomic. 32 | while(xchg(&lk->locked, 1) != 0) 33 | ; 34 | 35 | // Tell the C compiler and the processor to not move loads or stores 36 | // past this point, to ensure that the critical section's memory 37 | // references happen after the lock is acquired. 38 | __sync_synchronize(); 39 | 40 | // Record info about lock acquisition for debugging. 41 | lk->cpu = mycpu(); 42 | getcallerpcs(&lk, lk->pcs); 43 | } 44 | 45 | // Release the lock. 46 | void 47 | release(struct spinlock *lk) 48 | { 49 | if(!holding(lk)) 50 | panic("release"); 51 | 52 | lk->pcs[0] = 0; 53 | lk->cpu = 0; 54 | 55 | // Tell the C compiler and the processor to not move loads or stores 56 | // past this point, to ensure that all the stores in the critical 57 | // section are visible to other cores before the lock is released. 58 | // Both the C compiler and the hardware may re-order loads and 59 | // stores; __sync_synchronize() tells them both not to. 60 | __sync_synchronize(); 61 | 62 | // Release the lock, equivalent to lk->locked = 0. 63 | // This code can't use a C assignment, since it might 64 | // not be atomic. A real OS would use C atomics here. 65 | asm volatile("movl $0, %0" : "+m" (lk->locked) : ); 66 | 67 | popcli(); 68 | } 69 | 70 | // Record the current call stack in pcs[] by following the %ebp chain. 71 | void 72 | getcallerpcs(void *v, uint pcs[]) 73 | { 74 | uint *ebp; 75 | int i; 76 | 77 | ebp = (uint*)v - 2; 78 | for(i = 0; i < 10; i++){ 79 | if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff) 80 | break; 81 | pcs[i] = ebp[1]; // saved %eip 82 | ebp = (uint*)ebp[0]; // saved %ebp 83 | } 84 | for(; i < 10; i++) 85 | pcs[i] = 0; 86 | } 87 | 88 | // Check whether this cpu is holding the lock. 89 | int 90 | holding(struct spinlock *lock) 91 | { 92 | int r; 93 | pushcli(); 94 | r = lock->locked && lock->cpu == mycpu(); 95 | popcli(); 96 | return r; 97 | } 98 | 99 | 100 | // Pushcli/popcli are like cli/sti except that they are matched: 101 | // it takes two popcli to undo two pushcli. Also, if interrupts 102 | // are off, then pushcli, popcli leaves them off. 103 | 104 | void 105 | pushcli(void) 106 | { 107 | int eflags; 108 | 109 | eflags = readeflags(); 110 | cli(); 111 | if(mycpu()->ncli == 0) 112 | mycpu()->intena = eflags & FL_IF; 113 | mycpu()->ncli += 1; 114 | } 115 | 116 | void 117 | popcli(void) 118 | { 119 | if(readeflags()&FL_IF) 120 | panic("popcli - interruptible"); 121 | if(--mycpu()->ncli < 0) 122 | panic("popcli"); 123 | if(mycpu()->ncli == 0 && mycpu()->intena) 124 | sti(); 125 | } 126 | 127 | -------------------------------------------------------------------------------- /spinlock.h: -------------------------------------------------------------------------------- 1 | // Mutual exclusion lock. 2 | struct spinlock { 3 | uint locked; // Is the lock held? 4 | 5 | // For debugging: 6 | char *name; // Name of lock. 7 | struct cpu *cpu; // The cpu holding the lock. 8 | uint pcs[10]; // The call stack (an array of program counters) 9 | // that locked the lock. 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /spinp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# != 1 ] || [ ! -f "$1" ]; then 4 | echo 'usage: spinp file.p' 1>&2 5 | exit 1 6 | fi 7 | 8 | rm -f $1.trail 9 | spin -a $1 || exit 1 10 | cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c 11 | pan -i 12 | rm pan.* pan 13 | if [ -f $1.trail ]; then 14 | spin -t -p $1 15 | fi 16 | 17 | -------------------------------------------------------------------------------- /stat.h: -------------------------------------------------------------------------------- 1 | #define T_DIR 1 // Directory 2 | #define T_FILE 2 // File 3 | #define T_DEV 3 // Device 4 | 5 | struct stat { 6 | short type; // Type of file 7 | int dev; // File system's disk device 8 | uint ino; // Inode number 9 | short nlink; // Number of links to file 10 | uint size; // Size of file in bytes 11 | }; 12 | -------------------------------------------------------------------------------- /stressfs.c: -------------------------------------------------------------------------------- 1 | // Demonstrate that moving the "acquire" in iderw after the loop that 2 | // appends to the idequeue results in a race. 3 | 4 | // For this to work, you should also add a spin within iderw's 5 | // idequeue traversal loop. Adding the following demonstrated a panic 6 | // after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: 7 | // for (i = 0; i < 40000; i++) 8 | // asm volatile(""); 9 | 10 | #include "types.h" 11 | #include "stat.h" 12 | #include "user.h" 13 | #include "fs.h" 14 | #include "fcntl.h" 15 | 16 | int 17 | main(int argc, char *argv[]) 18 | { 19 | int fd, i; 20 | char path[] = "stressfs0"; 21 | char data[512]; 22 | 23 | printf(1, "stressfs starting\n"); 24 | memset(data, 'a', sizeof(data)); 25 | 26 | for(i = 0; i < 4; i++) 27 | if(fork() > 0) 28 | break; 29 | 30 | printf(1, "write %d\n", i); 31 | 32 | path[8] += i; 33 | fd = open(path, O_CREATE | O_RDWR); 34 | for(i = 0; i < 20; i++) 35 | // printf(fd, "%d\n", i); 36 | write(fd, data, sizeof(data)); 37 | close(fd); 38 | 39 | printf(1, "read\n"); 40 | 41 | fd = open(path, O_RDONLY); 42 | for (i = 0; i < 20; i++) 43 | read(fd, data, sizeof(data)); 44 | close(fd); 45 | 46 | wait(); 47 | 48 | exit(); 49 | } 50 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | 4 | void* 5 | memset(void *dst, int c, uint n) 6 | { 7 | if ((int)dst%4 == 0 && n%4 == 0){ 8 | c &= 0xFF; 9 | stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4); 10 | } else 11 | stosb(dst, c, n); 12 | return dst; 13 | } 14 | 15 | int 16 | memcmp(const void *v1, const void *v2, uint n) 17 | { 18 | const uchar *s1, *s2; 19 | 20 | s1 = v1; 21 | s2 = v2; 22 | while(n-- > 0){ 23 | if(*s1 != *s2) 24 | return *s1 - *s2; 25 | s1++, s2++; 26 | } 27 | 28 | return 0; 29 | } 30 | 31 | void* 32 | memmove(void *dst, const void *src, uint n) 33 | { 34 | const char *s; 35 | char *d; 36 | 37 | s = src; 38 | d = dst; 39 | if(s < d && s + n > d){ 40 | s += n; 41 | d += n; 42 | while(n-- > 0) 43 | *--d = *--s; 44 | } else 45 | while(n-- > 0) 46 | *d++ = *s++; 47 | 48 | return dst; 49 | } 50 | 51 | // memcpy exists to placate GCC. Use memmove. 52 | void* 53 | memcpy(void *dst, const void *src, uint n) 54 | { 55 | return memmove(dst, src, n); 56 | } 57 | 58 | int 59 | strncmp(const char *p, const char *q, uint n) 60 | { 61 | while(n > 0 && *p && *p == *q) 62 | n--, p++, q++; 63 | if(n == 0) 64 | return 0; 65 | return (uchar)*p - (uchar)*q; 66 | } 67 | 68 | char* 69 | strncpy(char *s, const char *t, int n) 70 | { 71 | char *os; 72 | 73 | os = s; 74 | while(n-- > 0 && (*s++ = *t++) != 0) 75 | ; 76 | while(n-- > 0) 77 | *s++ = 0; 78 | return os; 79 | } 80 | 81 | // Like strncpy but guaranteed to NUL-terminate. 82 | char* 83 | safestrcpy(char *s, const char *t, int n) 84 | { 85 | char *os; 86 | 87 | os = s; 88 | if(n <= 0) 89 | return os; 90 | while(--n > 0 && (*s++ = *t++) != 0) 91 | ; 92 | *s = 0; 93 | return os; 94 | } 95 | 96 | int 97 | strlen(const char *s) 98 | { 99 | int n; 100 | 101 | for(n = 0; s[n]; n++) 102 | ; 103 | return n; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /swtch.S: -------------------------------------------------------------------------------- 1 | # Context switch 2 | # 3 | # void swtch(struct context **old, struct context *new); 4 | # 5 | # Save the current registers on the stack, creating 6 | # a struct context, and save its address in *old. 7 | # Switch stacks to new and pop previously-saved registers. 8 | 9 | .globl swtch 10 | swtch: 11 | movl 4(%esp), %eax 12 | movl 8(%esp), %edx 13 | 14 | # Save old callee-saved registers 15 | pushl %ebp 16 | pushl %ebx 17 | pushl %esi 18 | pushl %edi 19 | 20 | # Switch stacks 21 | movl %esp, (%eax) 22 | movl %edx, %esp 23 | 24 | # Load new callee-saved registers 25 | popl %edi 26 | popl %esi 27 | popl %ebx 28 | popl %ebp 29 | ret 30 | -------------------------------------------------------------------------------- /syscall.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "memlayout.h" 5 | #include "mmu.h" 6 | #include "proc.h" 7 | #include "x86.h" 8 | #include "syscall.h" 9 | 10 | // User code makes a system call with INT T_SYSCALL. 11 | // System call number in %eax. 12 | // Arguments on the stack, from the user call to the C 13 | // library system call function. The saved user %esp points 14 | // to a saved program counter, and then the first argument. 15 | 16 | // Fetch the int at addr from the current process. 17 | int 18 | fetchint(uint addr, int *ip) 19 | { 20 | struct proc *curproc = myproc(); 21 | 22 | if(addr >= curproc->sz || addr+4 > curproc->sz) 23 | return -1; 24 | *ip = *(int*)(addr); 25 | return 0; 26 | } 27 | 28 | // Fetch the nul-terminated string at addr from the current process. 29 | // Doesn't actually copy the string - just sets *pp to point at it. 30 | // Returns length of string, not including nul. 31 | int 32 | fetchstr(uint addr, char **pp) 33 | { 34 | char *s, *ep; 35 | struct proc *curproc = myproc(); 36 | 37 | if(addr >= curproc->sz) 38 | return -1; 39 | *pp = (char*)addr; 40 | ep = (char*)curproc->sz; 41 | for(s = *pp; s < ep; s++){ 42 | if(*s == 0) 43 | return s - *pp; 44 | } 45 | return -1; 46 | } 47 | 48 | // Fetch the nth 32-bit system call argument. 49 | int 50 | argint(int n, int *ip) 51 | { 52 | return fetchint((myproc()->tf->esp) + 4 + 4*n, ip); 53 | } 54 | 55 | // Fetch the nth word-sized system call argument as a pointer 56 | // to a block of memory of size bytes. Check that the pointer 57 | // lies within the process address space. 58 | int 59 | argptr(int n, char **pp, int size) 60 | { 61 | int i; 62 | struct proc *curproc = myproc(); 63 | 64 | if(argint(n, &i) < 0) 65 | return -1; 66 | if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz) 67 | return -1; 68 | *pp = (char*)i; 69 | return 0; 70 | } 71 | 72 | // Fetch the nth word-sized system call argument as a string pointer. 73 | // Check that the pointer is valid and the string is nul-terminated. 74 | // (There is no shared writable memory, so the string can't change 75 | // between this check and being used by the kernel.) 76 | int 77 | argstr(int n, char **pp) 78 | { 79 | int addr; 80 | if(argint(n, &addr) < 0) 81 | return -1; 82 | return fetchstr(addr, pp); 83 | } 84 | 85 | extern int sys_chdir(void); 86 | extern int sys_close(void); 87 | extern int sys_dup(void); 88 | extern int sys_exec(void); 89 | extern int sys_exit(void); 90 | extern int sys_fork(void); 91 | extern int sys_fstat(void); 92 | extern int sys_getpid(void); 93 | extern int sys_kill(void); 94 | extern int sys_link(void); 95 | extern int sys_mkdir(void); 96 | extern int sys_mknod(void); 97 | extern int sys_open(void); 98 | extern int sys_pipe(void); 99 | extern int sys_read(void); 100 | extern int sys_sbrk(void); 101 | extern int sys_sleep(void); 102 | extern int sys_unlink(void); 103 | extern int sys_wait(void); 104 | extern int sys_write(void); 105 | extern int sys_uptime(void); 106 | extern int sys_clone(void); 107 | extern int sys_join(void); 108 | 109 | static int (*syscalls[])(void) = { 110 | [SYS_fork] sys_fork, 111 | [SYS_exit] sys_exit, 112 | [SYS_wait] sys_wait, 113 | [SYS_pipe] sys_pipe, 114 | [SYS_read] sys_read, 115 | [SYS_kill] sys_kill, 116 | [SYS_exec] sys_exec, 117 | [SYS_fstat] sys_fstat, 118 | [SYS_chdir] sys_chdir, 119 | [SYS_dup] sys_dup, 120 | [SYS_getpid] sys_getpid, 121 | [SYS_sbrk] sys_sbrk, 122 | [SYS_sleep] sys_sleep, 123 | [SYS_uptime] sys_uptime, 124 | [SYS_open] sys_open, 125 | [SYS_write] sys_write, 126 | [SYS_mknod] sys_mknod, 127 | [SYS_unlink] sys_unlink, 128 | [SYS_link] sys_link, 129 | [SYS_mkdir] sys_mkdir, 130 | [SYS_close] sys_close, 131 | [SYS_clone] sys_clone, 132 | [SYS_join] sys_join 133 | }; 134 | 135 | void 136 | syscall(void) 137 | { 138 | int num; 139 | struct proc *curproc = myproc(); 140 | 141 | num = curproc->tf->eax; 142 | if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 143 | curproc->tf->eax = syscalls[num](); 144 | } else { 145 | cprintf("%d %s: unknown sys call %d\n", 146 | curproc->pid, curproc->name, num); 147 | curproc->tf->eax = -1; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /syscall.h: -------------------------------------------------------------------------------- 1 | // System call numbers 2 | #define SYS_fork 1 3 | #define SYS_exit 2 4 | #define SYS_wait 3 5 | #define SYS_pipe 4 6 | #define SYS_read 5 7 | #define SYS_kill 6 8 | #define SYS_exec 7 9 | #define SYS_fstat 8 10 | #define SYS_chdir 9 11 | #define SYS_dup 10 12 | #define SYS_getpid 11 13 | #define SYS_sbrk 12 14 | #define SYS_sleep 13 15 | #define SYS_uptime 14 16 | #define SYS_open 15 17 | #define SYS_write 16 18 | #define SYS_mknod 17 19 | #define SYS_unlink 18 20 | #define SYS_link 19 21 | #define SYS_mkdir 20 22 | #define SYS_close 21 23 | #define SYS_clone 22 24 | #define SYS_join 23 25 | -------------------------------------------------------------------------------- /sysfile.c: -------------------------------------------------------------------------------- 1 | // 2 | // File-system system calls. 3 | // Mostly argument checking, since we don't trust 4 | // user code, and calls into file.c and fs.c. 5 | // 6 | 7 | #include "types.h" 8 | #include "defs.h" 9 | #include "param.h" 10 | #include "stat.h" 11 | #include "mmu.h" 12 | #include "proc.h" 13 | #include "fs.h" 14 | #include "spinlock.h" 15 | #include "sleeplock.h" 16 | #include "file.h" 17 | #include "fcntl.h" 18 | 19 | // Fetch the nth word-sized system call argument as a file descriptor 20 | // and return both the descriptor and the corresponding struct file. 21 | static int 22 | argfd(int n, int *pfd, struct file **pf) 23 | { 24 | int fd; 25 | struct file *f; 26 | 27 | if(argint(n, &fd) < 0) 28 | return -1; 29 | if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0) 30 | return -1; 31 | if(pfd) 32 | *pfd = fd; 33 | if(pf) 34 | *pf = f; 35 | return 0; 36 | } 37 | 38 | // Allocate a file descriptor for the given file. 39 | // Takes over file reference from caller on success. 40 | static int 41 | fdalloc(struct file *f) 42 | { 43 | int fd; 44 | struct proc *curproc = myproc(); 45 | 46 | for(fd = 0; fd < NOFILE; fd++){ 47 | if(curproc->ofile[fd] == 0){ 48 | curproc->ofile[fd] = f; 49 | return fd; 50 | } 51 | } 52 | return -1; 53 | } 54 | 55 | int 56 | sys_dup(void) 57 | { 58 | struct file *f; 59 | int fd; 60 | 61 | if(argfd(0, 0, &f) < 0) 62 | return -1; 63 | if((fd=fdalloc(f)) < 0) 64 | return -1; 65 | filedup(f); 66 | return fd; 67 | } 68 | 69 | int 70 | sys_read(void) 71 | { 72 | struct file *f; 73 | int n; 74 | char *p; 75 | 76 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 77 | return -1; 78 | return fileread(f, p, n); 79 | } 80 | 81 | int 82 | sys_write(void) 83 | { 84 | struct file *f; 85 | int n; 86 | char *p; 87 | 88 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 89 | return -1; 90 | return filewrite(f, p, n); 91 | } 92 | 93 | int 94 | sys_close(void) 95 | { 96 | int fd; 97 | struct file *f; 98 | 99 | if(argfd(0, &fd, &f) < 0) 100 | return -1; 101 | myproc()->ofile[fd] = 0; 102 | fileclose(f); 103 | return 0; 104 | } 105 | 106 | int 107 | sys_fstat(void) 108 | { 109 | struct file *f; 110 | struct stat *st; 111 | 112 | if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0) 113 | return -1; 114 | return filestat(f, st); 115 | } 116 | 117 | // Create the path new as a link to the same inode as old. 118 | int 119 | sys_link(void) 120 | { 121 | char name[DIRSIZ], *new, *old; 122 | struct inode *dp, *ip; 123 | 124 | if(argstr(0, &old) < 0 || argstr(1, &new) < 0) 125 | return -1; 126 | 127 | begin_op(); 128 | if((ip = namei(old)) == 0){ 129 | end_op(); 130 | return -1; 131 | } 132 | 133 | ilock(ip); 134 | if(ip->type == T_DIR){ 135 | iunlockput(ip); 136 | end_op(); 137 | return -1; 138 | } 139 | 140 | ip->nlink++; 141 | iupdate(ip); 142 | iunlock(ip); 143 | 144 | if((dp = nameiparent(new, name)) == 0) 145 | goto bad; 146 | ilock(dp); 147 | if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ 148 | iunlockput(dp); 149 | goto bad; 150 | } 151 | iunlockput(dp); 152 | iput(ip); 153 | 154 | end_op(); 155 | 156 | return 0; 157 | 158 | bad: 159 | ilock(ip); 160 | ip->nlink--; 161 | iupdate(ip); 162 | iunlockput(ip); 163 | end_op(); 164 | return -1; 165 | } 166 | 167 | // Is the directory dp empty except for "." and ".." ? 168 | static int 169 | isdirempty(struct inode *dp) 170 | { 171 | int off; 172 | struct dirent de; 173 | 174 | for(off=2*sizeof(de); offsize; off+=sizeof(de)){ 175 | if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) 176 | panic("isdirempty: readi"); 177 | if(de.inum != 0) 178 | return 0; 179 | } 180 | return 1; 181 | } 182 | 183 | //PAGEBREAK! 184 | int 185 | sys_unlink(void) 186 | { 187 | struct inode *ip, *dp; 188 | struct dirent de; 189 | char name[DIRSIZ], *path; 190 | uint off; 191 | 192 | if(argstr(0, &path) < 0) 193 | return -1; 194 | 195 | begin_op(); 196 | if((dp = nameiparent(path, name)) == 0){ 197 | end_op(); 198 | return -1; 199 | } 200 | 201 | ilock(dp); 202 | 203 | // Cannot unlink "." or "..". 204 | if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0) 205 | goto bad; 206 | 207 | if((ip = dirlookup(dp, name, &off)) == 0) 208 | goto bad; 209 | ilock(ip); 210 | 211 | if(ip->nlink < 1) 212 | panic("unlink: nlink < 1"); 213 | if(ip->type == T_DIR && !isdirempty(ip)){ 214 | iunlockput(ip); 215 | goto bad; 216 | } 217 | 218 | memset(&de, 0, sizeof(de)); 219 | if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) 220 | panic("unlink: writei"); 221 | if(ip->type == T_DIR){ 222 | dp->nlink--; 223 | iupdate(dp); 224 | } 225 | iunlockput(dp); 226 | 227 | ip->nlink--; 228 | iupdate(ip); 229 | iunlockput(ip); 230 | 231 | end_op(); 232 | 233 | return 0; 234 | 235 | bad: 236 | iunlockput(dp); 237 | end_op(); 238 | return -1; 239 | } 240 | 241 | static struct inode* 242 | create(char *path, short type, short major, short minor) 243 | { 244 | struct inode *ip, *dp; 245 | char name[DIRSIZ]; 246 | 247 | if((dp = nameiparent(path, name)) == 0) 248 | return 0; 249 | ilock(dp); 250 | 251 | if((ip = dirlookup(dp, name, 0)) != 0){ 252 | iunlockput(dp); 253 | ilock(ip); 254 | if(type == T_FILE && ip->type == T_FILE) 255 | return ip; 256 | iunlockput(ip); 257 | return 0; 258 | } 259 | 260 | if((ip = ialloc(dp->dev, type)) == 0) 261 | panic("create: ialloc"); 262 | 263 | ilock(ip); 264 | ip->major = major; 265 | ip->minor = minor; 266 | ip->nlink = 1; 267 | iupdate(ip); 268 | 269 | if(type == T_DIR){ // Create . and .. entries. 270 | dp->nlink++; // for ".." 271 | iupdate(dp); 272 | // No ip->nlink++ for ".": avoid cyclic ref count. 273 | if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) 274 | panic("create dots"); 275 | } 276 | 277 | if(dirlink(dp, name, ip->inum) < 0) 278 | panic("create: dirlink"); 279 | 280 | iunlockput(dp); 281 | 282 | return ip; 283 | } 284 | 285 | int 286 | sys_open(void) 287 | { 288 | char *path; 289 | int fd, omode; 290 | struct file *f; 291 | struct inode *ip; 292 | 293 | if(argstr(0, &path) < 0 || argint(1, &omode) < 0) 294 | return -1; 295 | 296 | begin_op(); 297 | 298 | if(omode & O_CREATE){ 299 | ip = create(path, T_FILE, 0, 0); 300 | if(ip == 0){ 301 | end_op(); 302 | return -1; 303 | } 304 | } else { 305 | if((ip = namei(path)) == 0){ 306 | end_op(); 307 | return -1; 308 | } 309 | ilock(ip); 310 | if(ip->type == T_DIR && omode != O_RDONLY){ 311 | iunlockput(ip); 312 | end_op(); 313 | return -1; 314 | } 315 | } 316 | 317 | if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ 318 | if(f) 319 | fileclose(f); 320 | iunlockput(ip); 321 | end_op(); 322 | return -1; 323 | } 324 | iunlock(ip); 325 | end_op(); 326 | 327 | f->type = FD_INODE; 328 | f->ip = ip; 329 | f->off = 0; 330 | f->readable = !(omode & O_WRONLY); 331 | f->writable = (omode & O_WRONLY) || (omode & O_RDWR); 332 | return fd; 333 | } 334 | 335 | int 336 | sys_mkdir(void) 337 | { 338 | char *path; 339 | struct inode *ip; 340 | 341 | begin_op(); 342 | if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ 343 | end_op(); 344 | return -1; 345 | } 346 | iunlockput(ip); 347 | end_op(); 348 | return 0; 349 | } 350 | 351 | int 352 | sys_mknod(void) 353 | { 354 | struct inode *ip; 355 | char *path; 356 | int major, minor; 357 | 358 | begin_op(); 359 | if((argstr(0, &path)) < 0 || 360 | argint(1, &major) < 0 || 361 | argint(2, &minor) < 0 || 362 | (ip = create(path, T_DEV, major, minor)) == 0){ 363 | end_op(); 364 | return -1; 365 | } 366 | iunlockput(ip); 367 | end_op(); 368 | return 0; 369 | } 370 | 371 | int 372 | sys_chdir(void) 373 | { 374 | char *path; 375 | struct inode *ip; 376 | struct proc *curproc = myproc(); 377 | 378 | begin_op(); 379 | if(argstr(0, &path) < 0 || (ip = namei(path)) == 0){ 380 | end_op(); 381 | return -1; 382 | } 383 | ilock(ip); 384 | if(ip->type != T_DIR){ 385 | iunlockput(ip); 386 | end_op(); 387 | return -1; 388 | } 389 | iunlock(ip); 390 | iput(curproc->cwd); 391 | end_op(); 392 | curproc->cwd = ip; 393 | return 0; 394 | } 395 | 396 | int 397 | sys_exec(void) 398 | { 399 | char *path, *argv[MAXARG]; 400 | int i; 401 | uint uargv, uarg; 402 | 403 | if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ 404 | return -1; 405 | } 406 | memset(argv, 0, sizeof(argv)); 407 | for(i=0;; i++){ 408 | if(i >= NELEM(argv)) 409 | return -1; 410 | if(fetchint(uargv+4*i, (int*)&uarg) < 0) 411 | return -1; 412 | if(uarg == 0){ 413 | argv[i] = 0; 414 | break; 415 | } 416 | if(fetchstr(uarg, &argv[i]) < 0) 417 | return -1; 418 | } 419 | return exec(path, argv); 420 | } 421 | 422 | int 423 | sys_pipe(void) 424 | { 425 | int *fd; 426 | struct file *rf, *wf; 427 | int fd0, fd1; 428 | 429 | if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0) 430 | return -1; 431 | if(pipealloc(&rf, &wf) < 0) 432 | return -1; 433 | fd0 = -1; 434 | if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ 435 | if(fd0 >= 0) 436 | myproc()->ofile[fd0] = 0; 437 | fileclose(rf); 438 | fileclose(wf); 439 | return -1; 440 | } 441 | fd[0] = fd0; 442 | fd[1] = fd1; 443 | return 0; 444 | } 445 | -------------------------------------------------------------------------------- /sysproc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | #include "defs.h" 4 | #include "date.h" 5 | #include "param.h" 6 | #include "memlayout.h" 7 | #include "mmu.h" 8 | #include "proc.h" 9 | 10 | int 11 | sys_fork(void) 12 | { 13 | return fork(); 14 | } 15 | 16 | int 17 | sys_exit(void) 18 | { 19 | exit(); 20 | return 0; // not reached 21 | } 22 | 23 | int 24 | sys_wait(void) 25 | { 26 | return wait(); 27 | } 28 | 29 | int 30 | sys_kill(void) 31 | { 32 | int pid; 33 | 34 | if(argint(0, &pid) < 0) 35 | return -1; 36 | return kill(pid); 37 | } 38 | 39 | int 40 | sys_getpid(void) 41 | { 42 | return myproc()->pid; 43 | } 44 | 45 | int 46 | sys_sbrk(void) 47 | { 48 | int addr; 49 | int n; 50 | 51 | if(argint(0, &n) < 0) 52 | return -1; 53 | addr = myproc()->sz; 54 | if(growproc(n) < 0) 55 | return -1; 56 | return addr; 57 | } 58 | 59 | int 60 | sys_sleep(void) 61 | { 62 | int n; 63 | uint ticks0; 64 | 65 | if(argint(0, &n) < 0) 66 | return -1; 67 | acquire(&tickslock); 68 | ticks0 = ticks; 69 | while(ticks - ticks0 < n){ 70 | if(myproc()->killed){ 71 | release(&tickslock); 72 | return -1; 73 | } 74 | sleep(&ticks, &tickslock); 75 | } 76 | release(&tickslock); 77 | return 0; 78 | } 79 | 80 | // return how many clock tick interrupts have occurred 81 | // since start. 82 | int 83 | sys_uptime(void) 84 | { 85 | uint xticks; 86 | 87 | acquire(&tickslock); 88 | xticks = ticks; 89 | release(&tickslock); 90 | return xticks; 91 | } 92 | 93 | int 94 | sys_clone(void) 95 | { 96 | int fcn, arg1, arg2, stack; 97 | if(argint(0, &fcn)<0 || argint(1, &arg1)<0 || argint(2, &arg2)<0 || argint(3, &stack)<0) 98 | return -1; 99 | return clone((void *)fcn, (void *)arg1, (void *)arg2, (void *)stack); 100 | } 101 | 102 | int 103 | sys_join(void) 104 | { 105 | void **stack; 106 | int stackArg; 107 | stackArg = argint(0, &stackArg); 108 | stack = (void**) stackArg; 109 | return join(stack); 110 | } -------------------------------------------------------------------------------- /testthreads.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | #include "fcntl.h" 5 | #define SLEEP_TIME 100 6 | 7 | lock_t* lk; 8 | 9 | void f1(void* arg1, void* arg2) { 10 | int num = *(int*)arg1; 11 | if (num) lock_acquire(lk); 12 | printf(1, "1. this should print %s\n", num ? "first" : "whenever"); 13 | printf(1, "1. sleep for %d ticks\n", SLEEP_TIME); 14 | sleep(SLEEP_TIME); 15 | if (num) lock_release(lk); 16 | exit(); 17 | } 18 | 19 | void f2(void* arg1, void* arg2) { 20 | int num = *(int*)arg1; 21 | if (num) lock_acquire(lk); 22 | printf(1, "2. this should print %s\n", num ? "second" : "whenever"); 23 | printf(1, "2. sleep for %d ticks\n", SLEEP_TIME); 24 | sleep(SLEEP_TIME); 25 | if (num) lock_release(lk); 26 | exit(); 27 | } 28 | 29 | void f3(void* arg1, void* arg2) { 30 | int num = *(int*)arg1; 31 | if (num) lock_acquire(lk); 32 | printf(1, "3. this should print %s\n", num ? "third" : "whenever"); 33 | printf(1, "3. sleep for %d ticks\n", SLEEP_TIME); 34 | sleep(SLEEP_TIME); 35 | if (num) lock_release(lk); 36 | exit(); 37 | } 38 | 39 | int 40 | main(int argc, char *argv[]) 41 | { 42 | lock_init(lk); 43 | int arg1 = 1, arg2 = 1; 44 | 45 | printf(1, "below should be sequential print statements:\n"); 46 | thread_create(&f1, (void *)&arg1, (void *)&arg2); 47 | thread_create(&f2, (void *)&arg1, (void *)&arg2); 48 | thread_create(&f3, (void *)&arg1, (void *)&arg2); 49 | thread_join(); 50 | thread_join(); 51 | thread_join(); 52 | 53 | arg1 = 0; 54 | printf(1, "below should be a jarbled mess:\n"); 55 | thread_create(&f1, (void *)&arg1, (void *)&arg2); 56 | thread_create(&f2, (void *)&arg1, (void *)&arg2); 57 | thread_create(&f3, (void *)&arg1, (void *)&arg2); 58 | thread_join(); 59 | thread_join(); 60 | thread_join(); 61 | 62 | exit(); 63 | } -------------------------------------------------------------------------------- /toc.ftr: -------------------------------------------------------------------------------- 1 | 2 | 3 | The source listing is preceded by a cross-reference that lists every defined 4 | constant, struct, global variable, and function in xv6. Each entry gives, 5 | on the same line as the name, the line number (or, in a few cases, numbers) 6 | where the name is defined. Successive lines in an entry list the line 7 | numbers where the name is used. For example, this entry: 8 | 9 | swtch 2658 10 | 0374 2428 2466 2657 2658 11 | 12 | indicates that swtch is defined on line 2658 and is mentioned on five lines 13 | on sheets 03, 24, and 26. 14 | -------------------------------------------------------------------------------- /toc.hdr: -------------------------------------------------------------------------------- 1 | The numbers to the left of the file names in the table are sheet numbers. 2 | The source code has been printed in a double column format with fifty 3 | lines per column, giving one hundred lines per sheet (or page). 4 | Thus there is a convenient relationship between line numbers and sheet numbers. 5 | 6 | 7 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "memlayout.h" 5 | #include "mmu.h" 6 | #include "proc.h" 7 | #include "x86.h" 8 | #include "traps.h" 9 | #include "spinlock.h" 10 | 11 | // Interrupt descriptor table (shared by all CPUs). 12 | struct gatedesc idt[256]; 13 | extern uint vectors[]; // in vectors.S: array of 256 entry pointers 14 | struct spinlock tickslock; 15 | uint ticks; 16 | 17 | void 18 | tvinit(void) 19 | { 20 | int i; 21 | 22 | for(i = 0; i < 256; i++) 23 | SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0); 24 | SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); 25 | 26 | initlock(&tickslock, "time"); 27 | } 28 | 29 | void 30 | idtinit(void) 31 | { 32 | lidt(idt, sizeof(idt)); 33 | } 34 | 35 | //PAGEBREAK: 41 36 | void 37 | trap(struct trapframe *tf) 38 | { 39 | if(tf->trapno == T_SYSCALL){ 40 | if(myproc()->killed) 41 | exit(); 42 | myproc()->tf = tf; 43 | syscall(); 44 | if(myproc()->killed) 45 | exit(); 46 | return; 47 | } 48 | 49 | switch(tf->trapno){ 50 | case T_IRQ0 + IRQ_TIMER: 51 | if(cpuid() == 0){ 52 | acquire(&tickslock); 53 | ticks++; 54 | wakeup(&ticks); 55 | release(&tickslock); 56 | } 57 | lapiceoi(); 58 | break; 59 | case T_IRQ0 + IRQ_IDE: 60 | ideintr(); 61 | lapiceoi(); 62 | break; 63 | case T_IRQ0 + IRQ_IDE+1: 64 | // Bochs generates spurious IDE1 interrupts. 65 | break; 66 | case T_IRQ0 + IRQ_KBD: 67 | kbdintr(); 68 | lapiceoi(); 69 | break; 70 | case T_IRQ0 + IRQ_COM1: 71 | uartintr(); 72 | lapiceoi(); 73 | break; 74 | case T_IRQ0 + 7: 75 | case T_IRQ0 + IRQ_SPURIOUS: 76 | cprintf("cpu%d: spurious interrupt at %x:%x\n", 77 | cpuid(), tf->cs, tf->eip); 78 | lapiceoi(); 79 | break; 80 | 81 | //PAGEBREAK: 13 82 | default: 83 | if(myproc() == 0 || (tf->cs&3) == 0){ 84 | // In kernel, it must be our mistake. 85 | cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n", 86 | tf->trapno, cpuid(), tf->eip, rcr2()); 87 | panic("trap"); 88 | } 89 | // In user space, assume process misbehaved. 90 | cprintf("pid %d %s: trap %d err %d on cpu %d " 91 | "eip 0x%x addr 0x%x--kill proc\n", 92 | myproc()->pid, myproc()->name, tf->trapno, 93 | tf->err, cpuid(), tf->eip, rcr2()); 94 | myproc()->killed = 1; 95 | } 96 | 97 | // Force process exit if it has been killed and is in user space. 98 | // (If it is still executing in the kernel, let it keep running 99 | // until it gets to the regular system call return.) 100 | if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER) 101 | exit(); 102 | 103 | // Force process to give up CPU on clock tick. 104 | // If interrupts were on while locks held, would need to check nlock. 105 | if(myproc() && myproc()->state == RUNNING && 106 | tf->trapno == T_IRQ0+IRQ_TIMER) 107 | yield(); 108 | 109 | // Check if the process has been killed since we yielded 110 | if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER) 111 | exit(); 112 | } 113 | -------------------------------------------------------------------------------- /trapasm.S: -------------------------------------------------------------------------------- 1 | #include "mmu.h" 2 | 3 | # vectors.S sends all traps here. 4 | .globl alltraps 5 | alltraps: 6 | # Build trap frame. 7 | pushl %ds 8 | pushl %es 9 | pushl %fs 10 | pushl %gs 11 | pushal 12 | 13 | # Set up data segments. 14 | movw $(SEG_KDATA<<3), %ax 15 | movw %ax, %ds 16 | movw %ax, %es 17 | 18 | # Call trap(tf), where tf=%esp 19 | pushl %esp 20 | call trap 21 | addl $4, %esp 22 | 23 | # Return falls through to trapret... 24 | .globl trapret 25 | trapret: 26 | popal 27 | popl %gs 28 | popl %fs 29 | popl %es 30 | popl %ds 31 | addl $0x8, %esp # trapno and errcode 32 | iret 33 | -------------------------------------------------------------------------------- /traps.h: -------------------------------------------------------------------------------- 1 | // x86 trap and interrupt constants. 2 | 3 | // Processor-defined: 4 | #define T_DIVIDE 0 // divide error 5 | #define T_DEBUG 1 // debug exception 6 | #define T_NMI 2 // non-maskable interrupt 7 | #define T_BRKPT 3 // breakpoint 8 | #define T_OFLOW 4 // overflow 9 | #define T_BOUND 5 // bounds check 10 | #define T_ILLOP 6 // illegal opcode 11 | #define T_DEVICE 7 // device not available 12 | #define T_DBLFLT 8 // double fault 13 | // #define T_COPROC 9 // reserved (not used since 486) 14 | #define T_TSS 10 // invalid task switch segment 15 | #define T_SEGNP 11 // segment not present 16 | #define T_STACK 12 // stack exception 17 | #define T_GPFLT 13 // general protection fault 18 | #define T_PGFLT 14 // page fault 19 | // #define T_RES 15 // reserved 20 | #define T_FPERR 16 // floating point error 21 | #define T_ALIGN 17 // aligment check 22 | #define T_MCHK 18 // machine check 23 | #define T_SIMDERR 19 // SIMD floating point error 24 | 25 | // These are arbitrarily chosen, but with care not to overlap 26 | // processor defined exceptions or interrupt vectors. 27 | #define T_SYSCALL 64 // system call 28 | #define T_DEFAULT 500 // catchall 29 | 30 | #define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ 31 | 32 | #define IRQ_TIMER 0 33 | #define IRQ_KBD 1 34 | #define IRQ_COM1 4 35 | #define IRQ_IDE 14 36 | #define IRQ_ERROR 19 37 | #define IRQ_SPURIOUS 31 38 | 39 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | typedef unsigned int uint; 2 | typedef unsigned short ushort; 3 | typedef unsigned char uchar; 4 | typedef uint pde_t; 5 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | // Intel 8250 serial port (UART). 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "traps.h" 7 | #include "spinlock.h" 8 | #include "sleeplock.h" 9 | #include "fs.h" 10 | #include "file.h" 11 | #include "mmu.h" 12 | #include "proc.h" 13 | #include "x86.h" 14 | 15 | #define COM1 0x3f8 16 | 17 | static int uart; // is there a uart? 18 | 19 | void 20 | uartinit(void) 21 | { 22 | char *p; 23 | 24 | // Turn off the FIFO 25 | outb(COM1+2, 0); 26 | 27 | // 9600 baud, 8 data bits, 1 stop bit, parity off. 28 | outb(COM1+3, 0x80); // Unlock divisor 29 | outb(COM1+0, 115200/9600); 30 | outb(COM1+1, 0); 31 | outb(COM1+3, 0x03); // Lock divisor, 8 data bits. 32 | outb(COM1+4, 0); 33 | outb(COM1+1, 0x01); // Enable receive interrupts. 34 | 35 | // If status is 0xFF, no serial port. 36 | if(inb(COM1+5) == 0xFF) 37 | return; 38 | uart = 1; 39 | 40 | // Acknowledge pre-existing interrupt conditions; 41 | // enable interrupts. 42 | inb(COM1+2); 43 | inb(COM1+0); 44 | ioapicenable(IRQ_COM1, 0); 45 | 46 | // Announce that we're here. 47 | for(p="xv6...\n"; *p; p++) 48 | uartputc(*p); 49 | } 50 | 51 | void 52 | uartputc(int c) 53 | { 54 | int i; 55 | 56 | if(!uart) 57 | return; 58 | for(i = 0; i < 128 && !(inb(COM1+5) & 0x20); i++) 59 | microdelay(10); 60 | outb(COM1+0, c); 61 | } 62 | 63 | static int 64 | uartgetc(void) 65 | { 66 | if(!uart) 67 | return -1; 68 | if(!(inb(COM1+5) & 0x01)) 69 | return -1; 70 | return inb(COM1+0); 71 | } 72 | 73 | void 74 | uartintr(void) 75 | { 76 | consoleintr(uartgetc); 77 | } 78 | -------------------------------------------------------------------------------- /ulib.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "fcntl.h" 4 | #include "user.h" 5 | #include "mmu.h" 6 | #include "x86.h" 7 | 8 | char* 9 | strcpy(char *s, const char *t) 10 | { 11 | char *os; 12 | 13 | os = s; 14 | while((*s++ = *t++) != 0) 15 | ; 16 | return os; 17 | } 18 | 19 | int 20 | strcmp(const char *p, const char *q) 21 | { 22 | while(*p && *p == *q) 23 | p++, q++; 24 | return (uchar)*p - (uchar)*q; 25 | } 26 | 27 | uint 28 | strlen(const char *s) 29 | { 30 | int n; 31 | 32 | for(n = 0; s[n]; n++) 33 | ; 34 | return n; 35 | } 36 | 37 | void* 38 | memset(void *dst, int c, uint n) 39 | { 40 | stosb(dst, c, n); 41 | return dst; 42 | } 43 | 44 | char* 45 | strchr(const char *s, char c) 46 | { 47 | for(; *s; s++) 48 | if(*s == c) 49 | return (char*)s; 50 | return 0; 51 | } 52 | 53 | char* 54 | gets(char *buf, int max) 55 | { 56 | int i, cc; 57 | char c; 58 | 59 | for(i=0; i+1 < max; ){ 60 | cc = read(0, &c, 1); 61 | if(cc < 1) 62 | break; 63 | buf[i++] = c; 64 | if(c == '\n' || c == '\r') 65 | break; 66 | } 67 | buf[i] = '\0'; 68 | return buf; 69 | } 70 | 71 | int 72 | stat(const char *n, struct stat *st) 73 | { 74 | int fd; 75 | int r; 76 | 77 | fd = open(n, O_RDONLY); 78 | if(fd < 0) 79 | return -1; 80 | r = fstat(fd, st); 81 | close(fd); 82 | return r; 83 | } 84 | 85 | int 86 | atoi(const char *s) 87 | { 88 | int n; 89 | 90 | n = 0; 91 | while('0' <= *s && *s <= '9') 92 | n = n*10 + *s++ - '0'; 93 | return n; 94 | } 95 | 96 | void* 97 | memmove(void *vdst, const void *vsrc, int n) 98 | { 99 | char *dst; 100 | const char *src; 101 | 102 | dst = vdst; 103 | src = vsrc; 104 | while(n-- > 0) 105 | *dst++ = *src++; 106 | return vdst; 107 | } 108 | 109 | int thread_create(void (*start_routine)(void *, void *), void* arg1, void* arg2) 110 | { 111 | void* stack; 112 | stack = malloc(PGSIZE); 113 | 114 | return clone(start_routine, arg1, arg2, stack); 115 | } 116 | 117 | int thread_join() 118 | { 119 | void * stackPtr; 120 | int x = join(&stackPtr); 121 | return x; 122 | } 123 | 124 | int lock_init(lock_t *lk) 125 | { 126 | lk->flag = 0; 127 | return 0; 128 | } 129 | 130 | void lock_acquire(lock_t *lk){ 131 | while(xchg(&lk->flag, 1) != 0); 132 | } 133 | 134 | void lock_release(lock_t *lk){ 135 | xchg(&lk->flag, 0); 136 | } -------------------------------------------------------------------------------- /umalloc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | #include "param.h" 5 | 6 | // Memory allocator by Kernighan and Ritchie, 7 | // The C programming Language, 2nd ed. Section 8.7. 8 | 9 | typedef long Align; 10 | 11 | union header { 12 | struct { 13 | union header *ptr; 14 | uint size; 15 | } s; 16 | Align x; 17 | }; 18 | 19 | typedef union header Header; 20 | 21 | static Header base; 22 | static Header *freep; 23 | 24 | void 25 | free(void *ap) 26 | { 27 | Header *bp, *p; 28 | 29 | bp = (Header*)ap - 1; 30 | for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) 31 | if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) 32 | break; 33 | if(bp + bp->s.size == p->s.ptr){ 34 | bp->s.size += p->s.ptr->s.size; 35 | bp->s.ptr = p->s.ptr->s.ptr; 36 | } else 37 | bp->s.ptr = p->s.ptr; 38 | if(p + p->s.size == bp){ 39 | p->s.size += bp->s.size; 40 | p->s.ptr = bp->s.ptr; 41 | } else 42 | p->s.ptr = bp; 43 | freep = p; 44 | } 45 | 46 | static Header* 47 | morecore(uint nu) 48 | { 49 | char *p; 50 | Header *hp; 51 | 52 | if(nu < 4096) 53 | nu = 4096; 54 | p = sbrk(nu * sizeof(Header)); 55 | if(p == (char*)-1) 56 | return 0; 57 | hp = (Header*)p; 58 | hp->s.size = nu; 59 | free((void*)(hp + 1)); 60 | return freep; 61 | } 62 | 63 | void* 64 | malloc(uint nbytes) 65 | { 66 | Header *p, *prevp; 67 | uint nunits; 68 | 69 | nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; 70 | if((prevp = freep) == 0){ 71 | base.s.ptr = freep = prevp = &base; 72 | base.s.size = 0; 73 | } 74 | for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ 75 | if(p->s.size >= nunits){ 76 | if(p->s.size == nunits) 77 | prevp->s.ptr = p->s.ptr; 78 | else { 79 | p->s.size -= nunits; 80 | p += p->s.size; 81 | p->s.size = nunits; 82 | } 83 | freep = prevp; 84 | return (void*)(p + 1); 85 | } 86 | if(p == freep) 87 | if((p = morecore(nunits)) == 0) 88 | return 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /user.h: -------------------------------------------------------------------------------- 1 | #ifndef __USER__ 2 | 3 | struct stat; 4 | struct rtcdate; 5 | typedef struct __lock_t{ 6 | uint flag; 7 | }lock_t; 8 | 9 | // system calls 10 | int fork(void); 11 | int exit(void) __attribute__((noreturn)); 12 | int wait(void); 13 | int pipe(int*); 14 | int write(int, const void*, int); 15 | int read(int, void*, int); 16 | int close(int); 17 | int kill(int); 18 | int exec(char*, char**); 19 | int open(const char*, int); 20 | int mknod(const char*, short, short); 21 | int unlink(const char*); 22 | int fstat(int fd, struct stat*); 23 | int link(const char*, const char*); 24 | int mkdir(const char*); 25 | int chdir(const char*); 26 | int dup(int); 27 | int getpid(void); 28 | char* sbrk(int); 29 | int sleep(int); 30 | int uptime(void); 31 | int clone(void (*start_routine)(void*,void*), void *, void *, void *); 32 | int join(void**); 33 | 34 | // ulib.c 35 | int stat(const char*, struct stat*); 36 | char* strcpy(char*, const char*); 37 | void *memmove(void*, const void*, int); 38 | char* strchr(const char*, char c); 39 | int strcmp(const char*, const char*); 40 | void printf(int, const char*, ...); 41 | char* gets(char*, int max); 42 | uint strlen(const char*); 43 | void* memset(void*, int, uint); 44 | void* malloc(uint); 45 | void free(void*); 46 | int atoi(const char*); 47 | int thread_create(void (*start_routine)(void *,void*), void * arg1, void * arg2); 48 | int thread_join(); 49 | int lock_init(lock_t *lk); 50 | void lock_acquire(lock_t *lk); 51 | void lock_release(lock_t *lk); 52 | 53 | #endif -------------------------------------------------------------------------------- /usys.S: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | #include "traps.h" 3 | 4 | #define SYSCALL(name) \ 5 | .globl name; \ 6 | name: \ 7 | movl $SYS_ ## name, %eax; \ 8 | int $T_SYSCALL; \ 9 | ret 10 | 11 | SYSCALL(fork) 12 | SYSCALL(exit) 13 | SYSCALL(wait) 14 | SYSCALL(pipe) 15 | SYSCALL(read) 16 | SYSCALL(write) 17 | SYSCALL(close) 18 | SYSCALL(kill) 19 | SYSCALL(exec) 20 | SYSCALL(open) 21 | SYSCALL(mknod) 22 | SYSCALL(unlink) 23 | SYSCALL(fstat) 24 | SYSCALL(link) 25 | SYSCALL(mkdir) 26 | SYSCALL(chdir) 27 | SYSCALL(dup) 28 | SYSCALL(getpid) 29 | SYSCALL(sbrk) 30 | SYSCALL(sleep) 31 | SYSCALL(uptime) 32 | SYSCALL(clone) 33 | SYSCALL(join) 34 | -------------------------------------------------------------------------------- /vectors.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Generate vectors.S, the trap/interrupt entry points. 4 | # There has to be one entry point per interrupt number 5 | # since otherwise there's no way for trap() to discover 6 | # the interrupt number. 7 | 8 | print "# generated by vectors.pl - do not edit\n"; 9 | print "# handlers\n"; 10 | print ".globl alltraps\n"; 11 | for(my $i = 0; $i < 256; $i++){ 12 | print ".globl vector$i\n"; 13 | print "vector$i:\n"; 14 | if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ 15 | print " pushl \$0\n"; 16 | } 17 | print " pushl \$$i\n"; 18 | print " jmp alltraps\n"; 19 | } 20 | 21 | print "\n# vector table\n"; 22 | print ".data\n"; 23 | print ".globl vectors\n"; 24 | print "vectors:\n"; 25 | for(my $i = 0; $i < 256; $i++){ 26 | print " .long vector$i\n"; 27 | } 28 | 29 | # sample output: 30 | # # handlers 31 | # .globl alltraps 32 | # .globl vector0 33 | # vector0: 34 | # pushl $0 35 | # pushl $0 36 | # jmp alltraps 37 | # ... 38 | # 39 | # # vector table 40 | # .data 41 | # .globl vectors 42 | # vectors: 43 | # .long vector0 44 | # .long vector1 45 | # .long vector2 46 | # ... 47 | 48 | -------------------------------------------------------------------------------- /wc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | char buf[512]; 6 | 7 | void 8 | wc(int fd, char *name) 9 | { 10 | int i, n; 11 | int l, w, c, inword; 12 | 13 | l = w = c = 0; 14 | inword = 0; 15 | while((n = read(fd, buf, sizeof(buf))) > 0){ 16 | for(i=0; i> 16; 70 | 71 | asm volatile("lgdt (%0)" : : "r" (pd)); 72 | } 73 | 74 | struct gatedesc; 75 | 76 | static inline void 77 | lidt(struct gatedesc *p, int size) 78 | { 79 | volatile ushort pd[3]; 80 | 81 | pd[0] = size-1; 82 | pd[1] = (uint)p; 83 | pd[2] = (uint)p >> 16; 84 | 85 | asm volatile("lidt (%0)" : : "r" (pd)); 86 | } 87 | 88 | static inline void 89 | ltr(ushort sel) 90 | { 91 | asm volatile("ltr %0" : : "r" (sel)); 92 | } 93 | 94 | static inline uint 95 | readeflags(void) 96 | { 97 | uint eflags; 98 | asm volatile("pushfl; popl %0" : "=r" (eflags)); 99 | return eflags; 100 | } 101 | 102 | static inline void 103 | loadgs(ushort v) 104 | { 105 | asm volatile("movw %0, %%gs" : : "r" (v)); 106 | } 107 | 108 | static inline void 109 | cli(void) 110 | { 111 | asm volatile("cli"); 112 | } 113 | 114 | static inline void 115 | sti(void) 116 | { 117 | asm volatile("sti"); 118 | } 119 | 120 | static inline uint 121 | xchg(volatile uint *addr, uint newval) 122 | { 123 | uint result; 124 | 125 | // The + in "+m" denotes a read-modify-write operand. 126 | asm volatile("lock; xchgl %0, %1" : 127 | "+m" (*addr), "=a" (result) : 128 | "1" (newval) : 129 | "cc"); 130 | return result; 131 | } 132 | 133 | static inline uint 134 | rcr2(void) 135 | { 136 | uint val; 137 | asm volatile("movl %%cr2,%0" : "=r" (val)); 138 | return val; 139 | } 140 | 141 | static inline void 142 | lcr3(uint val) 143 | { 144 | asm volatile("movl %0,%%cr3" : : "r" (val)); 145 | } 146 | 147 | //PAGEBREAK: 36 148 | // Layout of the trap frame built on the stack by the 149 | // hardware and by trapasm.S, and passed to trap(). 150 | struct trapframe { 151 | // registers as pushed by pusha 152 | uint edi; 153 | uint esi; 154 | uint ebp; 155 | uint oesp; // useless & ignored 156 | uint ebx; 157 | uint edx; 158 | uint ecx; 159 | uint eax; 160 | 161 | // rest of trap frame 162 | ushort gs; 163 | ushort padding1; 164 | ushort fs; 165 | ushort padding2; 166 | ushort es; 167 | ushort padding3; 168 | ushort ds; 169 | ushort padding4; 170 | uint trapno; 171 | 172 | // below here defined by x86 hardware 173 | uint err; 174 | uint eip; 175 | ushort cs; 176 | ushort padding5; 177 | uint eflags; 178 | 179 | // below here only when crossing rings, such as from user to kernel 180 | uint esp; 181 | ushort ss; 182 | ushort padding6; 183 | }; 184 | -------------------------------------------------------------------------------- /zombie.c: -------------------------------------------------------------------------------- 1 | // Create a zombie process that 2 | // must be reparented at exit. 3 | 4 | #include "types.h" 5 | #include "stat.h" 6 | #include "user.h" 7 | 8 | int 9 | main(void) 10 | { 11 | if(fork() > 0) 12 | sleep(5); // Let child exit before parent. 13 | exit(); 14 | } 15 | --------------------------------------------------------------------------------