├── .cvsignore ├── .dir-locals.el ├── .gdbinit.tmpl ├── .gitignore ├── BUGS ├── LICENSE ├── Makefile ├── Notes ├── README ├── 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 ├── forktest.c ├── 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 ├── 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 | GPATH 18 | GRTAGS 19 | GTAGS -------------------------------------------------------------------------------- /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-jos-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 -Og -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 | # 创建一个名为xv6.img的文件,大小为512*10000字节(约4.9MiB),且每个字节的值都为0 95 | dd if=/dev/zero of=xv6.img count=10000 96 | # 从xv6.img的第1个字节开始,顺序将bootblock文件中的每个字节覆盖到xv6.img文件里(bootblock文件小于等于512字节) 97 | dd if=bootblock of=xv6.img conv=notrunc 98 | # 从xv6.img的第513个字节开始,顺序将kernel文件中的每个字节覆盖到xv6.img文件里 99 | dd if=kernel of=xv6.img seek=1 conv=notrunc 100 | 101 | xv6memfs.img: bootblock kernelmemfs 102 | dd if=/dev/zero of=xv6memfs.img count=10000 103 | dd if=bootblock of=xv6memfs.img conv=notrunc 104 | dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc 105 | 106 | # bootblock为xv6操作系统的bootloader,它位于第一扇区(启动盘的第一个512字节) 107 | # 开机后,bios会将booloader的代码加载到内存地址0x7c00位置,然后再跳到0x7c00位置开始执行bootloader的代码 108 | bootblock: bootasm.S bootmain.c 109 | $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c 110 | $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S 111 | # 将bootblock代码的初始地址设置为0x7c00,开机后bios也是将bootblock的代码加载到0x7c00 112 | # 两个地址要一样,否则bootblock内相互引用的地址将会是错乱的 113 | # 设置start为bootblock的入口地址(-e start)其实不是必须的,因为start指向的是bootblock的第一行代码(加载到内存后,地址是0x7c00),在bios跳到bootblock后会被默认执行 114 | # 之所以有这个设置,我猜是为了消除警告(ld默认的入口地址为_start,bootblock中没这个地址,如果再不显式设置入口地址的话,ld会抛出警告) 115 | $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o 116 | $(OBJDUMP) -S bootblock.o > bootblock.asm 117 | # 将bootblock.o内的代码拷贝到bootblock里 118 | # 可以通过 hexdump -C bootblock 和 objdump -d bootblock.o 命令比较最终代码的内容,会发现是完全一样的(要注释掉下面一行,防止bootblock内容被修改) 119 | $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock 120 | # 将bootblock大小扩充至512字节,且设置最后两个字节的内容为0x55和0xAA 121 | # 这样bios就可以通过最后两个字节,确定该sector为boot sector 122 | ./sign.pl bootblock 123 | 124 | entryother: entryother.S 125 | $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S 126 | $(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o 127 | $(OBJCOPY) -S -O binary -j .text bootblockother.o entryother 128 | $(OBJDUMP) -S bootblockother.o > entryother.asm 129 | 130 | initcode: initcode.S 131 | $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S 132 | $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o 133 | $(OBJCOPY) -S -O binary initcode.out initcode 134 | $(OBJDUMP) -S initcode.o > initcode.asm 135 | 136 | kernel: $(OBJS) entry.o entryother initcode kernel.ld 137 | $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother 138 | $(OBJDUMP) -S kernel > kernel.asm 139 | $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym 140 | 141 | # kernelmemfs is a copy of kernel that maintains the 142 | # disk image in memory instead of writing to a disk. 143 | # This is not so useful for testing persistent storage or 144 | # exploring disk buffering implementations, but it is 145 | # great for testing the kernel on real hardware without 146 | # needing a scratch disk. 147 | MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o 148 | kernelmemfs: $(MEMFSOBJS) entry.o entryother initcode kernel.ld fs.img 149 | $(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o $(MEMFSOBJS) -b binary initcode entryother fs.img 150 | $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm 151 | $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym 152 | 153 | tags: $(OBJS) entryother.S _init 154 | etags *.S *.c 155 | 156 | vectors.S: vectors.pl 157 | ./vectors.pl > vectors.S 158 | 159 | ULIB = ulib.o usys.o printf.o umalloc.o 160 | 161 | _%: %.o $(ULIB) 162 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ 163 | $(OBJDUMP) -S $@ > $*.asm 164 | $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym 165 | 166 | # forktest has less library code linked in - needs to be small 167 | # in order to be able to max out the proc table. 168 | _forktest: forktest.o $(ULIB) 169 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o 170 | $(OBJDUMP) -S _forktest > forktest.asm 171 | 172 | mkfs: mkfs.c fs.h 173 | gcc -Werror -Wall -o mkfs mkfs.c 174 | 175 | # Prevent deletion of intermediate files, e.g. cat.o, after first build, so 176 | # that disk image changes after first build are persistent until clean. More 177 | # details: 178 | # http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html 179 | .PRECIOUS: %.o 180 | 181 | UPROGS=\ 182 | _cat\ 183 | _echo\ 184 | _forktest\ 185 | _grep\ 186 | _init\ 187 | _kill\ 188 | _ln\ 189 | _ls\ 190 | _mkdir\ 191 | _rm\ 192 | _sh\ 193 | _stressfs\ 194 | _usertests\ 195 | _wc\ 196 | _zombie\ 197 | 198 | fs.img: mkfs README $(UPROGS) 199 | ./mkfs fs.img README $(UPROGS) 200 | 201 | -include *.d 202 | 203 | clean: 204 | rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ 205 | *.o *.d *.asm *.sym vectors.S bootblock entryother \ 206 | initcode initcode.out kernel xv6.img fs.img kernelmemfs \ 207 | xv6memfs.img mkfs .gdbinit \ 208 | $(UPROGS) 209 | 210 | # make a printout 211 | FILES = $(shell grep -v '^\#' runoff.list) 212 | PRINT = runoff.list runoff.spec README toc.hdr toc.ftr $(FILES) 213 | 214 | xv6.pdf: $(PRINT) 215 | ./runoff 216 | ls -l xv6.pdf 217 | 218 | print: xv6.pdf 219 | 220 | # run in emulators 221 | 222 | bochs : fs.img xv6.img 223 | if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi 224 | bochs -q 225 | 226 | # try to generate a unique GDB port 227 | GDBPORT = $(shell expr `id -u` % 5000 + 25000) 228 | # QEMU's gdb stub command line changed in 0.11 229 | QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ 230 | then echo "-gdb tcp::$(GDBPORT)"; \ 231 | else echo "-s -p $(GDBPORT)"; fi) 232 | ifndef CPUS 233 | CPUS := 2 234 | endif 235 | 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) 236 | 237 | qemu: fs.img xv6.img 238 | $(QEMU) -serial mon:stdio $(QEMUOPTS) 239 | 240 | qemu-memfs: xv6memfs.img 241 | $(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256 242 | 243 | qemu-nox: fs.img xv6.img 244 | $(QEMU) -nographic $(QEMUOPTS) 245 | 246 | .gdbinit: .gdbinit.tmpl 247 | sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ 248 | 249 | qemu-gdb: fs.img xv6.img .gdbinit 250 | @echo "*** Now run 'gdb'." 1>&2 251 | $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) 252 | 253 | qemu-nox-gdb: fs.img xv6.img .gdbinit 254 | @echo "*** Now run 'gdb'." 1>&2 255 | $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) 256 | 257 | # CUT HERE 258 | # prepare dist for students 259 | # after running make dist, probably want to 260 | # rename it to rev0 or rev1 or so on and then 261 | # check in that version. 262 | 263 | EXTRA=\ 264 | mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\ 265 | ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\ 266 | printf.c umalloc.c\ 267 | README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\ 268 | .gdbinit.tmpl gdbutil\ 269 | 270 | dist: 271 | rm -rf dist 272 | mkdir dist 273 | for i in $(FILES); \ 274 | do \ 275 | grep -v PAGEBREAK $$i >dist/$$i; \ 276 | done 277 | sed '/CUT HERE/,$$d' Makefile >dist/Makefile 278 | echo >dist/runoff.spec 279 | cp $(EXTRA) dist 280 | 281 | dist-test: 282 | rm -rf dist 283 | make dist 284 | rm -rf dist-test 285 | mkdir dist-test 286 | cp dist/* dist-test 287 | cd dist-test; $(MAKE) print 288 | cd dist-test; $(MAKE) bochs || true 289 | cd dist-test; $(MAKE) qemu 290 | 291 | # update this rule (change rev#) when it is time to 292 | # make a new revision. 293 | tar: 294 | rm -rf /tmp/xv6 295 | mkdir -p /tmp/xv6 296 | cp dist/* dist/.gdbinit.tmpl /tmp/xv6 297 | (cd /tmp; tar cf - xv6) | gzip >xv6-rev10.tar.gz # the next one will be 10 (9/17) 298 | 299 | .PHONY: dist-test dist 300 | -------------------------------------------------------------------------------- /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: -------------------------------------------------------------------------------- 1 | xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix 2 | Version 6 (v6). xv6 loosely follows the structure and style of v6, 3 | but is implemented for a modern x86-based multiprocessor using ANSI C. 4 | 5 | ACKNOWLEDGMENTS 6 | 7 | xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer 8 | to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, 9 | 2000)). See also https://pdos.csail.mit.edu/6.828/, which 10 | provides pointers to on-line resources for v6. 11 | 12 | xv6 borrows code from the following sources: 13 | JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others) 14 | Plan 9 (entryother.S, mp.h, mp.c, lapic.c) 15 | FreeBSD (ioapic.c) 16 | NetBSD (console.c) 17 | 18 | The following people have made contributions: Russ Cox (context switching, 19 | locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin 20 | Clements. 21 | 22 | We are also grateful for the bug reports and patches contributed by Silas 23 | Boyd-Wickizer, Anton Burtsev, Cody Cutler, Mike CAT, Tej Chajed, eyalz800, 24 | Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, Peter 25 | Froehlich, Yakir Goaron,Shivam Handa, Bryan Henry, Jim Huang, Alexander 26 | Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller, Eddie Kohler, Austin 27 | Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi 28 | Merimovich, Mark Morrissey, mtasm, Joel Nider, Greg Price, Ayan Shafqat, 29 | Eldar Sehayek, Yongming Shen, Cam Tenny, tyfkda, Rafael Ubal, Warren 30 | Toomey, Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas 31 | Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang Wei. 32 | 33 | The code in the files that constitute xv6 is 34 | Copyright 2006-2018 Frans Kaashoek, Robert Morris, and Russ Cox. 35 | 36 | ERROR REPORTS 37 | 38 | We switched our focus to xv6 on RISC-V; see the mit-pdos/xv6-riscv.git 39 | repository on github.com. 40 | 41 | BUILDING AND RUNNING XV6 42 | 43 | To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run 44 | "make". On non-x86 or non-ELF machines (like OS X, even on x86), you 45 | will need to install a cross-compiler gcc suite capable of producing 46 | x86 ELF binaries (see https://pdos.csail.mit.edu/6.828/). 47 | Then run "make TOOLPREFIX=i386-jos-elf-". Now install the QEMU PC 48 | simulator and run "make qemu". -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv6 OS 2 | 3 | xv6是美国麻省理工学院(MIT)用来教学的一个操作系统,它麻雀虽小,但五脏俱全。 4 | 5 | 原项目的地址为[mit-pdos/xv6-public](https://github.com/mit-pdos/xv6-public),本项目会一直保持与原项目代码同步。 6 | 7 | 如果你想学操作系统的内部原理,想学如何自己从零写一个操作系统,该项目是非常好的入门资料。 8 | 9 | 可能对有部分人来说,直接阅读该项目的源码还有些困难。 10 | 11 | 我会尽可能的加非常详细的中文注释在代码里面。 12 | 13 | 也欢迎其他人提issue来讨论代码细节。 14 | 15 | 16 | ## 学习源码会用到的资料 17 | 18 | - [book-rev11.pdf](https://pdos.csail.mit.edu/6.828/2018/xv6/book-rev11.pdf) 19 | - [xv6-rev11.pdf](https://pdos.csail.mit.edu/6.828/2018/xv6/xv6-rev11.pdf) 20 | - [Intel® 64 and IA-32 Architectures Software Developer’s Manuals](https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf) 21 | - [gcc文档](https://gcc.gnu.org/onlinedocs/gcc/index.html) 22 | - [gas文档](https://sourceware.org/binutils/docs-2.33.1/as/index.html) 23 | - [ld文档](https://sourceware.org/binutils/docs/ld/index.html) 24 | - [gdb文档](https://sourceware.org/gdb/current/onlinedocs/gdb/) 25 | - [make文档](https://www.gnu.org/software/make/manual/html_node/index.html) -------------------------------------------------------------------------------- /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 | // lim后12位被丢弃的原因是,当前lim的单位是4KiB 12 | #define SEG_ASM(type,base,lim) \ 13 | .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ 14 | .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ 15 | (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) 16 | 17 | #define STA_X 0x8 // Executable segment 18 | #define STA_W 0x2 // Writeable (non-executable segments) 19 | #define STA_R 0x2 // Readable (executable segments) 这个是没有错误的,具体可见 20 | -------------------------------------------------------------------------------- /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 # 声明start对外开放,这样Makefile中才可以使用 12 | start: 13 | cli # BIOS enabled interrupts; disable 14 | 15 | # Zero data segment registers DS, ES, and SS. (cs在执行代码时会被用到,所以不能被修改) 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 | # 有关A20更多信息,请看下面的链接: 24 | # https://wiki.osdev.org/A20_Line 25 | # 有关seta20.1和seta20.2代码是什么意思,请看下面的链接: 26 | # https://www.win.tue.nl/~aeb/linux/kbd/A20.html 27 | # https://wiki.osdev.org/I/O_Ports 28 | # https://wiki.osdev.org/"8042"_PS/2_Controller 29 | seta20.1: 30 | inb $0x64,%al # Wait for not busy 31 | testb $0x2,%al 32 | jnz seta20.1 33 | 34 | movb $0xd1,%al # 0xd1 -> port 0x64 35 | outb %al,$0x64 36 | 37 | seta20.2: 38 | inb $0x64,%al # Wait for not busy 39 | testb $0x2,%al 40 | jnz seta20.2 41 | 42 | movb $0xdf,%al # 0xdf -> port 0x60 43 | outb %al,$0x60 44 | 45 | # Switch from real to protected mode. Use a bootstrap GDT that makes 46 | # virtual addresses map directly to physical addresses so that the 47 | # effective memory map doesn't change during the transition. 48 | lgdt gdtdesc # 初始化gdt 49 | movl %cr0, %eax 50 | orl $CR0_PE, %eax # 开启保护模式(cr0寄存器字段描述请看:https://wiki.osdev.org/CPU_Registers_x86) 51 | movl %eax, %cr0 52 | 53 | //PAGEBREAK! 54 | # Complete the transition to 32-bit protected mode by using a long jmp 55 | # to reload %cs and %eip. The segment descriptors are set up with no 56 | # translation, so that the mapping is still the identity mapping. 57 | # SEG_KCODE值为1,表示引用gdt中下标为1的segment descriptor,即内核代码 58 | # 左移3位的原因是segment selector的低3位要用于表示其他意义 59 | # 有关segment selector的具体格式请看intel开发手册卷3的3.4.2部分 60 | ljmp $(SEG_KCODE<<3), $start32 61 | 62 | .code32 # Tell assembler to generate 32-bit code now. 63 | start32: 64 | # Set up the protected-mode data segment registers 65 | movw $(SEG_KDATA<<3), %ax # Our data segment selector,SEG_KDATA值为2,表示指向gdt下标为2的segment descriptor 66 | movw %ax, %ds # -> DS: Data Segment 67 | movw %ax, %es # -> ES: Extra Segment 68 | movw %ax, %ss # -> SS: Stack Segment 69 | movw $0, %ax # Zero segments not ready for use,gdt的0下标指向的数据为null 70 | movw %ax, %fs # -> FS 71 | movw %ax, %gs # -> GS 72 | 73 | # Set up the stack pointer and call into C. 74 | # start为该代码的起始地址,即0x7c00 75 | # 下面将start值放入%esp中用来设置堆栈起始地址 76 | # 因为堆栈是向下增长的,所以说内存区域0x7c00地址往下是堆栈区,0x7c00地址往上是代码区 77 | movl $start, %esp 78 | 79 | # 调用bootmain.c中的bootmain方法 80 | call bootmain 81 | 82 | # 下面的逻辑不应该被执行,除非代码bug了 83 | # If bootmain returns (it shouldn't), trigger a Bochs 84 | # breakpoint if running under Bochs, then loop. 85 | movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 86 | movw %ax, %dx 87 | outw %ax, %dx 88 | movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 89 | outw %ax, %dx 90 | spin: 91 | jmp spin 92 | 93 | # Bootstrap GDT 94 | # 有关gdt和gdtdesc的详细格式说明,请阅读 Intel® 64 and IA-32 Architectures Software Developer’s Manual 的第3章的第3.4和3.5节 95 | # 地址为:https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf 96 | .p2align 2 # force 4 byte alignment 97 | gdt: 98 | SEG_NULLASM # null seg,gdt的地一个descriptor不用 99 | SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 100 | SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg 101 | 102 | gdtdesc: 103 | .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1,gdt的地址范围是[base, base+limit],包含最后一个字节,所以减1 104 | .long gdt # address gdt 105 | 106 | -------------------------------------------------------------------------------- /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 | // 将kernel elf header加载到这个位置 26 | elf = (struct elfhdr*)0x10000; // scratch space 27 | 28 | // 到目前为止可知的内存占用情况: 29 | // [0x100000-MAX ] | kernel | 30 | // [0x0a0000-0x100000) | device memory | 31 | // [0x010000-0x011000) | kernel elf header | 32 | // [0x007c00-0x007e00) | bootblock | 33 | // [0x000000-0x007c00) | stack | 34 | 35 | // Read 1st page off disk 36 | readseg((uchar*)elf, 4096, 0); 37 | 38 | // Is this an ELF executable? 39 | if(elf->magic != ELF_MAGIC) 40 | return; // let bootasm.S handle error 41 | 42 | // Load each program segment (ignores ph flags). 43 | // elf的具体格式请看:http://man7.org/linux/man-pages/man5/elf.5.html 44 | ph = (struct proghdr*)((uchar*)elf + elf->phoff); 45 | eph = ph + elf->phnum; 46 | for(; ph < eph; ph++){ 47 | pa = (uchar*)ph->paddr; 48 | readseg(pa, ph->filesz, ph->off); 49 | if(ph->memsz > ph->filesz) 50 | stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz); 51 | } 52 | 53 | // Call the entry point from the ELF header. 54 | // Does not return! 55 | 56 | entry = (void(*)(void))(elf->entry); 57 | entry(); // entry指向的是entry.S中的_start(物理地址是0x10000c) 58 | } 59 | 60 | void 61 | waitdisk(void) 62 | { 63 | // Wait for disk ready. 64 | while((inb(0x1F7) & 0xC0) != 0x40) 65 | ; 66 | } 67 | 68 | // Read a single sector at offset into dst. 69 | void 70 | readsect(void *dst, uint offset) 71 | { 72 | // Issue command. 73 | waitdisk(); 74 | outb(0x1F2, 1); // count = 1 75 | outb(0x1F3, offset); 76 | outb(0x1F4, offset >> 8); 77 | outb(0x1F5, offset >> 16); 78 | outb(0x1F6, (offset >> 24) | 0xE0); 79 | outb(0x1F7, 0x20); // cmd 0x20 - read sectors 80 | 81 | // Read data. 82 | waitdisk(); 83 | insl(0x1F0, dst, SECTSIZE/4); 84 | } 85 | 86 | // Read 'count' bytes at 'offset' from kernel into physical address 'pa'. 87 | // Might copy more than asked. 88 | void 89 | readseg(uchar* pa, uint count, uint offset) 90 | { 91 | uchar* epa; 92 | 93 | epa = pa + count; 94 | 95 | // Round down to sector boundary. 96 | pa -= offset % SECTSIZE; 97 | 98 | // Translate from bytes to sectors; kernel starts at sector 1. 99 | offset = (offset / SECTSIZE) + 1; 100 | 101 | // If this is too slow, we could read lots of sectors at a time. 102 | // We'd write more to memory than asked, but it doesn't matter -- 103 | // we load in increasing order. 104 | for(; pa < epa; pa += SECTSIZE, offset++) 105 | readsect(pa, offset); 106 | } 107 | -------------------------------------------------------------------------------- /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 | 159 | // timer.c 160 | void timerinit(void); 161 | 162 | // trap.c 163 | void idtinit(void); 164 | extern uint ticks; 165 | void tvinit(void); 166 | extern struct spinlock tickslock; 167 | 168 | // uart.c 169 | void uartinit(void); 170 | void uartintr(void); 171 | void uartputc(int); 172 | 173 | // vm.c 174 | void seginit(void); 175 | void kvmalloc(void); 176 | pde_t* setupkvm(void); 177 | char* uva2ka(pde_t*, char*); 178 | int allocuvm(pde_t*, uint, uint); 179 | int deallocuvm(pde_t*, uint, uint); 180 | void freevm(pde_t*); 181 | void inituvm(pde_t*, char*, uint); 182 | int loaduvm(pde_t*, char*, struct inode*, uint, uint); 183 | pde_t* copyuvm(pde_t*, uint); 184 | void switchuvm(struct proc*); 185 | void switchkvm(void); 186 | int copyout(pde_t*, uint, void*, uint); 187 | void clearpteu(pde_t *pgdir, char *uva); 188 | 189 | // number of elements in fixed-size array 190 | #define NELEM(x) (sizeof(x)/sizeof((x)[0])) 191 | -------------------------------------------------------------------------------- /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 # entrypgdir在main.c里 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 # 因为paging已经设置好,所以main方法是虚拟地址是可行的 66 | jmp *%eax 67 | 68 | .comm stack, KSTACKSIZE # 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 | -------------------------------------------------------------------------------- /forktest.c: -------------------------------------------------------------------------------- 1 | // Test that fork fails gracefully. 2 | // Tiny executable so that the limit can be filling the proc table. 3 | 4 | #include "types.h" 5 | #include "stat.h" 6 | #include "user.h" 7 | 8 | #define N 1000 9 | 10 | void 11 | printf(int fd, const char *s, ...) 12 | { 13 | write(fd, s, strlen(s)); 14 | } 15 | 16 | void 17 | forktest(void) 18 | { 19 | int n, pid; 20 | 21 | printf(1, "fork test\n"); 22 | 23 | for(n=0; n 0; n--){ 37 | if(wait() < 0){ 38 | printf(1, "wait stopped early\n"); 39 | exit(); 40 | } 41 | } 42 | 43 | if(wait() != -1){ 44 | printf(1, "wait got too many\n"); 45 | exit(); 46 | } 47 | 48 | printf(1, "fork test OK\n"); 49 | } 50 | 51 | int 52 | main(void) 53 | { 54 | forktest(); 55 | exit(); 56 | } 57 | -------------------------------------------------------------------------------- /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) { /* bootmain.C 会把kernel加载到这个物理地址 */ 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 | BYTE(0) /* Force the linker to allocate space 30 | for this section */ 31 | } 32 | 33 | .stabstr : { 34 | PROVIDE(__STABSTR_BEGIN__ = .); 35 | *(.stabstr); 36 | PROVIDE(__STABSTR_END__ = .); 37 | BYTE(0) /* Force the linker to allocate space 38 | for this section */ 39 | } 40 | 41 | /* Adjust the address for the data segment to the next page */ 42 | . = ALIGN(0x1000); 43 | 44 | /* Conventionally, Unix linkers provide pseudo-symbols 45 | * etext, edata, and end, at the end of the text, data, and bss. 46 | * For the kernel mapping, we need the address at the beginning 47 | * of the data section, but that's not one of the conventional 48 | * symbols, because the convention started before there was a 49 | * read-only rodata section between text and data. */ 50 | PROVIDE(data = .); 51 | 52 | /* The data segment */ 53 | .data : { 54 | *(.data) 55 | } 56 | 57 | PROVIDE(edata = .); 58 | 59 | .bss : { 60 | *(.bss) 61 | } 62 | 63 | PROVIDE(end = .); 64 | 65 | /DISCARD/ : { 66 | *(.eh_frame .note.GNU-stack) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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 66 | scheduler(); // start running processes 67 | } 68 | 69 | pde_t entrypgdir[]; // For entry.S 70 | 71 | // Start the non-boot (AP) processors. 72 | static void 73 | startothers(void) 74 | { 75 | extern uchar _binary_entryother_start[], _binary_entryother_size[]; 76 | uchar *code; 77 | struct cpu *c; 78 | char *stack; 79 | 80 | // Write entry code to unused memory at 0x7000. 81 | // The linker has placed the image of entryother.S in 82 | // _binary_entryother_start. 83 | code = P2V(0x7000); 84 | memmove(code, _binary_entryother_start, (uint)_binary_entryother_size); 85 | 86 | for(c = cpus; c < cpus+ncpu; c++){ 87 | if(c == mycpu()) // We've started already. 88 | continue; 89 | 90 | // Tell entryother.S what stack to use, where to enter, and what 91 | // pgdir to use. We cannot use kpgdir yet, because the AP processor 92 | // is running in low memory, so we use entrypgdir for the APs too. 93 | stack = kalloc(); 94 | *(void**)(code-4) = stack + KSTACKSIZE; 95 | *(void(**)(void))(code-8) = mpenter; 96 | *(int**)(code-12) = (void *) V2P(entrypgdir); 97 | 98 | lapicstartap(c->apicid, V2P(code)); 99 | 100 | // wait for cpu to finish mpmain() 101 | while(c->started == 0) 102 | ; 103 | } 104 | } 105 | 106 | // The boot page table used in entry.S and entryother.S. 107 | // Page directories (and page tables) must start on page boundaries, 108 | // hence the __aligned__ attribute. 109 | // PTE_PS in a page directory entry enables 4Mbyte pages. 110 | 111 | __attribute__((__aligned__(PGSIZE))) 112 | pde_t entrypgdir[NPDENTRIES] = { 113 | // Map VA's [0, 4MB) to PA's [0, 4MB) 114 | [0] = (0) | PTE_P | PTE_W | PTE_PS, 115 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 116 | // 0x80000000映射到0,所以0x80100000映射到0x100000 117 | // 0x80100000正好是kernel的起始虚拟地址 118 | // 0x100000正好是kernel被加载到物理内存的其实地址 119 | [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS, 120 | }; 121 | 122 | //PAGEBREAK! 123 | // Blank page. 124 | //PAGEBREAK! 125 | // Blank page. 126 | //PAGEBREAK! 127 | // Blank page. 128 | 129 | -------------------------------------------------------------------------------- /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 | enum procstate state; // Process state 43 | int pid; // Process ID 44 | struct proc *parent; // Parent process 45 | struct trapframe *tf; // Trap frame for current syscall 46 | struct context *context; // swtch() here to run process 47 | void *chan; // If non-zero, sleeping on chan 48 | int killed; // If non-zero, have been killed 49 | struct file *ofile[NOFILE]; // Open files 50 | struct inode *cwd; // Current directory 51 | char name[16]; // Process name (debugging) 52 | }; 53 | 54 | // Process memory is laid out contiguously, low addresses first: 55 | // text 56 | // original data and bss 57 | // fixed-size stack 58 | // expandable heap 59 | -------------------------------------------------------------------------------- /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){ # 最后两个字节要留给boot sector做标记 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); # 如果不够510字节,用0补全 15 | $buf .= "\x55\xAA"; # 最后两个字节是0x55和0xAA,bios通过这个标志来判断,该sector是boot sector 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 | 107 | static int (*syscalls[])(void) = { 108 | [SYS_fork] sys_fork, 109 | [SYS_exit] sys_exit, 110 | [SYS_wait] sys_wait, 111 | [SYS_pipe] sys_pipe, 112 | [SYS_read] sys_read, 113 | [SYS_kill] sys_kill, 114 | [SYS_exec] sys_exec, 115 | [SYS_fstat] sys_fstat, 116 | [SYS_chdir] sys_chdir, 117 | [SYS_dup] sys_dup, 118 | [SYS_getpid] sys_getpid, 119 | [SYS_sbrk] sys_sbrk, 120 | [SYS_sleep] sys_sleep, 121 | [SYS_uptime] sys_uptime, 122 | [SYS_open] sys_open, 123 | [SYS_write] sys_write, 124 | [SYS_mknod] sys_mknod, 125 | [SYS_unlink] sys_unlink, 126 | [SYS_link] sys_link, 127 | [SYS_mkdir] sys_mkdir, 128 | [SYS_close] sys_close, 129 | }; 130 | 131 | void 132 | syscall(void) 133 | { 134 | int num; 135 | struct proc *curproc = myproc(); 136 | 137 | num = curproc->tf->eax; 138 | if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 139 | curproc->tf->eax = syscalls[num](); 140 | } else { 141 | cprintf("%d %s: unknown sys call %d\n", 142 | curproc->pid, curproc->name, num); 143 | curproc->tf->eax = -1; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "x86.h" 6 | 7 | char* 8 | strcpy(char *s, const char *t) 9 | { 10 | char *os; 11 | 12 | os = s; 13 | while((*s++ = *t++) != 0) 14 | ; 15 | return os; 16 | } 17 | 18 | int 19 | strcmp(const char *p, const char *q) 20 | { 21 | while(*p && *p == *q) 22 | p++, q++; 23 | return (uchar)*p - (uchar)*q; 24 | } 25 | 26 | uint 27 | strlen(const char *s) 28 | { 29 | int n; 30 | 31 | for(n = 0; s[n]; n++) 32 | ; 33 | return n; 34 | } 35 | 36 | void* 37 | memset(void *dst, int c, uint n) 38 | { 39 | stosb(dst, c, n); 40 | return dst; 41 | } 42 | 43 | char* 44 | strchr(const char *s, char c) 45 | { 46 | for(; *s; s++) 47 | if(*s == c) 48 | return (char*)s; 49 | return 0; 50 | } 51 | 52 | char* 53 | gets(char *buf, int max) 54 | { 55 | int i, cc; 56 | char c; 57 | 58 | for(i=0; i+1 < max; ){ 59 | cc = read(0, &c, 1); 60 | if(cc < 1) 61 | break; 62 | buf[i++] = c; 63 | if(c == '\n' || c == '\r') 64 | break; 65 | } 66 | buf[i] = '\0'; 67 | return buf; 68 | } 69 | 70 | int 71 | stat(const char *n, struct stat *st) 72 | { 73 | int fd; 74 | int r; 75 | 76 | fd = open(n, O_RDONLY); 77 | if(fd < 0) 78 | return -1; 79 | r = fstat(fd, st); 80 | close(fd); 81 | return r; 82 | } 83 | 84 | int 85 | atoi(const char *s) 86 | { 87 | int n; 88 | 89 | n = 0; 90 | while('0' <= *s && *s <= '9') 91 | n = n*10 + *s++ - '0'; 92 | return n; 93 | } 94 | 95 | void* 96 | memmove(void *vdst, const void *vsrc, int n) 97 | { 98 | char *dst; 99 | const char *src; 100 | 101 | dst = vdst; 102 | src = vsrc; 103 | while(n-- > 0) 104 | *dst++ = *src++; 105 | return vdst; 106 | } 107 | -------------------------------------------------------------------------------- /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 | struct stat; 2 | struct rtcdate; 3 | 4 | // system calls 5 | int fork(void); 6 | int exit(void) __attribute__((noreturn)); 7 | int wait(void); 8 | int pipe(int*); 9 | int write(int, const void*, int); 10 | int read(int, void*, int); 11 | int close(int); 12 | int kill(int); 13 | int exec(char*, char**); 14 | int open(const char*, int); 15 | int mknod(const char*, short, short); 16 | int unlink(const char*); 17 | int fstat(int fd, struct stat*); 18 | int link(const char*, const char*); 19 | int mkdir(const char*); 20 | int chdir(const char*); 21 | int dup(int); 22 | int getpid(void); 23 | char* sbrk(int); 24 | int sleep(int); 25 | int uptime(void); 26 | 27 | // ulib.c 28 | int stat(const char*, struct stat*); 29 | char* strcpy(char*, const char*); 30 | void *memmove(void*, const void*, int); 31 | char* strchr(const char*, char c); 32 | int strcmp(const char*, const char*); 33 | void printf(int, const char*, ...); 34 | char* gets(char*, int max); 35 | uint strlen(const char*); 36 | void* memset(void*, int, uint); 37 | void* malloc(uint); 38 | void free(void*); 39 | int atoi(const char*); 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------