├── .dir-locals.el ├── .editorconfig ├── .gdbinit.tmpl-riscv ├── .gitignore ├── LICENSE ├── Makefile ├── README ├── book-riscv-rev1.pdf ├── conf └── lab.mk ├── grade-lab-util ├── gradelib.py ├── kernel ├── bio.c ├── buf.h ├── console.c ├── date.h ├── defs.h ├── elf.h ├── entry.S ├── exec.c ├── fcntl.h ├── file.c ├── file.h ├── fs.c ├── fs.h ├── kalloc.c ├── kernel.ld ├── kernelvec.S ├── log.c ├── main.c ├── memlayout.h ├── param.h ├── pipe.c ├── plic.c ├── printf.c ├── proc.c ├── proc.h ├── ramdisk.c ├── riscv.h ├── sleeplock.c ├── sleeplock.h ├── spinlock.c ├── spinlock.h ├── start.c ├── stat.h ├── string.c ├── swtch.S ├── syscall.c ├── syscall.h ├── sysfile.c ├── sysproc.c ├── trampoline.S ├── trap.c ├── types.h ├── uart.c ├── virtio.h ├── virtio_disk.c └── vm.c ├── mkfs └── mkfs.c ├── time.txt └── user ├── cat.c ├── echo.c ├── find.c ├── forktest.c ├── grep.c ├── grind.c ├── init.c ├── initcode.S ├── kill.c ├── ln.c ├── ls.c ├── mkdir.c ├── pingpong.c ├── primes.c ├── printf.c ├── rm.c ├── sh.c ├── sleep.c ├── stressfs.c ├── ulib.c ├── umalloc.c ├── user.h ├── usertests.c ├── usys.pl ├── wc.c ├── xargs.c ├── xargstest.sh └── zombie.c /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((c-mode 2 | (indent-tabs-mode . nil) 3 | (c-file-style . "bsd") 4 | (c-basic-offset . 2))) 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.{c,h}] 12 | indent_size = 2 13 | 14 | [*.S] 15 | indent_size = 8 16 | 17 | [*.ld] 18 | indent_size = 2 19 | 20 | [Makefile] 21 | indent_style = tab 22 | indent_size = 8 23 | -------------------------------------------------------------------------------- /.gdbinit.tmpl-riscv: -------------------------------------------------------------------------------- 1 | set confirm off 2 | set architecture riscv:rv64 3 | target remote 127.0.0.1:1234 4 | symbol-file kernel/kernel 5 | set disassemble-next-line auto 6 | -------------------------------------------------------------------------------- /.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 | kernelmemfs 14 | mkfs 15 | kernel/kernel 16 | user/usys.S 17 | .gdbinit 18 | myapi.key 19 | *-handin.tar.gz 20 | xv6.out* 21 | .vagrant/ 22 | submissions/ 23 | .DS_Store 24 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The xv6 software is: 2 | 3 | Copyright (c) 2006-2019 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 | 2 | # To compile and run with a lab solution, set the lab name in lab.mk 3 | # (e.g., LB=util). Run make grade to test solution with the lab's 4 | # grade script (e.g., grade-lab-util). 5 | 6 | -include conf/lab.mk 7 | 8 | K=kernel 9 | U=user 10 | 11 | OBJS = \ 12 | $K/entry.o \ 13 | $K/start.o \ 14 | $K/console.o \ 15 | $K/printf.o \ 16 | $K/uart.o \ 17 | $K/kalloc.o \ 18 | $K/spinlock.o \ 19 | $K/string.o \ 20 | $K/main.o \ 21 | $K/vm.o \ 22 | $K/proc.o \ 23 | $K/swtch.o \ 24 | $K/trampoline.o \ 25 | $K/trap.o \ 26 | $K/syscall.o \ 27 | $K/sysproc.o \ 28 | $K/bio.o \ 29 | $K/fs.o \ 30 | $K/log.o \ 31 | $K/sleeplock.o \ 32 | $K/file.o \ 33 | $K/pipe.o \ 34 | $K/exec.o \ 35 | $K/sysfile.o \ 36 | $K/kernelvec.o \ 37 | $K/plic.o \ 38 | $K/virtio_disk.o \ 39 | 40 | ifeq ($(LAB),pgtbl) 41 | OBJS += $K/vmcopyin.o 42 | endif 43 | 44 | # riscv64-unknown-elf- or riscv64-linux-gnu- 45 | # perhaps in /opt/riscv/bin 46 | #TOOLPREFIX = 47 | 48 | # Try to infer the correct TOOLPREFIX if not set 49 | ifndef TOOLPREFIX 50 | TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ 51 | then echo 'riscv64-unknown-elf-'; \ 52 | elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ 53 | then echo 'riscv64-linux-gnu-'; \ 54 | elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ 55 | then echo 'riscv64-unknown-linux-gnu-'; \ 56 | else echo "***" 1>&2; \ 57 | echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \ 58 | echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ 59 | echo "***" 1>&2; exit 1; fi) 60 | endif 61 | 62 | QEMU = qemu-system-riscv64 63 | 64 | CC = $(TOOLPREFIX)gcc 65 | AS = $(TOOLPREFIX)gas 66 | LD = $(TOOLPREFIX)ld 67 | OBJCOPY = $(TOOLPREFIX)objcopy 68 | OBJDUMP = $(TOOLPREFIX)objdump 69 | 70 | CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb 71 | 72 | ifdef LAB 73 | LABUPPER = $(shell echo $(LAB) | tr a-z A-Z) 74 | CFLAGS += -DSOL_$(LABUPPER) 75 | endif 76 | 77 | CFLAGS += -MD 78 | CFLAGS += -mcmodel=medany 79 | CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax 80 | CFLAGS += -I. 81 | CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) 82 | 83 | # Disable PIE when possible (for Ubuntu 16.10 toolchain) 84 | ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) 85 | CFLAGS += -fno-pie -no-pie 86 | endif 87 | ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) 88 | CFLAGS += -fno-pie -nopie 89 | endif 90 | 91 | LDFLAGS = -z max-page-size=4096 92 | 93 | $K/kernel: $(OBJS) $K/kernel.ld $U/initcode 94 | $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) 95 | $(OBJDUMP) -S $K/kernel > $K/kernel.asm 96 | $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym 97 | 98 | $U/initcode: $U/initcode.S 99 | $(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o 100 | $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o 101 | $(OBJCOPY) -S -O binary $U/initcode.out $U/initcode 102 | $(OBJDUMP) -S $U/initcode.o > $U/initcode.asm 103 | 104 | tags: $(OBJS) _init 105 | etags *.S *.c 106 | 107 | ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o 108 | 109 | _%: %.o $(ULIB) 110 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ 111 | $(OBJDUMP) -S $@ > $*.asm 112 | $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym 113 | 114 | $U/usys.S : $U/usys.pl 115 | perl $U/usys.pl > $U/usys.S 116 | 117 | $U/usys.o : $U/usys.S 118 | $(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S 119 | 120 | $U/_forktest: $U/forktest.o $(ULIB) 121 | # forktest has less library code linked in - needs to be small 122 | # in order to be able to max out the proc table. 123 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o 124 | $(OBJDUMP) -S $U/_forktest > $U/forktest.asm 125 | 126 | mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h 127 | gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c 128 | 129 | # Prevent deletion of intermediate files, e.g. cat.o, after first build, so 130 | # that disk image changes after first build are persistent until clean. More 131 | # details: 132 | # http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html 133 | .PRECIOUS: %.o 134 | 135 | UPROGS=\ 136 | $U/_cat\ 137 | $U/_echo\ 138 | $U/_forktest\ 139 | $U/_grep\ 140 | $U/_init\ 141 | $U/_kill\ 142 | $U/_ln\ 143 | $U/_ls\ 144 | $U/_mkdir\ 145 | $U/_rm\ 146 | $U/_sh\ 147 | $U/_stressfs\ 148 | $U/_usertests\ 149 | $U/_grind\ 150 | $U/_wc\ 151 | $U/_zombie\ 152 | $U/_sleep\ 153 | $U/_pingpong\ 154 | $U/_primes\ 155 | $U/_find\ 156 | $U/_xargs\ 157 | 158 | 159 | ifeq ($(LAB),syscall) 160 | UPROGS += \ 161 | $U/_trace\ 162 | $U/_sysinfotest 163 | endif 164 | 165 | ifeq ($(LAB),trap) 166 | UPROGS += \ 167 | $U/_call\ 168 | $U/_alarmtest 169 | endif 170 | 171 | ifeq ($(LAB),lazy) 172 | UPROGS += \ 173 | $U/_lazytests 174 | endif 175 | 176 | ifeq ($(LAB),cow) 177 | UPROGS += \ 178 | $U/_cowtest 179 | endif 180 | 181 | UEXTRA= 182 | ifeq ($(LAB),util) 183 | UEXTRA += user/xargstest.sh 184 | endif 185 | 186 | fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS) 187 | mkfs/mkfs fs.img README $(UEXTRA) $(UPROGS) 188 | 189 | -include kernel/*.d user/*.d 190 | 191 | clean: 192 | rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ 193 | */*.o */*.d */*.asm */*.sym \ 194 | $U/initcode $U/initcode.out $K/kernel fs.img \ 195 | mkfs/mkfs .gdbinit \ 196 | $U/usys.S \ 197 | $(UPROGS) 198 | 199 | # try to generate a unique GDB port 200 | GDBPORT = $(shell expr `id -u` % 5000 + 25000) 201 | # QEMU's gdb stub command line changed in 0.11 202 | QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ 203 | then echo "-gdb tcp::$(GDBPORT)"; \ 204 | else echo "-s -p $(GDBPORT)"; fi) 205 | ifndef CPUS 206 | CPUS := 3 207 | endif 208 | 209 | QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic 210 | QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 211 | QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 212 | 213 | qemu: $K/kernel fs.img 214 | $(QEMU) $(QEMUOPTS) 215 | 216 | .gdbinit: .gdbinit.tmpl-riscv 217 | sed "s/:1234/:$(GDBPORT)/" < $^ > $@ 218 | 219 | qemu-gdb: $K/kernel .gdbinit fs.img 220 | @echo "*** Now run 'gdb' in another window." 1>&2 221 | $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) 222 | 223 | ## 224 | ## FOR testing lab grading script 225 | ## 226 | 227 | ifneq ($(V),@) 228 | GRADEFLAGS += -v 229 | endif 230 | 231 | print-gdbport: 232 | @echo $(GDBPORT) 233 | 234 | grade: 235 | @echo $(MAKE) clean 236 | @$(MAKE) clean || \ 237 | (echo "'make clean' failed. HINT: Do you have another running instance of xv6?" && exit 1) 238 | ./grade-lab-$(LAB) $(GRADEFLAGS) 239 | 240 | ## 241 | ## FOR web handin 242 | ## 243 | 244 | 245 | WEBSUB := https://6828.scripts.mit.edu/2020/handin.py 246 | 247 | handin: tarball-pref myapi.key 248 | @SUF=$(LAB); \ 249 | curl -f -F file=@lab-$$SUF-handin.tar.gz -F key=\ /dev/null || { \ 251 | echo ; \ 252 | echo Submit seems to have failed.; \ 253 | echo Please go to $(WEBSUB)/ and upload the tarball manually.; } 254 | 255 | handin-check: 256 | @if ! test -d .git; then \ 257 | echo No .git directory, is this a git repository?; \ 258 | false; \ 259 | fi 260 | @if test "$$(git symbolic-ref HEAD)" != refs/heads/$(LAB); then \ 261 | git branch; \ 262 | read -p "You are not on the $(LAB) branch. Hand-in the current branch? [y/N] " r; \ 263 | test "$$r" = y; \ 264 | fi 265 | @if ! git diff-files --quiet || ! git diff-index --quiet --cached HEAD; then \ 266 | git status -s; \ 267 | echo; \ 268 | echo "You have uncomitted changes. Please commit or stash them."; \ 269 | false; \ 270 | fi 271 | @if test -n "`git status -s`"; then \ 272 | git status -s; \ 273 | read -p "Untracked files will not be handed in. Continue? [y/N] " r; \ 274 | test "$$r" = y; \ 275 | fi 276 | 277 | UPSTREAM := $(shell git remote -v | grep -m 1 "xv6-labs-2020" | awk '{split($$0,a," "); print a[1]}') 278 | 279 | tarball: handin-check 280 | git archive --format=tar HEAD | gzip > lab-$(LAB)-handin.tar.gz 281 | 282 | tarball-pref: handin-check 283 | @SUF=$(LAB); \ 284 | git archive --format=tar HEAD > lab-$$SUF-handin.tar; \ 285 | git diff $(UPSTREAM)/$(LAB) > /tmp/lab-$$SUF-diff.patch; \ 286 | tar -rf lab-$$SUF-handin.tar /tmp/lab-$$SUF-diff.patch; \ 287 | gzip -c lab-$$SUF-handin.tar > lab-$$SUF-handin.tar.gz; \ 288 | rm lab-$$SUF-handin.tar; \ 289 | rm /tmp/lab-$$SUF-diff.patch; \ 290 | 291 | myapi.key: 292 | @echo Get an API key for yourself by visiting $(WEBSUB)/ 293 | @read -p "Please enter your API key: " k; \ 294 | if test `echo "$$k" |tr -d '\n' |wc -c` = 32 ; then \ 295 | TF=`mktemp -t tmp.XXXXXX`; \ 296 | if test "x$$TF" != "x" ; then \ 297 | echo "$$k" |tr -d '\n' > $$TF; \ 298 | mv -f $$TF $@; \ 299 | else \ 300 | echo mktemp failed; \ 301 | false; \ 302 | fi; \ 303 | else \ 304 | echo Bad API key: $$k; \ 305 | echo An API key should be 32 characters long.; \ 306 | false; \ 307 | fi; 308 | 309 | 310 | .PHONY: handin tarball tarball-pref clean grade handin-check 311 | -------------------------------------------------------------------------------- /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 RISC-V 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 | The following people have made contributions: Russ Cox (context switching, 13 | locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin 14 | Clements. 15 | 16 | We are also grateful for the bug reports and patches contributed by 17 | Silas Boyd-Wickizer, Anton Burtsev, Dan Cross, Cody Cutler, Mike CAT, 18 | Tej Chajed, Asami Doi, eyalz800, , Nelson Elhage, Saar Ettinger, Alice 19 | Ferrazzi, Nathaniel Filardo, Peter Froehlich, Yakir Goaron,Shivam 20 | Handa, Bryan Henry, jaichenhengjie, Jim Huang, Alexander Kapshuk, 21 | Anders Kaseorg, kehao95, Wolfgang Keller, Jonathan Kimmitt, Eddie 22 | Kohler, Austin Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, 23 | Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider, 24 | Greg Price, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya 25 | Shigemitsu, Takahiro, Cam Tenny, tyfkda, Rafael Ubal, Warren Toomey, 26 | Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas 27 | Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang 28 | Wei. 29 | 30 | The code in the files that constitute xv6 is 31 | Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox. 32 | 33 | ERROR REPORTS 34 | 35 | Please send errors and suggestions to Frans Kaashoek and Robert Morris 36 | (kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching 37 | operating system for MIT's 6.S081, so we are more interested in 38 | simplifications and clarifications than new features. 39 | 40 | BUILDING AND RUNNING XV6 41 | 42 | You will need a RISC-V "newlib" tool chain from 43 | https://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for 44 | riscv64-softmmu. Once they are installed, and in your shell 45 | search path, you can run "make qemu". 46 | -------------------------------------------------------------------------------- /book-riscv-rev1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YukunJ/xv6-operating-system/84c7ecbdafa16d5027897f471c98d8db696337fa/book-riscv-rev1.pdf -------------------------------------------------------------------------------- /conf/lab.mk: -------------------------------------------------------------------------------- 1 | LAB=util 2 | -------------------------------------------------------------------------------- /grade-lab-util: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | from gradelib import * 5 | 6 | r = Runner(save("xv6.out")) 7 | 8 | @test(5, "sleep, no arguments") 9 | def test_sleep_no_args(): 10 | r.run_qemu(shell_script([ 11 | 'sleep' 12 | ])) 13 | r.match(no=["exec .* failed", "$ sleep\n$"]) 14 | 15 | @test(5, "sleep, returns") 16 | def test_sleep_no_args(): 17 | r.run_qemu(shell_script([ 18 | 'sleep', 19 | 'echo OK' 20 | ])) 21 | r.match('^OK$', no=["exec .* failed", "$ sleep\n$"]) 22 | 23 | @test(10, "sleep, makes syscall") 24 | def test_sleep(): 25 | r.run_qemu(shell_script([ 26 | 'sleep 10', 27 | 'echo FAIL' 28 | ]), stop_breakpoint('sys_sleep')) 29 | r.match('\\$ sleep 10', no=['FAIL']) 30 | 31 | @test(20, "pingpong") 32 | def test_pingpong(): 33 | r.run_qemu(shell_script([ 34 | 'pingpong', 'echo OK' 35 | ])) 36 | r.match('^\\d+: received ping$', '^\\d+: received pong$', '^OK$') 37 | 38 | @test(20, "primes") 39 | def test_primes(): 40 | r.run_qemu(shell_script([ 41 | 'primes', 'echo OK' 42 | ])) 43 | args = ['prime %d' % i for i in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]] 44 | args.append('^OK$') 45 | r.match(*args) 46 | 47 | @test(10, "find, in current directory") 48 | def test_find_curdir(): 49 | fn = random_str() 50 | r.run_qemu(shell_script([ 51 | 'echo > %s' % fn, 52 | 'find . %s' % fn 53 | ])) 54 | r.match('./%s' % fn) 55 | 56 | @test(10, "find, recursive") 57 | def test_find_recursive(): 58 | needle = random_str() 59 | dirs = [random_str() for _ in range(3)] 60 | r.run_qemu(shell_script([ 61 | 'mkdir %s' % dirs[0], 62 | 'echo > %s/%s' % (dirs[0], needle), 63 | 'mkdir %s/%s' % (dirs[0], dirs[1]), 64 | 'echo > %s/%s/%s' % (dirs[0], dirs[1], needle), 65 | 'mkdir %s' % dirs[2], 66 | 'echo > %s/%s' % (dirs[2], needle), 67 | 'find . %s' % needle 68 | ])) 69 | r.match('./%s/%s' % (dirs[0], needle), 70 | './%s/%s/%s' % (dirs[0], dirs[1], needle), 71 | './%s/%s' % (dirs[2], needle)) 72 | 73 | @test(19, "xargs") 74 | def test_xargs(): 75 | r.run_qemu(shell_script([ 76 | 'sh < xargstest.sh', 77 | 'echo DONE', 78 | ], 'DONE')) 79 | matches = re.findall("hello", r.qemu.output) 80 | assert_equal(len(matches), 3, "Number of appearances of 'hello'") 81 | 82 | @test(1, "time") 83 | def test_time(): 84 | check_time() 85 | 86 | run_tests() 87 | -------------------------------------------------------------------------------- /kernel/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 | 17 | #include "types.h" 18 | #include "param.h" 19 | #include "spinlock.h" 20 | #include "sleeplock.h" 21 | #include "riscv.h" 22 | #include "defs.h" 23 | #include "fs.h" 24 | #include "buf.h" 25 | 26 | struct { 27 | struct spinlock lock; 28 | struct buf buf[NBUF]; 29 | 30 | // Linked list of all buffers, through prev/next. 31 | // Sorted by how recently the buffer was used. 32 | // head.next is most recent, head.prev is least. 33 | struct buf head; 34 | } bcache; 35 | 36 | void 37 | binit(void) 38 | { 39 | struct buf *b; 40 | 41 | initlock(&bcache.lock, "bcache"); 42 | 43 | // Create linked list of buffers 44 | bcache.head.prev = &bcache.head; 45 | bcache.head.next = &bcache.head; 46 | for(b = bcache.buf; b < bcache.buf+NBUF; b++){ 47 | b->next = bcache.head.next; 48 | b->prev = &bcache.head; 49 | initsleeplock(&b->lock, "buffer"); 50 | bcache.head.next->prev = b; 51 | bcache.head.next = b; 52 | } 53 | } 54 | 55 | // Look through buffer cache for block on device dev. 56 | // If not found, allocate a buffer. 57 | // In either case, return locked buffer. 58 | static struct buf* 59 | bget(uint dev, uint blockno) 60 | { 61 | struct buf *b; 62 | 63 | acquire(&bcache.lock); 64 | 65 | // Is the block already cached? 66 | for(b = bcache.head.next; b != &bcache.head; b = b->next){ 67 | if(b->dev == dev && b->blockno == blockno){ 68 | b->refcnt++; 69 | release(&bcache.lock); 70 | acquiresleep(&b->lock); 71 | return b; 72 | } 73 | } 74 | 75 | // Not cached. 76 | // Recycle the least recently used (LRU) unused buffer. 77 | for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ 78 | if(b->refcnt == 0) { 79 | b->dev = dev; 80 | b->blockno = blockno; 81 | b->valid = 0; 82 | b->refcnt = 1; 83 | release(&bcache.lock); 84 | acquiresleep(&b->lock); 85 | return b; 86 | } 87 | } 88 | panic("bget: no buffers"); 89 | } 90 | 91 | // Return a locked buf with the contents of the indicated block. 92 | struct buf* 93 | bread(uint dev, uint blockno) 94 | { 95 | struct buf *b; 96 | 97 | b = bget(dev, blockno); 98 | if(!b->valid) { 99 | virtio_disk_rw(b, 0); 100 | b->valid = 1; 101 | } 102 | return b; 103 | } 104 | 105 | // Write b's contents to disk. Must be locked. 106 | void 107 | bwrite(struct buf *b) 108 | { 109 | if(!holdingsleep(&b->lock)) 110 | panic("bwrite"); 111 | virtio_disk_rw(b, 1); 112 | } 113 | 114 | // Release a locked buffer. 115 | // Move to the head of the most-recently-used list. 116 | void 117 | brelse(struct buf *b) 118 | { 119 | if(!holdingsleep(&b->lock)) 120 | panic("brelse"); 121 | 122 | releasesleep(&b->lock); 123 | 124 | acquire(&bcache.lock); 125 | b->refcnt--; 126 | if (b->refcnt == 0) { 127 | // no one is waiting for it. 128 | b->next->prev = b->prev; 129 | b->prev->next = b->next; 130 | b->next = bcache.head.next; 131 | b->prev = &bcache.head; 132 | bcache.head.next->prev = b; 133 | bcache.head.next = b; 134 | } 135 | 136 | release(&bcache.lock); 137 | } 138 | 139 | void 140 | bpin(struct buf *b) { 141 | acquire(&bcache.lock); 142 | b->refcnt++; 143 | release(&bcache.lock); 144 | } 145 | 146 | void 147 | bunpin(struct buf *b) { 148 | acquire(&bcache.lock); 149 | b->refcnt--; 150 | release(&bcache.lock); 151 | } 152 | 153 | 154 | -------------------------------------------------------------------------------- /kernel/buf.h: -------------------------------------------------------------------------------- 1 | struct buf { 2 | int valid; // has data been read from disk? 3 | int disk; // does disk "own" buf? 4 | uint dev; 5 | uint blockno; 6 | struct sleeplock lock; 7 | uint refcnt; 8 | struct buf *prev; // LRU cache list 9 | struct buf *next; 10 | uchar data[BSIZE]; 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /kernel/console.c: -------------------------------------------------------------------------------- 1 | // 2 | // Console input and output, to the uart. 3 | // Reads are line at a time. 4 | // Implements special input characters: 5 | // newline -- end of line 6 | // control-h -- backspace 7 | // control-u -- kill line 8 | // control-d -- end of file 9 | // control-p -- print process list 10 | // 11 | 12 | #include 13 | 14 | #include "types.h" 15 | #include "param.h" 16 | #include "spinlock.h" 17 | #include "sleeplock.h" 18 | #include "fs.h" 19 | #include "file.h" 20 | #include "memlayout.h" 21 | #include "riscv.h" 22 | #include "defs.h" 23 | #include "proc.h" 24 | 25 | #define BACKSPACE 0x100 26 | #define C(x) ((x)-'@') // Control-x 27 | 28 | // 29 | // send one character to the uart. 30 | // called by printf, and to echo input characters, 31 | // but not from write(). 32 | // 33 | void 34 | consputc(int c) 35 | { 36 | if(c == BACKSPACE){ 37 | // if the user typed backspace, overwrite with a space. 38 | uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); 39 | } else { 40 | uartputc_sync(c); 41 | } 42 | } 43 | 44 | struct { 45 | struct spinlock lock; 46 | 47 | // input 48 | #define INPUT_BUF 128 49 | char buf[INPUT_BUF]; 50 | uint r; // Read index 51 | uint w; // Write index 52 | uint e; // Edit index 53 | } cons; 54 | 55 | // 56 | // user write()s to the console go here. 57 | // 58 | int 59 | consolewrite(int user_src, uint64 src, int n) 60 | { 61 | int i; 62 | 63 | acquire(&cons.lock); 64 | for(i = 0; i < n; i++){ 65 | char c; 66 | if(either_copyin(&c, user_src, src+i, 1) == -1) 67 | break; 68 | uartputc(c); 69 | } 70 | release(&cons.lock); 71 | 72 | return i; 73 | } 74 | 75 | // 76 | // user read()s from the console go here. 77 | // copy (up to) a whole input line to dst. 78 | // user_dist indicates whether dst is a user 79 | // or kernel address. 80 | // 81 | int 82 | consoleread(int user_dst, uint64 dst, int n) 83 | { 84 | uint target; 85 | int c; 86 | char cbuf; 87 | 88 | target = n; 89 | acquire(&cons.lock); 90 | while(n > 0){ 91 | // wait until interrupt handler has put some 92 | // input into cons.buffer. 93 | while(cons.r == cons.w){ 94 | if(myproc()->killed){ 95 | release(&cons.lock); 96 | return -1; 97 | } 98 | sleep(&cons.r, &cons.lock); 99 | } 100 | 101 | c = cons.buf[cons.r++ % INPUT_BUF]; 102 | 103 | if(c == C('D')){ // end-of-file 104 | if(n < target){ 105 | // Save ^D for next time, to make sure 106 | // caller gets a 0-byte result. 107 | cons.r--; 108 | } 109 | break; 110 | } 111 | 112 | // copy the input byte to the user-space buffer. 113 | cbuf = c; 114 | if(either_copyout(user_dst, dst, &cbuf, 1) == -1) 115 | break; 116 | 117 | dst++; 118 | --n; 119 | 120 | if(c == '\n'){ 121 | // a whole line has arrived, return to 122 | // the user-level read(). 123 | break; 124 | } 125 | } 126 | release(&cons.lock); 127 | 128 | return target - n; 129 | } 130 | 131 | // 132 | // the console input interrupt handler. 133 | // uartintr() calls this for input character. 134 | // do erase/kill processing, append to cons.buf, 135 | // wake up consoleread() if a whole line has arrived. 136 | // 137 | void 138 | consoleintr(int c) 139 | { 140 | acquire(&cons.lock); 141 | 142 | switch(c){ 143 | case C('P'): // Print process list. 144 | procdump(); 145 | break; 146 | case C('U'): // Kill line. 147 | while(cons.e != cons.w && 148 | cons.buf[(cons.e-1) % INPUT_BUF] != '\n'){ 149 | cons.e--; 150 | consputc(BACKSPACE); 151 | } 152 | break; 153 | case C('H'): // Backspace 154 | case '\x7f': 155 | if(cons.e != cons.w){ 156 | cons.e--; 157 | consputc(BACKSPACE); 158 | } 159 | break; 160 | default: 161 | if(c != 0 && cons.e-cons.r < INPUT_BUF){ 162 | c = (c == '\r') ? '\n' : c; 163 | 164 | // echo back to the user. 165 | consputc(c); 166 | 167 | // store for consumption by consoleread(). 168 | cons.buf[cons.e++ % INPUT_BUF] = c; 169 | 170 | if(c == '\n' || c == C('D') || cons.e == cons.r+INPUT_BUF){ 171 | // wake up consoleread() if a whole line (or end-of-file) 172 | // has arrived. 173 | cons.w = cons.e; 174 | wakeup(&cons.r); 175 | } 176 | } 177 | break; 178 | } 179 | 180 | release(&cons.lock); 181 | } 182 | 183 | void 184 | consoleinit(void) 185 | { 186 | initlock(&cons.lock, "cons"); 187 | 188 | uartinit(); 189 | 190 | // connect read and write system calls 191 | // to consoleread and consolewrite. 192 | devsw[CONSOLE].read = consoleread; 193 | devsw[CONSOLE].write = consolewrite; 194 | } 195 | -------------------------------------------------------------------------------- /kernel/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 | -------------------------------------------------------------------------------- /kernel/defs.h: -------------------------------------------------------------------------------- 1 | struct buf; 2 | struct context; 3 | struct file; 4 | struct inode; 5 | struct pipe; 6 | struct proc; 7 | struct spinlock; 8 | struct sleeplock; 9 | struct stat; 10 | struct superblock; 11 | 12 | // bio.c 13 | void binit(void); 14 | struct buf* bread(uint, uint); 15 | void brelse(struct buf*); 16 | void bwrite(struct buf*); 17 | void bpin(struct buf*); 18 | void bunpin(struct buf*); 19 | 20 | // console.c 21 | void consoleinit(void); 22 | void consoleintr(int); 23 | void consputc(int); 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*, uint64, int n); 34 | int filestat(struct file*, uint64 addr); 35 | int filewrite(struct file*, uint64, int n); 36 | 37 | // fs.c 38 | void fsinit(int); 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(); 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*, int, uint64, uint, uint); 53 | void stati(struct inode*, struct stat*); 54 | int writei(struct inode*, int, uint64, uint, uint); 55 | void itrunc(struct inode*); 56 | 57 | // ramdisk.c 58 | void ramdiskinit(void); 59 | void ramdiskintr(void); 60 | void ramdiskrw(struct buf*); 61 | 62 | // kalloc.c 63 | void* kalloc(void); 64 | void kfree(void *); 65 | void kinit(void); 66 | 67 | // log.c 68 | void initlog(int, struct superblock*); 69 | void log_write(struct buf*); 70 | void begin_op(void); 71 | void end_op(void); 72 | 73 | // pipe.c 74 | int pipealloc(struct file**, struct file**); 75 | void pipeclose(struct pipe*, int); 76 | int piperead(struct pipe*, uint64, int); 77 | int pipewrite(struct pipe*, uint64, int); 78 | 79 | // printf.c 80 | void printf(char*, ...); 81 | void panic(char*) __attribute__((noreturn)); 82 | void printfinit(void); 83 | 84 | // proc.c 85 | int cpuid(void); 86 | void exit(int); 87 | int fork(void); 88 | int growproc(int); 89 | pagetable_t proc_pagetable(struct proc *); 90 | void proc_freepagetable(pagetable_t, uint64); 91 | int kill(int); 92 | struct cpu* mycpu(void); 93 | struct cpu* getmycpu(void); 94 | struct proc* myproc(); 95 | void procinit(void); 96 | void scheduler(void) __attribute__((noreturn)); 97 | void sched(void); 98 | void setproc(struct proc*); 99 | void sleep(void*, struct spinlock*); 100 | void userinit(void); 101 | int wait(uint64); 102 | void wakeup(void*); 103 | void yield(void); 104 | int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); 105 | int either_copyin(void *dst, int user_src, uint64 src, uint64 len); 106 | void procdump(void); 107 | 108 | // swtch.S 109 | void swtch(struct context*, struct context*); 110 | 111 | // spinlock.c 112 | void acquire(struct spinlock*); 113 | int holding(struct spinlock*); 114 | void initlock(struct spinlock*, char*); 115 | void release(struct spinlock*); 116 | void push_off(void); 117 | void pop_off(void); 118 | 119 | // sleeplock.c 120 | void acquiresleep(struct sleeplock*); 121 | void releasesleep(struct sleeplock*); 122 | int holdingsleep(struct sleeplock*); 123 | void initsleeplock(struct sleeplock*, char*); 124 | 125 | // string.c 126 | int memcmp(const void*, const void*, uint); 127 | void* memmove(void*, const void*, uint); 128 | void* memset(void*, int, uint); 129 | char* safestrcpy(char*, const char*, int); 130 | int strlen(const char*); 131 | int strncmp(const char*, const char*, uint); 132 | char* strncpy(char*, const char*, int); 133 | 134 | // syscall.c 135 | int argint(int, int*); 136 | int argstr(int, char*, int); 137 | int argaddr(int, uint64 *); 138 | int fetchstr(uint64, char*, int); 139 | int fetchaddr(uint64, uint64*); 140 | void syscall(); 141 | 142 | // trap.c 143 | extern uint ticks; 144 | void trapinit(void); 145 | void trapinithart(void); 146 | extern struct spinlock tickslock; 147 | void usertrapret(void); 148 | 149 | // uart.c 150 | void uartinit(void); 151 | void uartintr(void); 152 | void uartputc(int); 153 | void uartputc_sync(int); 154 | int uartgetc(void); 155 | 156 | // vm.c 157 | void kvminit(void); 158 | void kvminithart(void); 159 | uint64 kvmpa(uint64); 160 | void kvmmap(uint64, uint64, uint64, int); 161 | int mappages(pagetable_t, uint64, uint64, uint64, int); 162 | pagetable_t uvmcreate(void); 163 | void uvminit(pagetable_t, uchar *, uint); 164 | uint64 uvmalloc(pagetable_t, uint64, uint64); 165 | uint64 uvmdealloc(pagetable_t, uint64, uint64); 166 | int uvmcopy(pagetable_t, pagetable_t, uint64); 167 | void uvmfree(pagetable_t, uint64); 168 | void uvmunmap(pagetable_t, uint64, uint64, int); 169 | void uvmclear(pagetable_t, uint64); 170 | uint64 walkaddr(pagetable_t, uint64); 171 | int copyout(pagetable_t, uint64, char *, uint64); 172 | int copyin(pagetable_t, char *, uint64, uint64); 173 | int copyinstr(pagetable_t, char *, uint64, uint64); 174 | 175 | // plic.c 176 | void plicinit(void); 177 | void plicinithart(void); 178 | int plic_claim(void); 179 | void plic_complete(int); 180 | 181 | // virtio_disk.c 182 | void virtio_disk_init(void); 183 | void virtio_disk_rw(struct buf *, int); 184 | void virtio_disk_intr(void); 185 | 186 | // number of elements in fixed-size array 187 | #define NELEM(x) (sizeof(x)/sizeof((x)[0])) 188 | -------------------------------------------------------------------------------- /kernel/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 | uint64 entry; 13 | uint64 phoff; 14 | uint64 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 | uint32 type; 27 | uint32 flags; 28 | uint64 off; 29 | uint64 vaddr; 30 | uint64 paddr; 31 | uint64 filesz; 32 | uint64 memsz; 33 | uint64 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 | -------------------------------------------------------------------------------- /kernel/entry.S: -------------------------------------------------------------------------------- 1 | # qemu -kernel loads the kernel at 0x80000000 2 | # and causes each CPU to jump there. 3 | # kernel.ld causes the following code to 4 | # be placed at 0x80000000. 5 | .section .text 6 | _entry: 7 | # set up a stack for C. 8 | # stack0 is declared in start.c, 9 | # with a 4096-byte stack per CPU. 10 | # sp = stack0 + (hartid * 4096) 11 | la sp, stack0 12 | li a0, 1024*4 13 | csrr a1, mhartid 14 | addi a1, a1, 1 15 | mul a0, a0, a1 16 | add sp, sp, a0 17 | # jump to start() in start.c 18 | call start 19 | spin: 20 | j spin 21 | -------------------------------------------------------------------------------- /kernel/exec.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "spinlock.h" 6 | #include "proc.h" 7 | #include "defs.h" 8 | #include "elf.h" 9 | 10 | static int loadseg(pde_t *pgdir, uint64 addr, struct inode *ip, uint offset, uint sz); 11 | 12 | int 13 | exec(char *path, char **argv) 14 | { 15 | char *s, *last; 16 | int i, off; 17 | uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase; 18 | struct elfhdr elf; 19 | struct inode *ip; 20 | struct proghdr ph; 21 | pagetable_t pagetable = 0, oldpagetable; 22 | struct proc *p = myproc(); 23 | 24 | begin_op(); 25 | 26 | if((ip = namei(path)) == 0){ 27 | end_op(); 28 | return -1; 29 | } 30 | ilock(ip); 31 | 32 | // Check ELF header 33 | if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) 34 | goto bad; 35 | if(elf.magic != ELF_MAGIC) 36 | goto bad; 37 | 38 | if((pagetable = proc_pagetable(p)) == 0) 39 | goto bad; 40 | 41 | // Load program into memory. 42 | for(i=0, off=elf.phoff; isz; 66 | 67 | // Allocate two pages at the next page boundary. 68 | // Use the second as the user stack. 69 | sz = PGROUNDUP(sz); 70 | uint64 sz1; 71 | if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0) 72 | goto bad; 73 | sz = sz1; 74 | uvmclear(pagetable, sz-2*PGSIZE); 75 | sp = sz; 76 | stackbase = sp - PGSIZE; 77 | 78 | // Push argument strings, prepare rest of stack in ustack. 79 | for(argc = 0; argv[argc]; argc++) { 80 | if(argc >= MAXARG) 81 | goto bad; 82 | sp -= strlen(argv[argc]) + 1; 83 | sp -= sp % 16; // riscv sp must be 16-byte aligned 84 | if(sp < stackbase) 85 | goto bad; 86 | if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) 87 | goto bad; 88 | ustack[argc] = sp; 89 | } 90 | ustack[argc] = 0; 91 | 92 | // push the array of argv[] pointers. 93 | sp -= (argc+1) * sizeof(uint64); 94 | sp -= sp % 16; 95 | if(sp < stackbase) 96 | goto bad; 97 | if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0) 98 | goto bad; 99 | 100 | // arguments to user main(argc, argv) 101 | // argc is returned via the system call return 102 | // value, which goes in a0. 103 | p->trapframe->a1 = sp; 104 | 105 | // Save program name for debugging. 106 | for(last=s=path; *s; s++) 107 | if(*s == '/') 108 | last = s+1; 109 | safestrcpy(p->name, last, sizeof(p->name)); 110 | 111 | // Commit to the user image. 112 | oldpagetable = p->pagetable; 113 | p->pagetable = pagetable; 114 | p->sz = sz; 115 | p->trapframe->epc = elf.entry; // initial program counter = main 116 | p->trapframe->sp = sp; // initial stack pointer 117 | proc_freepagetable(oldpagetable, oldsz); 118 | 119 | return argc; // this ends up in a0, the first argument to main(argc, argv) 120 | 121 | bad: 122 | if(pagetable) 123 | proc_freepagetable(pagetable, sz); 124 | if(ip){ 125 | iunlockput(ip); 126 | end_op(); 127 | } 128 | return -1; 129 | } 130 | 131 | // Load a program segment into pagetable at virtual address va. 132 | // va must be page-aligned 133 | // and the pages from va to va+sz must already be mapped. 134 | // Returns 0 on success, -1 on failure. 135 | static int 136 | loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz) 137 | { 138 | uint i, n; 139 | uint64 pa; 140 | 141 | if((va % PGSIZE) != 0) 142 | panic("loadseg: va must be page aligned"); 143 | 144 | for(i = 0; i < sz; i += PGSIZE){ 145 | pa = walkaddr(pagetable, va + i); 146 | if(pa == 0) 147 | panic("loadseg: address should exist"); 148 | if(sz - i < PGSIZE) 149 | n = sz - i; 150 | else 151 | n = PGSIZE; 152 | if(readi(ip, 0, (uint64)pa, offset+i, n) != n) 153 | return -1; 154 | } 155 | 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /kernel/fcntl.h: -------------------------------------------------------------------------------- 1 | #define O_RDONLY 0x000 2 | #define O_WRONLY 0x001 3 | #define O_RDWR 0x002 4 | #define O_CREATE 0x200 5 | #define O_TRUNC 0x400 6 | -------------------------------------------------------------------------------- /kernel/file.c: -------------------------------------------------------------------------------- 1 | // 2 | // Support functions for system calls that involve file descriptors. 3 | // 4 | 5 | #include "types.h" 6 | #include "riscv.h" 7 | #include "defs.h" 8 | #include "param.h" 9 | #include "fs.h" 10 | #include "spinlock.h" 11 | #include "sleeplock.h" 12 | #include "file.h" 13 | #include "stat.h" 14 | #include "proc.h" 15 | 16 | struct devsw devsw[NDEV]; 17 | struct { 18 | struct spinlock lock; 19 | struct file file[NFILE]; 20 | } ftable; 21 | 22 | void 23 | fileinit(void) 24 | { 25 | initlock(&ftable.lock, "ftable"); 26 | } 27 | 28 | // Allocate a file structure. 29 | struct file* 30 | filealloc(void) 31 | { 32 | struct file *f; 33 | 34 | acquire(&ftable.lock); 35 | for(f = ftable.file; f < ftable.file + NFILE; f++){ 36 | if(f->ref == 0){ 37 | f->ref = 1; 38 | release(&ftable.lock); 39 | return f; 40 | } 41 | } 42 | release(&ftable.lock); 43 | return 0; 44 | } 45 | 46 | // Increment ref count for file f. 47 | struct file* 48 | filedup(struct file *f) 49 | { 50 | acquire(&ftable.lock); 51 | if(f->ref < 1) 52 | panic("filedup"); 53 | f->ref++; 54 | release(&ftable.lock); 55 | return f; 56 | } 57 | 58 | // Close file f. (Decrement ref count, close when reaches 0.) 59 | void 60 | fileclose(struct file *f) 61 | { 62 | struct file ff; 63 | 64 | acquire(&ftable.lock); 65 | if(f->ref < 1) 66 | panic("fileclose"); 67 | if(--f->ref > 0){ 68 | release(&ftable.lock); 69 | return; 70 | } 71 | ff = *f; 72 | f->ref = 0; 73 | f->type = FD_NONE; 74 | release(&ftable.lock); 75 | 76 | if(ff.type == FD_PIPE){ 77 | pipeclose(ff.pipe, ff.writable); 78 | } else if(ff.type == FD_INODE || ff.type == FD_DEVICE){ 79 | begin_op(); 80 | iput(ff.ip); 81 | end_op(); 82 | } 83 | } 84 | 85 | // Get metadata about file f. 86 | // addr is a user virtual address, pointing to a struct stat. 87 | int 88 | filestat(struct file *f, uint64 addr) 89 | { 90 | struct proc *p = myproc(); 91 | struct stat st; 92 | 93 | if(f->type == FD_INODE || f->type == FD_DEVICE){ 94 | ilock(f->ip); 95 | stati(f->ip, &st); 96 | iunlock(f->ip); 97 | if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) 98 | return -1; 99 | return 0; 100 | } 101 | return -1; 102 | } 103 | 104 | // Read from file f. 105 | // addr is a user virtual address. 106 | int 107 | fileread(struct file *f, uint64 addr, int n) 108 | { 109 | int r = 0; 110 | 111 | if(f->readable == 0) 112 | return -1; 113 | 114 | if(f->type == FD_PIPE){ 115 | r = piperead(f->pipe, addr, n); 116 | } else if(f->type == FD_DEVICE){ 117 | if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read) 118 | return -1; 119 | r = devsw[f->major].read(1, addr, n); 120 | } else if(f->type == FD_INODE){ 121 | ilock(f->ip); 122 | if((r = readi(f->ip, 1, addr, f->off, n)) > 0) 123 | f->off += r; 124 | iunlock(f->ip); 125 | } else { 126 | panic("fileread"); 127 | } 128 | 129 | return r; 130 | } 131 | 132 | // Write to file f. 133 | // addr is a user virtual address. 134 | int 135 | filewrite(struct file *f, uint64 addr, int n) 136 | { 137 | int r, ret = 0; 138 | 139 | if(f->writable == 0) 140 | return -1; 141 | 142 | if(f->type == FD_PIPE){ 143 | ret = pipewrite(f->pipe, addr, n); 144 | } else if(f->type == FD_DEVICE){ 145 | if(f->major < 0 || f->major >= NDEV || !devsw[f->major].write) 146 | return -1; 147 | ret = devsw[f->major].write(1, addr, n); 148 | } else if(f->type == FD_INODE){ 149 | // write a few blocks at a time to avoid exceeding 150 | // the maximum log transaction size, including 151 | // i-node, indirect block, allocation blocks, 152 | // and 2 blocks of slop for non-aligned writes. 153 | // this really belongs lower down, since writei() 154 | // might be writing a device like the console. 155 | int max = ((MAXOPBLOCKS-1-1-2) / 2) * BSIZE; 156 | int i = 0; 157 | while(i < n){ 158 | int n1 = n - i; 159 | if(n1 > max) 160 | n1 = max; 161 | 162 | begin_op(); 163 | ilock(f->ip); 164 | if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0) 165 | f->off += r; 166 | iunlock(f->ip); 167 | end_op(); 168 | 169 | if(r < 0) 170 | break; 171 | if(r != n1) 172 | panic("short filewrite"); 173 | i += r; 174 | } 175 | ret = (i == n ? n : -1); 176 | } else { 177 | panic("filewrite"); 178 | } 179 | 180 | return ret; 181 | } 182 | 183 | -------------------------------------------------------------------------------- /kernel/file.h: -------------------------------------------------------------------------------- 1 | struct file { 2 | enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type; 3 | int ref; // reference count 4 | char readable; 5 | char writable; 6 | struct pipe *pipe; // FD_PIPE 7 | struct inode *ip; // FD_INODE and FD_DEVICE 8 | uint off; // FD_INODE 9 | short major; // FD_DEVICE 10 | }; 11 | 12 | #define major(dev) ((dev) >> 16 & 0xFFFF) 13 | #define minor(dev) ((dev) & 0xFFFF) 14 | #define mkdev(m,n) ((uint)((m)<<16| (n))) 15 | 16 | // in-memory copy of an inode 17 | struct inode { 18 | uint dev; // Device number 19 | uint inum; // Inode number 20 | int ref; // Reference count 21 | struct sleeplock lock; // protects everything below here 22 | int valid; // inode has been read from disk? 23 | 24 | short type; // copy of disk inode 25 | short major; 26 | short minor; 27 | short nlink; 28 | uint size; 29 | uint addrs[NDIRECT+1]; 30 | }; 31 | 32 | // map major device number to device functions. 33 | struct devsw { 34 | int (*read)(int, uint64, int); 35 | int (*write)(int, uint64, int); 36 | }; 37 | 38 | extern struct devsw devsw[]; 39 | 40 | #define CONSOLE 1 41 | -------------------------------------------------------------------------------- /kernel/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 1024 // 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 magic; // Must be FSMAGIC 16 | uint size; // Size of file system image (blocks) 17 | uint nblocks; // Number of data blocks 18 | uint ninodes; // Number of inodes. 19 | uint nlog; // Number of log blocks 20 | uint logstart; // Block number of first log block 21 | uint inodestart; // Block number of first inode block 22 | uint bmapstart; // Block number of first free map block 23 | }; 24 | 25 | #define FSMAGIC 0x10203040 26 | 27 | #define NDIRECT 12 28 | #define NINDIRECT (BSIZE / sizeof(uint)) 29 | #define MAXFILE (NDIRECT + NINDIRECT) 30 | 31 | // On-disk inode structure 32 | struct dinode { 33 | short type; // File type 34 | short major; // Major device number (T_DEVICE only) 35 | short minor; // Minor device number (T_DEVICE only) 36 | short nlink; // Number of links to inode in file system 37 | uint size; // Size of file (bytes) 38 | uint addrs[NDIRECT+1]; // Data block addresses 39 | }; 40 | 41 | // Inodes per block. 42 | #define IPB (BSIZE / sizeof(struct dinode)) 43 | 44 | // Block containing inode i 45 | #define IBLOCK(i, sb) ((i) / IPB + sb.inodestart) 46 | 47 | // Bitmap bits per block 48 | #define BPB (BSIZE*8) 49 | 50 | // Block of free map containing bit for block b 51 | #define BBLOCK(b, sb) ((b)/BPB + sb.bmapstart) 52 | 53 | // Directory is a file containing a sequence of dirent structures. 54 | #define DIRSIZ 14 55 | 56 | struct dirent { 57 | ushort inum; 58 | char name[DIRSIZ]; 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /kernel/kalloc.c: -------------------------------------------------------------------------------- 1 | // Physical memory allocator, for user processes, 2 | // kernel stacks, page-table pages, 3 | // and pipe buffers. Allocates whole 4096-byte pages. 4 | 5 | #include "types.h" 6 | #include "param.h" 7 | #include "memlayout.h" 8 | #include "spinlock.h" 9 | #include "riscv.h" 10 | #include "defs.h" 11 | 12 | void freerange(void *pa_start, void *pa_end); 13 | 14 | extern char end[]; // first address after kernel. 15 | // defined by kernel.ld. 16 | 17 | struct run { 18 | struct run *next; 19 | }; 20 | 21 | struct { 22 | struct spinlock lock; 23 | struct run *freelist; 24 | } kmem; 25 | 26 | void 27 | kinit() 28 | { 29 | initlock(&kmem.lock, "kmem"); 30 | freerange(end, (void*)PHYSTOP); 31 | } 32 | 33 | void 34 | freerange(void *pa_start, void *pa_end) 35 | { 36 | char *p; 37 | p = (char*)PGROUNDUP((uint64)pa_start); 38 | for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) 39 | kfree(p); 40 | } 41 | 42 | // Free the page of physical memory pointed at by v, 43 | // which normally should have been returned by a 44 | // call to kalloc(). (The exception is when 45 | // initializing the allocator; see kinit above.) 46 | void 47 | kfree(void *pa) 48 | { 49 | struct run *r; 50 | 51 | if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) 52 | panic("kfree"); 53 | 54 | // Fill with junk to catch dangling refs. 55 | memset(pa, 1, PGSIZE); 56 | 57 | r = (struct run*)pa; 58 | 59 | acquire(&kmem.lock); 60 | r->next = kmem.freelist; 61 | kmem.freelist = r; 62 | release(&kmem.lock); 63 | } 64 | 65 | // Allocate one 4096-byte page of physical memory. 66 | // Returns a pointer that the kernel can use. 67 | // Returns 0 if the memory cannot be allocated. 68 | void * 69 | kalloc(void) 70 | { 71 | struct run *r; 72 | 73 | acquire(&kmem.lock); 74 | r = kmem.freelist; 75 | if(r) 76 | kmem.freelist = r->next; 77 | release(&kmem.lock); 78 | 79 | if(r) 80 | memset((char*)r, 5, PGSIZE); // fill with junk 81 | return (void*)r; 82 | } 83 | -------------------------------------------------------------------------------- /kernel/kernel.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | ENTRY( _entry ) 3 | 4 | SECTIONS 5 | { 6 | /* 7 | * ensure that entry.S / _entry is at 0x80000000, 8 | * where qemu's -kernel jumps. 9 | */ 10 | . = 0x80000000; 11 | 12 | .text : { 13 | *(.text .text.*) 14 | . = ALIGN(0x1000); 15 | _trampoline = .; 16 | *(trampsec) 17 | . = ALIGN(0x1000); 18 | ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page"); 19 | PROVIDE(etext = .); 20 | } 21 | 22 | .rodata : { 23 | . = ALIGN(16); 24 | *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ 25 | . = ALIGN(16); 26 | *(.rodata .rodata.*) 27 | } 28 | 29 | .data : { 30 | . = ALIGN(16); 31 | *(.sdata .sdata.*) /* do not need to distinguish this from .data */ 32 | . = ALIGN(16); 33 | *(.data .data.*) 34 | } 35 | 36 | .bss : { 37 | . = ALIGN(16); 38 | *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ 39 | . = ALIGN(16); 40 | *(.bss .bss.*) 41 | } 42 | 43 | PROVIDE(end = .); 44 | } 45 | -------------------------------------------------------------------------------- /kernel/kernelvec.S: -------------------------------------------------------------------------------- 1 | # 2 | # interrupts and exceptions while in supervisor 3 | # mode come here. 4 | # 5 | # push all registers, call kerneltrap(), restore, return. 6 | # 7 | .globl kerneltrap 8 | .globl kernelvec 9 | .align 4 10 | kernelvec: 11 | // make room to save registers. 12 | addi sp, sp, -256 13 | 14 | // save the registers. 15 | sd ra, 0(sp) 16 | sd sp, 8(sp) 17 | sd gp, 16(sp) 18 | sd tp, 24(sp) 19 | sd t0, 32(sp) 20 | sd t1, 40(sp) 21 | sd t2, 48(sp) 22 | sd s0, 56(sp) 23 | sd s1, 64(sp) 24 | sd a0, 72(sp) 25 | sd a1, 80(sp) 26 | sd a2, 88(sp) 27 | sd a3, 96(sp) 28 | sd a4, 104(sp) 29 | sd a5, 112(sp) 30 | sd a6, 120(sp) 31 | sd a7, 128(sp) 32 | sd s2, 136(sp) 33 | sd s3, 144(sp) 34 | sd s4, 152(sp) 35 | sd s5, 160(sp) 36 | sd s6, 168(sp) 37 | sd s7, 176(sp) 38 | sd s8, 184(sp) 39 | sd s9, 192(sp) 40 | sd s10, 200(sp) 41 | sd s11, 208(sp) 42 | sd t3, 216(sp) 43 | sd t4, 224(sp) 44 | sd t5, 232(sp) 45 | sd t6, 240(sp) 46 | 47 | // call the C trap handler in trap.c 48 | call kerneltrap 49 | 50 | // restore registers. 51 | ld ra, 0(sp) 52 | ld sp, 8(sp) 53 | ld gp, 16(sp) 54 | // not this, in case we moved CPUs: ld tp, 24(sp) 55 | ld t0, 32(sp) 56 | ld t1, 40(sp) 57 | ld t2, 48(sp) 58 | ld s0, 56(sp) 59 | ld s1, 64(sp) 60 | ld a0, 72(sp) 61 | ld a1, 80(sp) 62 | ld a2, 88(sp) 63 | ld a3, 96(sp) 64 | ld a4, 104(sp) 65 | ld a5, 112(sp) 66 | ld a6, 120(sp) 67 | ld a7, 128(sp) 68 | ld s2, 136(sp) 69 | ld s3, 144(sp) 70 | ld s4, 152(sp) 71 | ld s5, 160(sp) 72 | ld s6, 168(sp) 73 | ld s7, 176(sp) 74 | ld s8, 184(sp) 75 | ld s9, 192(sp) 76 | ld s10, 200(sp) 77 | ld s11, 208(sp) 78 | ld t3, 216(sp) 79 | ld t4, 224(sp) 80 | ld t5, 232(sp) 81 | ld t6, 240(sp) 82 | 83 | addi sp, sp, 256 84 | 85 | // return to whatever we were doing in the kernel. 86 | sret 87 | 88 | # 89 | # machine-mode timer interrupt. 90 | # 91 | .globl timervec 92 | .align 4 93 | timervec: 94 | # start.c has set up the memory that mscratch points to: 95 | # scratch[0,8,16] : register save area. 96 | # scratch[32] : address of CLINT's MTIMECMP register. 97 | # scratch[40] : desired interval between interrupts. 98 | 99 | csrrw a0, mscratch, a0 100 | sd a1, 0(a0) 101 | sd a2, 8(a0) 102 | sd a3, 16(a0) 103 | 104 | # schedule the next timer interrupt 105 | # by adding interval to mtimecmp. 106 | ld a1, 32(a0) # CLINT_MTIMECMP(hart) 107 | ld a2, 40(a0) # interval 108 | ld a3, 0(a1) 109 | add a3, a3, a2 110 | sd a3, 0(a1) 111 | 112 | # raise a supervisor software interrupt. 113 | li a1, 2 114 | csrw sip, a1 115 | 116 | ld a3, 16(a0) 117 | ld a2, 8(a0) 118 | ld a1, 0(a0) 119 | csrrw a0, mscratch, a0 120 | 121 | mret 122 | -------------------------------------------------------------------------------- /kernel/log.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "riscv.h" 3 | #include "defs.h" 4 | #include "param.h" 5 | #include "spinlock.h" 6 | #include "sleeplock.h" 7 | #include "fs.h" 8 | #include "buf.h" 9 | 10 | // Simple logging that allows concurrent FS system calls. 11 | // 12 | // A log transaction contains the updates of multiple FS system 13 | // calls. The logging system only commits when there are 14 | // no FS system calls active. Thus there is never 15 | // any reasoning required about whether a commit might 16 | // write an uncommitted system call's updates to disk. 17 | // 18 | // A system call should call begin_op()/end_op() to mark 19 | // its start and end. Usually begin_op() just increments 20 | // the count of in-progress FS system calls and returns. 21 | // But if it thinks the log is close to running out, it 22 | // sleeps until the last outstanding end_op() commits. 23 | // 24 | // The log is a physical re-do log containing disk blocks. 25 | // The on-disk log format: 26 | // header block, containing block #s for block A, B, C, ... 27 | // block A 28 | // block B 29 | // block C 30 | // ... 31 | // Log appends are synchronous. 32 | 33 | // Contents of the header block, used for both the on-disk header block 34 | // and to keep track in memory of logged block# before commit. 35 | struct logheader { 36 | int n; 37 | int block[LOGSIZE]; 38 | }; 39 | 40 | struct log { 41 | struct spinlock lock; 42 | int start; 43 | int size; 44 | int outstanding; // how many FS sys calls are executing. 45 | int committing; // in commit(), please wait. 46 | int dev; 47 | struct logheader lh; 48 | }; 49 | struct log log; 50 | 51 | static void recover_from_log(void); 52 | static void commit(); 53 | 54 | void 55 | initlog(int dev, struct superblock *sb) 56 | { 57 | if (sizeof(struct logheader) >= BSIZE) 58 | panic("initlog: too big logheader"); 59 | 60 | initlock(&log.lock, "log"); 61 | log.start = sb->logstart; 62 | log.size = sb->nlog; 63 | log.dev = dev; 64 | recover_from_log(); 65 | } 66 | 67 | // Copy committed blocks from log to their home location 68 | static void 69 | install_trans(void) 70 | { 71 | int tail; 72 | 73 | for (tail = 0; tail < log.lh.n; tail++) { 74 | struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block 75 | struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst 76 | memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst 77 | bwrite(dbuf); // write dst to disk 78 | bunpin(dbuf); 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 by increasing refcnt. 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) { // Add new block to log? 230 | bpin(b); 231 | log.lh.n++; 232 | } 233 | release(&log.lock); 234 | } 235 | 236 | -------------------------------------------------------------------------------- /kernel/main.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "defs.h" 6 | 7 | volatile static int started = 0; 8 | 9 | // start() jumps here in supervisor mode on all CPUs. 10 | void 11 | main() 12 | { 13 | if(cpuid() == 0){ 14 | consoleinit(); 15 | printfinit(); 16 | printf("\n"); 17 | printf("xv6 kernel is booting\n"); 18 | printf("\n"); 19 | kinit(); // physical page allocator 20 | kvminit(); // create kernel page table 21 | kvminithart(); // turn on paging 22 | procinit(); // process table 23 | trapinit(); // trap vectors 24 | trapinithart(); // install kernel trap vector 25 | plicinit(); // set up interrupt controller 26 | plicinithart(); // ask PLIC for device interrupts 27 | binit(); // buffer cache 28 | iinit(); // inode cache 29 | fileinit(); // file table 30 | virtio_disk_init(); // emulated hard disk 31 | userinit(); // first user process 32 | __sync_synchronize(); 33 | started = 1; 34 | } else { 35 | while(started == 0) 36 | ; 37 | __sync_synchronize(); 38 | printf("hart %d starting\n", cpuid()); 39 | kvminithart(); // turn on paging 40 | trapinithart(); // install kernel trap vector 41 | plicinithart(); // ask PLIC for device interrupts 42 | } 43 | 44 | scheduler(); 45 | } 46 | -------------------------------------------------------------------------------- /kernel/memlayout.h: -------------------------------------------------------------------------------- 1 | // Physical memory layout 2 | 3 | // qemu -machine virt is set up like this, 4 | // based on qemu's hw/riscv/virt.c: 5 | // 6 | // 00001000 -- boot ROM, provided by qemu 7 | // 02000000 -- CLINT 8 | // 0C000000 -- PLIC 9 | // 10000000 -- uart0 10 | // 10001000 -- virtio disk 11 | // 80000000 -- boot ROM jumps here in machine mode 12 | // -kernel loads the kernel here 13 | // unused RAM after 80000000. 14 | 15 | // the kernel uses physical memory thus: 16 | // 80000000 -- entry.S, then kernel text and data 17 | // end -- start of kernel page allocation area 18 | // PHYSTOP -- end RAM used by the kernel 19 | 20 | // qemu puts UART registers here in physical memory. 21 | #define UART0 0x10000000L 22 | #define UART0_IRQ 10 23 | 24 | // virtio mmio interface 25 | #define VIRTIO0 0x10001000 26 | #define VIRTIO0_IRQ 1 27 | 28 | // local interrupt controller, which contains the timer. 29 | #define CLINT 0x2000000L 30 | #define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) 31 | #define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. 32 | 33 | // qemu puts programmable interrupt controller here. 34 | #define PLIC 0x0c000000L 35 | #define PLIC_PRIORITY (PLIC + 0x0) 36 | #define PLIC_PENDING (PLIC + 0x1000) 37 | #define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) 38 | #define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) 39 | #define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) 40 | #define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) 41 | #define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) 42 | #define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) 43 | 44 | // the kernel expects there to be RAM 45 | // for use by the kernel and user pages 46 | // from physical address 0x80000000 to PHYSTOP. 47 | #define KERNBASE 0x80000000L 48 | #define PHYSTOP (KERNBASE + 128*1024*1024) 49 | 50 | // map the trampoline page to the highest address, 51 | // in both user and kernel space. 52 | #define TRAMPOLINE (MAXVA - PGSIZE) 53 | 54 | // map kernel stacks beneath the trampoline, 55 | // each surrounded by invalid guard pages. 56 | #define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE) 57 | 58 | // User memory layout. 59 | // Address zero first: 60 | // text 61 | // original data and bss 62 | // fixed-size stack 63 | // expandable heap 64 | // ... 65 | // TRAPFRAME (p->trapframe, used by the trampoline) 66 | // TRAMPOLINE (the same page as in the kernel) 67 | #define TRAPFRAME (TRAMPOLINE - PGSIZE) 68 | -------------------------------------------------------------------------------- /kernel/param.h: -------------------------------------------------------------------------------- 1 | #define NPROC 64 // maximum number of processes 2 | #define NCPU 8 // maximum number of CPUs 3 | #define NOFILE 16 // open files per process 4 | #define NFILE 100 // open files per system 5 | #define NINODE 50 // maximum number of active i-nodes 6 | #define NDEV 10 // maximum major device number 7 | #define ROOTDEV 1 // device number of file system root disk 8 | #define MAXARG 32 // max exec arguments 9 | #define MAXOPBLOCKS 10 // max # of blocks any FS op writes 10 | #define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log 11 | #define NBUF (MAXOPBLOCKS*3) // size of disk block cache 12 | #define FSSIZE 1000 // size of file system in blocks 13 | #define MAXPATH 128 // maximum file path name 14 | -------------------------------------------------------------------------------- /kernel/pipe.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "riscv.h" 3 | #include "defs.h" 4 | #include "param.h" 5 | #include "spinlock.h" 6 | #include "proc.h" 7 | #include "fs.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 *pi; 26 | 27 | pi = 0; 28 | *f0 = *f1 = 0; 29 | if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) 30 | goto bad; 31 | if((pi = (struct pipe*)kalloc()) == 0) 32 | goto bad; 33 | pi->readopen = 1; 34 | pi->writeopen = 1; 35 | pi->nwrite = 0; 36 | pi->nread = 0; 37 | initlock(&pi->lock, "pipe"); 38 | (*f0)->type = FD_PIPE; 39 | (*f0)->readable = 1; 40 | (*f0)->writable = 0; 41 | (*f0)->pipe = pi; 42 | (*f1)->type = FD_PIPE; 43 | (*f1)->readable = 0; 44 | (*f1)->writable = 1; 45 | (*f1)->pipe = pi; 46 | return 0; 47 | 48 | bad: 49 | if(pi) 50 | kfree((char*)pi); 51 | if(*f0) 52 | fileclose(*f0); 53 | if(*f1) 54 | fileclose(*f1); 55 | return -1; 56 | } 57 | 58 | void 59 | pipeclose(struct pipe *pi, int writable) 60 | { 61 | acquire(&pi->lock); 62 | if(writable){ 63 | pi->writeopen = 0; 64 | wakeup(&pi->nread); 65 | } else { 66 | pi->readopen = 0; 67 | wakeup(&pi->nwrite); 68 | } 69 | if(pi->readopen == 0 && pi->writeopen == 0){ 70 | release(&pi->lock); 71 | kfree((char*)pi); 72 | } else 73 | release(&pi->lock); 74 | } 75 | 76 | int 77 | pipewrite(struct pipe *pi, uint64 addr, int n) 78 | { 79 | int i; 80 | char ch; 81 | struct proc *pr = myproc(); 82 | 83 | acquire(&pi->lock); 84 | for(i = 0; i < n; i++){ 85 | while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full 86 | if(pi->readopen == 0 || pr->killed){ 87 | release(&pi->lock); 88 | return -1; 89 | } 90 | wakeup(&pi->nread); 91 | sleep(&pi->nwrite, &pi->lock); 92 | } 93 | if(copyin(pr->pagetable, &ch, addr + i, 1) == -1) 94 | break; 95 | pi->data[pi->nwrite++ % PIPESIZE] = ch; 96 | } 97 | wakeup(&pi->nread); 98 | release(&pi->lock); 99 | return i; 100 | } 101 | 102 | int 103 | piperead(struct pipe *pi, uint64 addr, int n) 104 | { 105 | int i; 106 | struct proc *pr = myproc(); 107 | char ch; 108 | 109 | acquire(&pi->lock); 110 | while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty 111 | if(pr->killed){ 112 | release(&pi->lock); 113 | return -1; 114 | } 115 | sleep(&pi->nread, &pi->lock); //DOC: piperead-sleep 116 | } 117 | for(i = 0; i < n; i++){ //DOC: piperead-copy 118 | if(pi->nread == pi->nwrite) 119 | break; 120 | ch = pi->data[pi->nread++ % PIPESIZE]; 121 | if(copyout(pr->pagetable, addr + i, &ch, 1) == -1) 122 | break; 123 | } 124 | wakeup(&pi->nwrite); //DOC: piperead-wakeup 125 | release(&pi->lock); 126 | return i; 127 | } 128 | -------------------------------------------------------------------------------- /kernel/plic.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "defs.h" 6 | 7 | // 8 | // the riscv Platform Level Interrupt Controller (PLIC). 9 | // 10 | 11 | void 12 | plicinit(void) 13 | { 14 | // set desired IRQ priorities non-zero (otherwise disabled). 15 | *(uint32*)(PLIC + UART0_IRQ*4) = 1; 16 | *(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1; 17 | } 18 | 19 | void 20 | plicinithart(void) 21 | { 22 | int hart = cpuid(); 23 | 24 | // set uart's enable bit for this hart's S-mode. 25 | *(uint32*)PLIC_SENABLE(hart)= (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); 26 | 27 | // set this hart's S-mode priority threshold to 0. 28 | *(uint32*)PLIC_SPRIORITY(hart) = 0; 29 | } 30 | 31 | // ask the PLIC what interrupt we should serve. 32 | int 33 | plic_claim(void) 34 | { 35 | int hart = cpuid(); 36 | int irq = *(uint32*)PLIC_SCLAIM(hart); 37 | return irq; 38 | } 39 | 40 | // tell the PLIC we've served this IRQ. 41 | void 42 | plic_complete(int irq) 43 | { 44 | int hart = cpuid(); 45 | *(uint32*)PLIC_SCLAIM(hart) = irq; 46 | } 47 | -------------------------------------------------------------------------------- /kernel/printf.c: -------------------------------------------------------------------------------- 1 | // 2 | // formatted console output -- printf, panic. 3 | // 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "param.h" 9 | #include "spinlock.h" 10 | #include "sleeplock.h" 11 | #include "fs.h" 12 | #include "file.h" 13 | #include "memlayout.h" 14 | #include "riscv.h" 15 | #include "defs.h" 16 | #include "proc.h" 17 | 18 | volatile int panicked = 0; 19 | 20 | // lock to avoid interleaving concurrent printf's. 21 | static struct { 22 | struct spinlock lock; 23 | int locking; 24 | } pr; 25 | 26 | static char digits[] = "0123456789abcdef"; 27 | 28 | static void 29 | printint(int xx, int base, int sign) 30 | { 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 | 52 | static void 53 | printptr(uint64 x) 54 | { 55 | int i; 56 | consputc('0'); 57 | consputc('x'); 58 | for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) 59 | consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); 60 | } 61 | 62 | // Print to the console. only understands %d, %x, %p, %s. 63 | void 64 | printf(char *fmt, ...) 65 | { 66 | va_list ap; 67 | int i, c, locking; 68 | char *s; 69 | 70 | locking = pr.locking; 71 | if(locking) 72 | acquire(&pr.lock); 73 | 74 | if (fmt == 0) 75 | panic("null fmt"); 76 | 77 | va_start(ap, fmt); 78 | for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ 79 | if(c != '%'){ 80 | consputc(c); 81 | continue; 82 | } 83 | c = fmt[++i] & 0xff; 84 | if(c == 0) 85 | break; 86 | switch(c){ 87 | case 'd': 88 | printint(va_arg(ap, int), 10, 1); 89 | break; 90 | case 'x': 91 | printint(va_arg(ap, int), 16, 1); 92 | break; 93 | case 'p': 94 | printptr(va_arg(ap, uint64)); 95 | break; 96 | case 's': 97 | if((s = va_arg(ap, char*)) == 0) 98 | s = "(null)"; 99 | for(; *s; s++) 100 | consputc(*s); 101 | break; 102 | case '%': 103 | consputc('%'); 104 | break; 105 | default: 106 | // Print unknown % sequence to draw attention. 107 | consputc('%'); 108 | consputc(c); 109 | break; 110 | } 111 | } 112 | 113 | if(locking) 114 | release(&pr.lock); 115 | } 116 | 117 | void 118 | panic(char *s) 119 | { 120 | pr.locking = 0; 121 | printf("panic: "); 122 | printf(s); 123 | printf("\n"); 124 | panicked = 1; // freeze uart output from other CPUs 125 | for(;;) 126 | ; 127 | } 128 | 129 | void 130 | printfinit(void) 131 | { 132 | initlock(&pr.lock, "pr"); 133 | pr.locking = 1; 134 | } 135 | -------------------------------------------------------------------------------- /kernel/proc.h: -------------------------------------------------------------------------------- 1 | // Saved registers for kernel context switches. 2 | struct context { 3 | uint64 ra; 4 | uint64 sp; 5 | 6 | // callee-saved 7 | uint64 s0; 8 | uint64 s1; 9 | uint64 s2; 10 | uint64 s3; 11 | uint64 s4; 12 | uint64 s5; 13 | uint64 s6; 14 | uint64 s7; 15 | uint64 s8; 16 | uint64 s9; 17 | uint64 s10; 18 | uint64 s11; 19 | }; 20 | 21 | // Per-CPU state. 22 | struct cpu { 23 | struct proc *proc; // The process running on this cpu, or null. 24 | struct context context; // swtch() here to enter scheduler(). 25 | int noff; // Depth of push_off() nesting. 26 | int intena; // Were interrupts enabled before push_off()? 27 | }; 28 | 29 | extern struct cpu cpus[NCPU]; 30 | 31 | // per-process data for the trap handling code in trampoline.S. 32 | // sits in a page by itself just under the trampoline page in the 33 | // user page table. not specially mapped in the kernel page table. 34 | // the sscratch register points here. 35 | // uservec in trampoline.S saves user registers in the trapframe, 36 | // then initializes registers from the trapframe's 37 | // kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap. 38 | // usertrapret() and userret in trampoline.S set up 39 | // the trapframe's kernel_*, restore user registers from the 40 | // trapframe, switch to the user page table, and enter user space. 41 | // the trapframe includes callee-saved user registers like s0-s11 because the 42 | // return-to-user path via usertrapret() doesn't return through 43 | // the entire kernel call stack. 44 | struct trapframe { 45 | /* 0 */ uint64 kernel_satp; // kernel page table 46 | /* 8 */ uint64 kernel_sp; // top of process's kernel stack 47 | /* 16 */ uint64 kernel_trap; // usertrap() 48 | /* 24 */ uint64 epc; // saved user program counter 49 | /* 32 */ uint64 kernel_hartid; // saved kernel tp 50 | /* 40 */ uint64 ra; 51 | /* 48 */ uint64 sp; 52 | /* 56 */ uint64 gp; 53 | /* 64 */ uint64 tp; 54 | /* 72 */ uint64 t0; 55 | /* 80 */ uint64 t1; 56 | /* 88 */ uint64 t2; 57 | /* 96 */ uint64 s0; 58 | /* 104 */ uint64 s1; 59 | /* 112 */ uint64 a0; 60 | /* 120 */ uint64 a1; 61 | /* 128 */ uint64 a2; 62 | /* 136 */ uint64 a3; 63 | /* 144 */ uint64 a4; 64 | /* 152 */ uint64 a5; 65 | /* 160 */ uint64 a6; 66 | /* 168 */ uint64 a7; 67 | /* 176 */ uint64 s2; 68 | /* 184 */ uint64 s3; 69 | /* 192 */ uint64 s4; 70 | /* 200 */ uint64 s5; 71 | /* 208 */ uint64 s6; 72 | /* 216 */ uint64 s7; 73 | /* 224 */ uint64 s8; 74 | /* 232 */ uint64 s9; 75 | /* 240 */ uint64 s10; 76 | /* 248 */ uint64 s11; 77 | /* 256 */ uint64 t3; 78 | /* 264 */ uint64 t4; 79 | /* 272 */ uint64 t5; 80 | /* 280 */ uint64 t6; 81 | }; 82 | 83 | enum procstate { UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; 84 | 85 | // Per-process state 86 | struct proc { 87 | struct spinlock lock; 88 | 89 | // p->lock must be held when using these: 90 | enum procstate state; // Process state 91 | struct proc *parent; // Parent process 92 | void *chan; // If non-zero, sleeping on chan 93 | int killed; // If non-zero, have been killed 94 | int xstate; // Exit status to be returned to parent's wait 95 | int pid; // Process ID 96 | 97 | // these are private to the process, so p->lock need not be held. 98 | uint64 kstack; // Virtual address of kernel stack 99 | uint64 sz; // Size of process memory (bytes) 100 | pagetable_t pagetable; // User page table 101 | struct trapframe *trapframe; // data page for trampoline.S 102 | struct context context; // swtch() here to run process 103 | struct file *ofile[NOFILE]; // Open files 104 | struct inode *cwd; // Current directory 105 | char name[16]; // Process name (debugging) 106 | }; 107 | -------------------------------------------------------------------------------- /kernel/ramdisk.c: -------------------------------------------------------------------------------- 1 | // 2 | // ramdisk that uses the disk image loaded by qemu -initrd fs.img 3 | // 4 | 5 | #include "types.h" 6 | #include "riscv.h" 7 | #include "defs.h" 8 | #include "param.h" 9 | #include "memlayout.h" 10 | #include "spinlock.h" 11 | #include "sleeplock.h" 12 | #include "fs.h" 13 | #include "buf.h" 14 | 15 | void 16 | ramdiskinit(void) 17 | { 18 | } 19 | 20 | // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 21 | // Else if B_VALID is not set, read buf from disk, set B_VALID. 22 | void 23 | ramdiskrw(struct buf *b) 24 | { 25 | if(!holdingsleep(&b->lock)) 26 | panic("ramdiskrw: buf not locked"); 27 | if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 28 | panic("ramdiskrw: nothing to do"); 29 | 30 | if(b->blockno >= FSSIZE) 31 | panic("ramdiskrw: blockno too big"); 32 | 33 | uint64 diskaddr = b->blockno * BSIZE; 34 | char *addr = (char *)RAMDISK + diskaddr; 35 | 36 | if(b->flags & B_DIRTY){ 37 | // write 38 | memmove(addr, b->data, BSIZE); 39 | b->flags &= ~B_DIRTY; 40 | } else { 41 | // read 42 | memmove(b->data, addr, BSIZE); 43 | b->flags |= B_VALID; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kernel/riscv.h: -------------------------------------------------------------------------------- 1 | // which hart (core) is this? 2 | static inline uint64 3 | r_mhartid() 4 | { 5 | uint64 x; 6 | asm volatile("csrr %0, mhartid" : "=r" (x) ); 7 | return x; 8 | } 9 | 10 | // Machine Status Register, mstatus 11 | 12 | #define MSTATUS_MPP_MASK (3L << 11) // previous mode. 13 | #define MSTATUS_MPP_M (3L << 11) 14 | #define MSTATUS_MPP_S (1L << 11) 15 | #define MSTATUS_MPP_U (0L << 11) 16 | #define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. 17 | 18 | static inline uint64 19 | r_mstatus() 20 | { 21 | uint64 x; 22 | asm volatile("csrr %0, mstatus" : "=r" (x) ); 23 | return x; 24 | } 25 | 26 | static inline void 27 | w_mstatus(uint64 x) 28 | { 29 | asm volatile("csrw mstatus, %0" : : "r" (x)); 30 | } 31 | 32 | // machine exception program counter, holds the 33 | // instruction address to which a return from 34 | // exception will go. 35 | static inline void 36 | w_mepc(uint64 x) 37 | { 38 | asm volatile("csrw mepc, %0" : : "r" (x)); 39 | } 40 | 41 | // Supervisor Status Register, sstatus 42 | 43 | #define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User 44 | #define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable 45 | #define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable 46 | #define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable 47 | #define SSTATUS_UIE (1L << 0) // User Interrupt Enable 48 | 49 | static inline uint64 50 | r_sstatus() 51 | { 52 | uint64 x; 53 | asm volatile("csrr %0, sstatus" : "=r" (x) ); 54 | return x; 55 | } 56 | 57 | static inline void 58 | w_sstatus(uint64 x) 59 | { 60 | asm volatile("csrw sstatus, %0" : : "r" (x)); 61 | } 62 | 63 | // Supervisor Interrupt Pending 64 | static inline uint64 65 | r_sip() 66 | { 67 | uint64 x; 68 | asm volatile("csrr %0, sip" : "=r" (x) ); 69 | return x; 70 | } 71 | 72 | static inline void 73 | w_sip(uint64 x) 74 | { 75 | asm volatile("csrw sip, %0" : : "r" (x)); 76 | } 77 | 78 | // Supervisor Interrupt Enable 79 | #define SIE_SEIE (1L << 9) // external 80 | #define SIE_STIE (1L << 5) // timer 81 | #define SIE_SSIE (1L << 1) // software 82 | static inline uint64 83 | r_sie() 84 | { 85 | uint64 x; 86 | asm volatile("csrr %0, sie" : "=r" (x) ); 87 | return x; 88 | } 89 | 90 | static inline void 91 | w_sie(uint64 x) 92 | { 93 | asm volatile("csrw sie, %0" : : "r" (x)); 94 | } 95 | 96 | // Machine-mode Interrupt Enable 97 | #define MIE_MEIE (1L << 11) // external 98 | #define MIE_MTIE (1L << 7) // timer 99 | #define MIE_MSIE (1L << 3) // software 100 | static inline uint64 101 | r_mie() 102 | { 103 | uint64 x; 104 | asm volatile("csrr %0, mie" : "=r" (x) ); 105 | return x; 106 | } 107 | 108 | static inline void 109 | w_mie(uint64 x) 110 | { 111 | asm volatile("csrw mie, %0" : : "r" (x)); 112 | } 113 | 114 | // machine exception program counter, holds the 115 | // instruction address to which a return from 116 | // exception will go. 117 | static inline void 118 | w_sepc(uint64 x) 119 | { 120 | asm volatile("csrw sepc, %0" : : "r" (x)); 121 | } 122 | 123 | static inline uint64 124 | r_sepc() 125 | { 126 | uint64 x; 127 | asm volatile("csrr %0, sepc" : "=r" (x) ); 128 | return x; 129 | } 130 | 131 | // Machine Exception Delegation 132 | static inline uint64 133 | r_medeleg() 134 | { 135 | uint64 x; 136 | asm volatile("csrr %0, medeleg" : "=r" (x) ); 137 | return x; 138 | } 139 | 140 | static inline void 141 | w_medeleg(uint64 x) 142 | { 143 | asm volatile("csrw medeleg, %0" : : "r" (x)); 144 | } 145 | 146 | // Machine Interrupt Delegation 147 | static inline uint64 148 | r_mideleg() 149 | { 150 | uint64 x; 151 | asm volatile("csrr %0, mideleg" : "=r" (x) ); 152 | return x; 153 | } 154 | 155 | static inline void 156 | w_mideleg(uint64 x) 157 | { 158 | asm volatile("csrw mideleg, %0" : : "r" (x)); 159 | } 160 | 161 | // Supervisor Trap-Vector Base Address 162 | // low two bits are mode. 163 | static inline void 164 | w_stvec(uint64 x) 165 | { 166 | asm volatile("csrw stvec, %0" : : "r" (x)); 167 | } 168 | 169 | static inline uint64 170 | r_stvec() 171 | { 172 | uint64 x; 173 | asm volatile("csrr %0, stvec" : "=r" (x) ); 174 | return x; 175 | } 176 | 177 | // Machine-mode interrupt vector 178 | static inline void 179 | w_mtvec(uint64 x) 180 | { 181 | asm volatile("csrw mtvec, %0" : : "r" (x)); 182 | } 183 | 184 | // use riscv's sv39 page table scheme. 185 | #define SATP_SV39 (8L << 60) 186 | 187 | #define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) 188 | 189 | // supervisor address translation and protection; 190 | // holds the address of the page table. 191 | static inline void 192 | w_satp(uint64 x) 193 | { 194 | asm volatile("csrw satp, %0" : : "r" (x)); 195 | } 196 | 197 | static inline uint64 198 | r_satp() 199 | { 200 | uint64 x; 201 | asm volatile("csrr %0, satp" : "=r" (x) ); 202 | return x; 203 | } 204 | 205 | // Supervisor Scratch register, for early trap handler in trampoline.S. 206 | static inline void 207 | w_sscratch(uint64 x) 208 | { 209 | asm volatile("csrw sscratch, %0" : : "r" (x)); 210 | } 211 | 212 | static inline void 213 | w_mscratch(uint64 x) 214 | { 215 | asm volatile("csrw mscratch, %0" : : "r" (x)); 216 | } 217 | 218 | // Supervisor Trap Cause 219 | static inline uint64 220 | r_scause() 221 | { 222 | uint64 x; 223 | asm volatile("csrr %0, scause" : "=r" (x) ); 224 | return x; 225 | } 226 | 227 | // Supervisor Trap Value 228 | static inline uint64 229 | r_stval() 230 | { 231 | uint64 x; 232 | asm volatile("csrr %0, stval" : "=r" (x) ); 233 | return x; 234 | } 235 | 236 | // Machine-mode Counter-Enable 237 | static inline void 238 | w_mcounteren(uint64 x) 239 | { 240 | asm volatile("csrw mcounteren, %0" : : "r" (x)); 241 | } 242 | 243 | static inline uint64 244 | r_mcounteren() 245 | { 246 | uint64 x; 247 | asm volatile("csrr %0, mcounteren" : "=r" (x) ); 248 | return x; 249 | } 250 | 251 | // machine-mode cycle counter 252 | static inline uint64 253 | r_time() 254 | { 255 | uint64 x; 256 | asm volatile("csrr %0, time" : "=r" (x) ); 257 | return x; 258 | } 259 | 260 | // enable device interrupts 261 | static inline void 262 | intr_on() 263 | { 264 | w_sstatus(r_sstatus() | SSTATUS_SIE); 265 | } 266 | 267 | // disable device interrupts 268 | static inline void 269 | intr_off() 270 | { 271 | w_sstatus(r_sstatus() & ~SSTATUS_SIE); 272 | } 273 | 274 | // are device interrupts enabled? 275 | static inline int 276 | intr_get() 277 | { 278 | uint64 x = r_sstatus(); 279 | return (x & SSTATUS_SIE) != 0; 280 | } 281 | 282 | static inline uint64 283 | r_sp() 284 | { 285 | uint64 x; 286 | asm volatile("mv %0, sp" : "=r" (x) ); 287 | return x; 288 | } 289 | 290 | // read and write tp, the thread pointer, which holds 291 | // this core's hartid (core number), the index into cpus[]. 292 | static inline uint64 293 | r_tp() 294 | { 295 | uint64 x; 296 | asm volatile("mv %0, tp" : "=r" (x) ); 297 | return x; 298 | } 299 | 300 | static inline void 301 | w_tp(uint64 x) 302 | { 303 | asm volatile("mv tp, %0" : : "r" (x)); 304 | } 305 | 306 | static inline uint64 307 | r_ra() 308 | { 309 | uint64 x; 310 | asm volatile("mv %0, ra" : "=r" (x) ); 311 | return x; 312 | } 313 | 314 | // flush the TLB. 315 | static inline void 316 | sfence_vma() 317 | { 318 | // the zero, zero means flush all TLB entries. 319 | asm volatile("sfence.vma zero, zero"); 320 | } 321 | 322 | 323 | #define PGSIZE 4096 // bytes per page 324 | #define PGSHIFT 12 // bits of offset within a page 325 | 326 | #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) 327 | #define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) 328 | 329 | #define PTE_V (1L << 0) // valid 330 | #define PTE_R (1L << 1) 331 | #define PTE_W (1L << 2) 332 | #define PTE_X (1L << 3) 333 | #define PTE_U (1L << 4) // 1 -> user can access 334 | 335 | // shift a physical address to the right place for a PTE. 336 | #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) 337 | 338 | #define PTE2PA(pte) (((pte) >> 10) << 12) 339 | 340 | #define PTE_FLAGS(pte) ((pte) & 0x3FF) 341 | 342 | // extract the three 9-bit page table indices from a virtual address. 343 | #define PXMASK 0x1FF // 9 bits 344 | #define PXSHIFT(level) (PGSHIFT+(9*(level))) 345 | #define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK) 346 | 347 | // one beyond the highest possible virtual address. 348 | // MAXVA is actually one bit less than the max allowed by 349 | // Sv39, to avoid having to sign-extend virtual addresses 350 | // that have the high bit set. 351 | #define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) 352 | 353 | typedef uint64 pte_t; 354 | typedef uint64 *pagetable_t; // 512 PTEs 355 | -------------------------------------------------------------------------------- /kernel/sleeplock.c: -------------------------------------------------------------------------------- 1 | // Sleeping locks 2 | 3 | #include "types.h" 4 | #include "riscv.h" 5 | #include "defs.h" 6 | #include "param.h" 7 | #include "memlayout.h" 8 | #include "spinlock.h" 9 | #include "proc.h" 10 | #include "sleeplock.h" 11 | 12 | void 13 | initsleeplock(struct sleeplock *lk, char *name) 14 | { 15 | initlock(&lk->lk, "sleep lock"); 16 | lk->name = name; 17 | lk->locked = 0; 18 | lk->pid = 0; 19 | } 20 | 21 | void 22 | acquiresleep(struct sleeplock *lk) 23 | { 24 | acquire(&lk->lk); 25 | while (lk->locked) { 26 | sleep(lk, &lk->lk); 27 | } 28 | lk->locked = 1; 29 | lk->pid = myproc()->pid; 30 | release(&lk->lk); 31 | } 32 | 33 | void 34 | releasesleep(struct sleeplock *lk) 35 | { 36 | acquire(&lk->lk); 37 | lk->locked = 0; 38 | lk->pid = 0; 39 | wakeup(lk); 40 | release(&lk->lk); 41 | } 42 | 43 | int 44 | holdingsleep(struct sleeplock *lk) 45 | { 46 | int r; 47 | 48 | acquire(&lk->lk); 49 | r = lk->locked && (lk->pid == myproc()->pid); 50 | release(&lk->lk); 51 | return r; 52 | } 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /kernel/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 | -------------------------------------------------------------------------------- /kernel/spinlock.c: -------------------------------------------------------------------------------- 1 | // Mutual exclusion spin locks. 2 | 3 | #include "types.h" 4 | #include "param.h" 5 | #include "memlayout.h" 6 | #include "spinlock.h" 7 | #include "riscv.h" 8 | #include "proc.h" 9 | #include "defs.h" 10 | 11 | void 12 | initlock(struct spinlock *lk, char *name) 13 | { 14 | lk->name = name; 15 | lk->locked = 0; 16 | lk->cpu = 0; 17 | } 18 | 19 | // Acquire the lock. 20 | // Loops (spins) until the lock is acquired. 21 | void 22 | acquire(struct spinlock *lk) 23 | { 24 | push_off(); // disable interrupts to avoid deadlock. 25 | if(holding(lk)) 26 | panic("acquire"); 27 | 28 | // On RISC-V, sync_lock_test_and_set turns into an atomic swap: 29 | // a5 = 1 30 | // s1 = &lk->locked 31 | // amoswap.w.aq a5, a5, (s1) 32 | while(__sync_lock_test_and_set(&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 strictly after the lock is acquired. 38 | // On RISC-V, this emits a fence instruction. 39 | __sync_synchronize(); 40 | 41 | // Record info about lock acquisition for holding() and debugging. 42 | lk->cpu = mycpu(); 43 | } 44 | 45 | // Release the lock. 46 | void 47 | release(struct spinlock *lk) 48 | { 49 | if(!holding(lk)) 50 | panic("release"); 51 | 52 | lk->cpu = 0; 53 | 54 | // Tell the C compiler and the CPU to not move loads or stores 55 | // past this point, to ensure that all the stores in the critical 56 | // section are visible to other CPUs before the lock is released, 57 | // and that loads in the critical section occur strictly before 58 | // the lock is released. 59 | // On RISC-V, this emits a fence instruction. 60 | __sync_synchronize(); 61 | 62 | // Release the lock, equivalent to lk->locked = 0. 63 | // This code doesn't use a C assignment, since the C standard 64 | // implies that an assignment might be implemented with 65 | // multiple store instructions. 66 | // On RISC-V, sync_lock_release turns into an atomic swap: 67 | // s1 = &lk->locked 68 | // amoswap.w zero, zero, (s1) 69 | __sync_lock_release(&lk->locked); 70 | 71 | pop_off(); 72 | } 73 | 74 | // Check whether this cpu is holding the lock. 75 | // Interrupts must be off. 76 | int 77 | holding(struct spinlock *lk) 78 | { 79 | int r; 80 | r = (lk->locked && lk->cpu == mycpu()); 81 | return r; 82 | } 83 | 84 | // push_off/pop_off are like intr_off()/intr_on() except that they are matched: 85 | // it takes two pop_off()s to undo two push_off()s. Also, if interrupts 86 | // are initially off, then push_off, pop_off leaves them off. 87 | 88 | void 89 | push_off(void) 90 | { 91 | int old = intr_get(); 92 | 93 | intr_off(); 94 | if(mycpu()->noff == 0) 95 | mycpu()->intena = old; 96 | mycpu()->noff += 1; 97 | } 98 | 99 | void 100 | pop_off(void) 101 | { 102 | struct cpu *c = mycpu(); 103 | if(intr_get()) 104 | panic("pop_off - interruptible"); 105 | if(c->noff < 1) 106 | panic("pop_off"); 107 | c->noff -= 1; 108 | if(c->noff == 0 && c->intena) 109 | intr_on(); 110 | } 111 | -------------------------------------------------------------------------------- /kernel/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 | }; 9 | 10 | -------------------------------------------------------------------------------- /kernel/start.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "defs.h" 6 | 7 | void main(); 8 | void timerinit(); 9 | 10 | // entry.S needs one stack per CPU. 11 | __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; 12 | 13 | // scratch area for timer interrupt, one per CPU. 14 | uint64 mscratch0[NCPU * 32]; 15 | 16 | // assembly code in kernelvec.S for machine-mode timer interrupt. 17 | extern void timervec(); 18 | 19 | // entry.S jumps here in machine mode on stack0. 20 | void 21 | start() 22 | { 23 | // set M Previous Privilege mode to Supervisor, for mret. 24 | unsigned long x = r_mstatus(); 25 | x &= ~MSTATUS_MPP_MASK; 26 | x |= MSTATUS_MPP_S; 27 | w_mstatus(x); 28 | 29 | // set M Exception Program Counter to main, for mret. 30 | // requires gcc -mcmodel=medany 31 | w_mepc((uint64)main); 32 | 33 | // disable paging for now. 34 | w_satp(0); 35 | 36 | // delegate all interrupts and exceptions to supervisor mode. 37 | w_medeleg(0xffff); 38 | w_mideleg(0xffff); 39 | w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); 40 | 41 | // ask for clock interrupts. 42 | timerinit(); 43 | 44 | // keep each CPU's hartid in its tp register, for cpuid(). 45 | int id = r_mhartid(); 46 | w_tp(id); 47 | 48 | // switch to supervisor mode and jump to main(). 49 | asm volatile("mret"); 50 | } 51 | 52 | // set up to receive timer interrupts in machine mode, 53 | // which arrive at timervec in kernelvec.S, 54 | // which turns them into software interrupts for 55 | // devintr() in trap.c. 56 | void 57 | timerinit() 58 | { 59 | // each CPU has a separate source of timer interrupts. 60 | int id = r_mhartid(); 61 | 62 | // ask the CLINT for a timer interrupt. 63 | int interval = 1000000; // cycles; about 1/10th second in qemu. 64 | *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; 65 | 66 | // prepare information in scratch[] for timervec. 67 | // scratch[0..3] : space for timervec to save registers. 68 | // scratch[4] : address of CLINT MTIMECMP register. 69 | // scratch[5] : desired interval (in cycles) between timer interrupts. 70 | uint64 *scratch = &mscratch0[32 * id]; 71 | scratch[4] = CLINT_MTIMECMP(id); 72 | scratch[5] = interval; 73 | w_mscratch((uint64)scratch); 74 | 75 | // set the machine-mode trap handler. 76 | w_mtvec((uint64)timervec); 77 | 78 | // enable machine-mode interrupts. 79 | w_mstatus(r_mstatus() | MSTATUS_MIE); 80 | 81 | // enable machine-mode timer interrupts. 82 | w_mie(r_mie() | MIE_MTIE); 83 | } 84 | -------------------------------------------------------------------------------- /kernel/stat.h: -------------------------------------------------------------------------------- 1 | #define T_DIR 1 // Directory 2 | #define T_FILE 2 // File 3 | #define T_DEVICE 3 // Device 4 | 5 | struct stat { 6 | int dev; // File system's disk device 7 | uint ino; // Inode number 8 | short type; // Type of file 9 | short nlink; // Number of links to file 10 | uint64 size; // Size of file in bytes 11 | }; 12 | -------------------------------------------------------------------------------- /kernel/string.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | void* 4 | memset(void *dst, int c, uint n) 5 | { 6 | char *cdst = (char *) dst; 7 | int i; 8 | for(i = 0; i < n; i++){ 9 | cdst[i] = c; 10 | } 11 | return dst; 12 | } 13 | 14 | int 15 | memcmp(const void *v1, const void *v2, uint n) 16 | { 17 | const uchar *s1, *s2; 18 | 19 | s1 = v1; 20 | s2 = v2; 21 | while(n-- > 0){ 22 | if(*s1 != *s2) 23 | return *s1 - *s2; 24 | s1++, s2++; 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | void* 31 | memmove(void *dst, const void *src, uint n) 32 | { 33 | const char *s; 34 | char *d; 35 | 36 | s = src; 37 | d = dst; 38 | if(s < d && s + n > d){ 39 | s += n; 40 | d += n; 41 | while(n-- > 0) 42 | *--d = *--s; 43 | } else 44 | while(n-- > 0) 45 | *d++ = *s++; 46 | 47 | return dst; 48 | } 49 | 50 | // memcpy exists to placate GCC. Use memmove. 51 | void* 52 | memcpy(void *dst, const void *src, uint n) 53 | { 54 | return memmove(dst, src, n); 55 | } 56 | 57 | int 58 | strncmp(const char *p, const char *q, uint n) 59 | { 60 | while(n > 0 && *p && *p == *q) 61 | n--, p++, q++; 62 | if(n == 0) 63 | return 0; 64 | return (uchar)*p - (uchar)*q; 65 | } 66 | 67 | char* 68 | strncpy(char *s, const char *t, int n) 69 | { 70 | char *os; 71 | 72 | os = s; 73 | while(n-- > 0 && (*s++ = *t++) != 0) 74 | ; 75 | while(n-- > 0) 76 | *s++ = 0; 77 | return os; 78 | } 79 | 80 | // Like strncpy but guaranteed to NUL-terminate. 81 | char* 82 | safestrcpy(char *s, const char *t, int n) 83 | { 84 | char *os; 85 | 86 | os = s; 87 | if(n <= 0) 88 | return os; 89 | while(--n > 0 && (*s++ = *t++) != 0) 90 | ; 91 | *s = 0; 92 | return os; 93 | } 94 | 95 | int 96 | strlen(const char *s) 97 | { 98 | int n; 99 | 100 | for(n = 0; s[n]; n++) 101 | ; 102 | return n; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /kernel/swtch.S: -------------------------------------------------------------------------------- 1 | # Context switch 2 | # 3 | # void swtch(struct context *old, struct context *new); 4 | # 5 | # Save current registers in old. Load from new. 6 | 7 | 8 | .globl swtch 9 | swtch: 10 | sd ra, 0(a0) 11 | sd sp, 8(a0) 12 | sd s0, 16(a0) 13 | sd s1, 24(a0) 14 | sd s2, 32(a0) 15 | sd s3, 40(a0) 16 | sd s4, 48(a0) 17 | sd s5, 56(a0) 18 | sd s6, 64(a0) 19 | sd s7, 72(a0) 20 | sd s8, 80(a0) 21 | sd s9, 88(a0) 22 | sd s10, 96(a0) 23 | sd s11, 104(a0) 24 | 25 | ld ra, 0(a1) 26 | ld sp, 8(a1) 27 | ld s0, 16(a1) 28 | ld s1, 24(a1) 29 | ld s2, 32(a1) 30 | ld s3, 40(a1) 31 | ld s4, 48(a1) 32 | ld s5, 56(a1) 33 | ld s6, 64(a1) 34 | ld s7, 72(a1) 35 | ld s8, 80(a1) 36 | ld s9, 88(a1) 37 | ld s10, 96(a1) 38 | ld s11, 104(a1) 39 | 40 | ret 41 | 42 | 43 | -------------------------------------------------------------------------------- /kernel/syscall.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "spinlock.h" 6 | #include "proc.h" 7 | #include "syscall.h" 8 | #include "defs.h" 9 | 10 | // Fetch the uint64 at addr from the current process. 11 | int 12 | fetchaddr(uint64 addr, uint64 *ip) 13 | { 14 | struct proc *p = myproc(); 15 | if(addr >= p->sz || addr+sizeof(uint64) > p->sz) 16 | return -1; 17 | if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) 18 | return -1; 19 | return 0; 20 | } 21 | 22 | // Fetch the nul-terminated string at addr from the current process. 23 | // Returns length of string, not including nul, or -1 for error. 24 | int 25 | fetchstr(uint64 addr, char *buf, int max) 26 | { 27 | struct proc *p = myproc(); 28 | int err = copyinstr(p->pagetable, buf, addr, max); 29 | if(err < 0) 30 | return err; 31 | return strlen(buf); 32 | } 33 | 34 | static uint64 35 | argraw(int n) 36 | { 37 | struct proc *p = myproc(); 38 | switch (n) { 39 | case 0: 40 | return p->trapframe->a0; 41 | case 1: 42 | return p->trapframe->a1; 43 | case 2: 44 | return p->trapframe->a2; 45 | case 3: 46 | return p->trapframe->a3; 47 | case 4: 48 | return p->trapframe->a4; 49 | case 5: 50 | return p->trapframe->a5; 51 | } 52 | panic("argraw"); 53 | return -1; 54 | } 55 | 56 | // Fetch the nth 32-bit system call argument. 57 | int 58 | argint(int n, int *ip) 59 | { 60 | *ip = argraw(n); 61 | return 0; 62 | } 63 | 64 | // Retrieve an argument as a pointer. 65 | // Doesn't check for legality, since 66 | // copyin/copyout will do that. 67 | int 68 | argaddr(int n, uint64 *ip) 69 | { 70 | *ip = argraw(n); 71 | return 0; 72 | } 73 | 74 | // Fetch the nth word-sized system call argument as a null-terminated string. 75 | // Copies into buf, at most max. 76 | // Returns string length if OK (including nul), -1 if error. 77 | int 78 | argstr(int n, char *buf, int max) 79 | { 80 | uint64 addr; 81 | if(argaddr(n, &addr) < 0) 82 | return -1; 83 | return fetchstr(addr, buf, max); 84 | } 85 | 86 | extern uint64 sys_chdir(void); 87 | extern uint64 sys_close(void); 88 | extern uint64 sys_dup(void); 89 | extern uint64 sys_exec(void); 90 | extern uint64 sys_exit(void); 91 | extern uint64 sys_fork(void); 92 | extern uint64 sys_fstat(void); 93 | extern uint64 sys_getpid(void); 94 | extern uint64 sys_kill(void); 95 | extern uint64 sys_link(void); 96 | extern uint64 sys_mkdir(void); 97 | extern uint64 sys_mknod(void); 98 | extern uint64 sys_open(void); 99 | extern uint64 sys_pipe(void); 100 | extern uint64 sys_read(void); 101 | extern uint64 sys_sbrk(void); 102 | extern uint64 sys_sleep(void); 103 | extern uint64 sys_unlink(void); 104 | extern uint64 sys_wait(void); 105 | extern uint64 sys_write(void); 106 | extern uint64 sys_uptime(void); 107 | 108 | static uint64 (*syscalls[])(void) = { 109 | [SYS_fork] sys_fork, 110 | [SYS_exit] sys_exit, 111 | [SYS_wait] sys_wait, 112 | [SYS_pipe] sys_pipe, 113 | [SYS_read] sys_read, 114 | [SYS_kill] sys_kill, 115 | [SYS_exec] sys_exec, 116 | [SYS_fstat] sys_fstat, 117 | [SYS_chdir] sys_chdir, 118 | [SYS_dup] sys_dup, 119 | [SYS_getpid] sys_getpid, 120 | [SYS_sbrk] sys_sbrk, 121 | [SYS_sleep] sys_sleep, 122 | [SYS_uptime] sys_uptime, 123 | [SYS_open] sys_open, 124 | [SYS_write] sys_write, 125 | [SYS_mknod] sys_mknod, 126 | [SYS_unlink] sys_unlink, 127 | [SYS_link] sys_link, 128 | [SYS_mkdir] sys_mkdir, 129 | [SYS_close] sys_close, 130 | }; 131 | 132 | void 133 | syscall(void) 134 | { 135 | int num; 136 | struct proc *p = myproc(); 137 | 138 | num = p->trapframe->a7; 139 | if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 140 | p->trapframe->a0 = syscalls[num](); 141 | } else { 142 | printf("%d %s: unknown sys call %d\n", 143 | p->pid, p->name, num); 144 | p->trapframe->a0 = -1; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kernel/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 | -------------------------------------------------------------------------------- /kernel/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 "riscv.h" 9 | #include "defs.h" 10 | #include "param.h" 11 | #include "stat.h" 12 | #include "spinlock.h" 13 | #include "proc.h" 14 | #include "fs.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 *p = myproc(); 45 | 46 | for(fd = 0; fd < NOFILE; fd++){ 47 | if(p->ofile[fd] == 0){ 48 | p->ofile[fd] = f; 49 | return fd; 50 | } 51 | } 52 | return -1; 53 | } 54 | 55 | uint64 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 | uint64 70 | sys_read(void) 71 | { 72 | struct file *f; 73 | int n; 74 | uint64 p; 75 | 76 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0) 77 | return -1; 78 | return fileread(f, p, n); 79 | } 80 | 81 | uint64 82 | sys_write(void) 83 | { 84 | struct file *f; 85 | int n; 86 | uint64 p; 87 | 88 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0) 89 | return -1; 90 | 91 | return filewrite(f, p, n); 92 | } 93 | 94 | uint64 95 | sys_close(void) 96 | { 97 | int fd; 98 | struct file *f; 99 | 100 | if(argfd(0, &fd, &f) < 0) 101 | return -1; 102 | myproc()->ofile[fd] = 0; 103 | fileclose(f); 104 | return 0; 105 | } 106 | 107 | uint64 108 | sys_fstat(void) 109 | { 110 | struct file *f; 111 | uint64 st; // user pointer to struct stat 112 | 113 | if(argfd(0, 0, &f) < 0 || argaddr(1, &st) < 0) 114 | return -1; 115 | return filestat(f, st); 116 | } 117 | 118 | // Create the path new as a link to the same inode as old. 119 | uint64 120 | sys_link(void) 121 | { 122 | char name[DIRSIZ], new[MAXPATH], old[MAXPATH]; 123 | struct inode *dp, *ip; 124 | 125 | if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0) 126 | return -1; 127 | 128 | begin_op(); 129 | if((ip = namei(old)) == 0){ 130 | end_op(); 131 | return -1; 132 | } 133 | 134 | ilock(ip); 135 | if(ip->type == T_DIR){ 136 | iunlockput(ip); 137 | end_op(); 138 | return -1; 139 | } 140 | 141 | ip->nlink++; 142 | iupdate(ip); 143 | iunlock(ip); 144 | 145 | if((dp = nameiparent(new, name)) == 0) 146 | goto bad; 147 | ilock(dp); 148 | if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ 149 | iunlockput(dp); 150 | goto bad; 151 | } 152 | iunlockput(dp); 153 | iput(ip); 154 | 155 | end_op(); 156 | 157 | return 0; 158 | 159 | bad: 160 | ilock(ip); 161 | ip->nlink--; 162 | iupdate(ip); 163 | iunlockput(ip); 164 | end_op(); 165 | return -1; 166 | } 167 | 168 | // Is the directory dp empty except for "." and ".." ? 169 | static int 170 | isdirempty(struct inode *dp) 171 | { 172 | int off; 173 | struct dirent de; 174 | 175 | for(off=2*sizeof(de); offsize; off+=sizeof(de)){ 176 | if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) 177 | panic("isdirempty: readi"); 178 | if(de.inum != 0) 179 | return 0; 180 | } 181 | return 1; 182 | } 183 | 184 | uint64 185 | sys_unlink(void) 186 | { 187 | struct inode *ip, *dp; 188 | struct dirent de; 189 | char name[DIRSIZ], path[MAXPATH]; 190 | uint off; 191 | 192 | if(argstr(0, path, MAXPATH) < 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, 0, (uint64)&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 | 250 | ilock(dp); 251 | 252 | if((ip = dirlookup(dp, name, 0)) != 0){ 253 | iunlockput(dp); 254 | ilock(ip); 255 | if(type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE)) 256 | return ip; 257 | iunlockput(ip); 258 | return 0; 259 | } 260 | 261 | if((ip = ialloc(dp->dev, type)) == 0) 262 | panic("create: ialloc"); 263 | 264 | ilock(ip); 265 | ip->major = major; 266 | ip->minor = minor; 267 | ip->nlink = 1; 268 | iupdate(ip); 269 | 270 | if(type == T_DIR){ // Create . and .. entries. 271 | dp->nlink++; // for ".." 272 | iupdate(dp); 273 | // No ip->nlink++ for ".": avoid cyclic ref count. 274 | if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) 275 | panic("create dots"); 276 | } 277 | 278 | if(dirlink(dp, name, ip->inum) < 0) 279 | panic("create: dirlink"); 280 | 281 | iunlockput(dp); 282 | 283 | return ip; 284 | } 285 | 286 | uint64 287 | sys_open(void) 288 | { 289 | char path[MAXPATH]; 290 | int fd, omode; 291 | struct file *f; 292 | struct inode *ip; 293 | int n; 294 | 295 | if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0) 296 | return -1; 297 | 298 | begin_op(); 299 | 300 | if(omode & O_CREATE){ 301 | ip = create(path, T_FILE, 0, 0); 302 | if(ip == 0){ 303 | end_op(); 304 | return -1; 305 | } 306 | } else { 307 | if((ip = namei(path)) == 0){ 308 | end_op(); 309 | return -1; 310 | } 311 | ilock(ip); 312 | if(ip->type == T_DIR && omode != O_RDONLY){ 313 | iunlockput(ip); 314 | end_op(); 315 | return -1; 316 | } 317 | } 318 | 319 | if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){ 320 | iunlockput(ip); 321 | end_op(); 322 | return -1; 323 | } 324 | 325 | if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ 326 | if(f) 327 | fileclose(f); 328 | iunlockput(ip); 329 | end_op(); 330 | return -1; 331 | } 332 | 333 | if(ip->type == T_DEVICE){ 334 | f->type = FD_DEVICE; 335 | f->major = ip->major; 336 | } else { 337 | f->type = FD_INODE; 338 | f->off = 0; 339 | } 340 | f->ip = ip; 341 | f->readable = !(omode & O_WRONLY); 342 | f->writable = (omode & O_WRONLY) || (omode & O_RDWR); 343 | 344 | if((omode & O_TRUNC) && ip->type == T_FILE){ 345 | itrunc(ip); 346 | } 347 | 348 | iunlock(ip); 349 | end_op(); 350 | 351 | return fd; 352 | } 353 | 354 | uint64 355 | sys_mkdir(void) 356 | { 357 | char path[MAXPATH]; 358 | struct inode *ip; 359 | 360 | begin_op(); 361 | if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ 362 | end_op(); 363 | return -1; 364 | } 365 | iunlockput(ip); 366 | end_op(); 367 | return 0; 368 | } 369 | 370 | uint64 371 | sys_mknod(void) 372 | { 373 | struct inode *ip; 374 | char path[MAXPATH]; 375 | int major, minor; 376 | 377 | begin_op(); 378 | if((argstr(0, path, MAXPATH)) < 0 || 379 | argint(1, &major) < 0 || 380 | argint(2, &minor) < 0 || 381 | (ip = create(path, T_DEVICE, major, minor)) == 0){ 382 | end_op(); 383 | return -1; 384 | } 385 | iunlockput(ip); 386 | end_op(); 387 | return 0; 388 | } 389 | 390 | uint64 391 | sys_chdir(void) 392 | { 393 | char path[MAXPATH]; 394 | struct inode *ip; 395 | struct proc *p = myproc(); 396 | 397 | begin_op(); 398 | if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){ 399 | end_op(); 400 | return -1; 401 | } 402 | ilock(ip); 403 | if(ip->type != T_DIR){ 404 | iunlockput(ip); 405 | end_op(); 406 | return -1; 407 | } 408 | iunlock(ip); 409 | iput(p->cwd); 410 | end_op(); 411 | p->cwd = ip; 412 | return 0; 413 | } 414 | 415 | uint64 416 | sys_exec(void) 417 | { 418 | char path[MAXPATH], *argv[MAXARG]; 419 | int i; 420 | uint64 uargv, uarg; 421 | 422 | if(argstr(0, path, MAXPATH) < 0 || argaddr(1, &uargv) < 0){ 423 | return -1; 424 | } 425 | memset(argv, 0, sizeof(argv)); 426 | for(i=0;; i++){ 427 | if(i >= NELEM(argv)){ 428 | goto bad; 429 | } 430 | if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){ 431 | goto bad; 432 | } 433 | if(uarg == 0){ 434 | argv[i] = 0; 435 | break; 436 | } 437 | argv[i] = kalloc(); 438 | if(argv[i] == 0) 439 | goto bad; 440 | if(fetchstr(uarg, argv[i], PGSIZE) < 0) 441 | goto bad; 442 | } 443 | 444 | int ret = exec(path, argv); 445 | 446 | for(i = 0; i < NELEM(argv) && argv[i] != 0; i++) 447 | kfree(argv[i]); 448 | 449 | return ret; 450 | 451 | bad: 452 | for(i = 0; i < NELEM(argv) && argv[i] != 0; i++) 453 | kfree(argv[i]); 454 | return -1; 455 | } 456 | 457 | uint64 458 | sys_pipe(void) 459 | { 460 | uint64 fdarray; // user pointer to array of two integers 461 | struct file *rf, *wf; 462 | int fd0, fd1; 463 | struct proc *p = myproc(); 464 | 465 | if(argaddr(0, &fdarray) < 0) 466 | return -1; 467 | if(pipealloc(&rf, &wf) < 0) 468 | return -1; 469 | fd0 = -1; 470 | if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ 471 | if(fd0 >= 0) 472 | p->ofile[fd0] = 0; 473 | fileclose(rf); 474 | fileclose(wf); 475 | return -1; 476 | } 477 | if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 || 478 | copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){ 479 | p->ofile[fd0] = 0; 480 | p->ofile[fd1] = 0; 481 | fileclose(rf); 482 | fileclose(wf); 483 | return -1; 484 | } 485 | return 0; 486 | } 487 | -------------------------------------------------------------------------------- /kernel/sysproc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "riscv.h" 3 | #include "defs.h" 4 | #include "date.h" 5 | #include "param.h" 6 | #include "memlayout.h" 7 | #include "spinlock.h" 8 | #include "proc.h" 9 | 10 | uint64 11 | sys_exit(void) 12 | { 13 | int n; 14 | if(argint(0, &n) < 0) 15 | return -1; 16 | exit(n); 17 | return 0; // not reached 18 | } 19 | 20 | uint64 21 | sys_getpid(void) 22 | { 23 | return myproc()->pid; 24 | } 25 | 26 | uint64 27 | sys_fork(void) 28 | { 29 | return fork(); 30 | } 31 | 32 | uint64 33 | sys_wait(void) 34 | { 35 | uint64 p; 36 | if(argaddr(0, &p) < 0) 37 | return -1; 38 | return wait(p); 39 | } 40 | 41 | uint64 42 | sys_sbrk(void) 43 | { 44 | int addr; 45 | int n; 46 | 47 | if(argint(0, &n) < 0) 48 | return -1; 49 | addr = myproc()->sz; 50 | if(growproc(n) < 0) 51 | return -1; 52 | return addr; 53 | } 54 | 55 | uint64 56 | sys_sleep(void) 57 | { 58 | int n; 59 | uint ticks0; 60 | 61 | if(argint(0, &n) < 0) 62 | return -1; 63 | acquire(&tickslock); 64 | ticks0 = ticks; 65 | while(ticks - ticks0 < n){ 66 | if(myproc()->killed){ 67 | release(&tickslock); 68 | return -1; 69 | } 70 | sleep(&ticks, &tickslock); 71 | } 72 | release(&tickslock); 73 | return 0; 74 | } 75 | 76 | uint64 77 | sys_kill(void) 78 | { 79 | int pid; 80 | 81 | if(argint(0, &pid) < 0) 82 | return -1; 83 | return kill(pid); 84 | } 85 | 86 | // return how many clock tick interrupts have occurred 87 | // since start. 88 | uint64 89 | sys_uptime(void) 90 | { 91 | uint xticks; 92 | 93 | acquire(&tickslock); 94 | xticks = ticks; 95 | release(&tickslock); 96 | return xticks; 97 | } 98 | -------------------------------------------------------------------------------- /kernel/trampoline.S: -------------------------------------------------------------------------------- 1 | # 2 | # code to switch between user and kernel space. 3 | # 4 | # this code is mapped at the same virtual address 5 | # (TRAMPOLINE) in user and kernel space so that 6 | # it continues to work when it switches page tables. 7 | # 8 | # kernel.ld causes this to be aligned 9 | # to a page boundary. 10 | # 11 | .section trampsec 12 | .globl trampoline 13 | trampoline: 14 | .align 4 15 | .globl uservec 16 | uservec: 17 | # 18 | # trap.c sets stvec to point here, so 19 | # traps from user space start here, 20 | # in supervisor mode, but with a 21 | # user page table. 22 | # 23 | # sscratch points to where the process's p->trapframe is 24 | # mapped into user space, at TRAPFRAME. 25 | # 26 | 27 | # swap a0 and sscratch 28 | # so that a0 is TRAPFRAME 29 | csrrw a0, sscratch, a0 30 | 31 | # save the user registers in TRAPFRAME 32 | sd ra, 40(a0) 33 | sd sp, 48(a0) 34 | sd gp, 56(a0) 35 | sd tp, 64(a0) 36 | sd t0, 72(a0) 37 | sd t1, 80(a0) 38 | sd t2, 88(a0) 39 | sd s0, 96(a0) 40 | sd s1, 104(a0) 41 | sd a1, 120(a0) 42 | sd a2, 128(a0) 43 | sd a3, 136(a0) 44 | sd a4, 144(a0) 45 | sd a5, 152(a0) 46 | sd a6, 160(a0) 47 | sd a7, 168(a0) 48 | sd s2, 176(a0) 49 | sd s3, 184(a0) 50 | sd s4, 192(a0) 51 | sd s5, 200(a0) 52 | sd s6, 208(a0) 53 | sd s7, 216(a0) 54 | sd s8, 224(a0) 55 | sd s9, 232(a0) 56 | sd s10, 240(a0) 57 | sd s11, 248(a0) 58 | sd t3, 256(a0) 59 | sd t4, 264(a0) 60 | sd t5, 272(a0) 61 | sd t6, 280(a0) 62 | 63 | # save the user a0 in p->trapframe->a0 64 | csrr t0, sscratch 65 | sd t0, 112(a0) 66 | 67 | # restore kernel stack pointer from p->trapframe->kernel_sp 68 | ld sp, 8(a0) 69 | 70 | # make tp hold the current hartid, from p->trapframe->kernel_hartid 71 | ld tp, 32(a0) 72 | 73 | # load the address of usertrap(), p->trapframe->kernel_trap 74 | ld t0, 16(a0) 75 | 76 | # restore kernel page table from p->trapframe->kernel_satp 77 | ld t1, 0(a0) 78 | csrw satp, t1 79 | sfence.vma zero, zero 80 | 81 | # a0 is no longer valid, since the kernel page 82 | # table does not specially map p->tf. 83 | 84 | # jump to usertrap(), which does not return 85 | jr t0 86 | 87 | .globl userret 88 | userret: 89 | # userret(TRAPFRAME, pagetable) 90 | # switch from kernel to user. 91 | # usertrapret() calls here. 92 | # a0: TRAPFRAME, in user page table. 93 | # a1: user page table, for satp. 94 | 95 | # switch to the user page table. 96 | csrw satp, a1 97 | sfence.vma zero, zero 98 | 99 | # put the saved user a0 in sscratch, so we 100 | # can swap it with our a0 (TRAPFRAME) in the last step. 101 | ld t0, 112(a0) 102 | csrw sscratch, t0 103 | 104 | # restore all but a0 from TRAPFRAME 105 | ld ra, 40(a0) 106 | ld sp, 48(a0) 107 | ld gp, 56(a0) 108 | ld tp, 64(a0) 109 | ld t0, 72(a0) 110 | ld t1, 80(a0) 111 | ld t2, 88(a0) 112 | ld s0, 96(a0) 113 | ld s1, 104(a0) 114 | ld a1, 120(a0) 115 | ld a2, 128(a0) 116 | ld a3, 136(a0) 117 | ld a4, 144(a0) 118 | ld a5, 152(a0) 119 | ld a6, 160(a0) 120 | ld a7, 168(a0) 121 | ld s2, 176(a0) 122 | ld s3, 184(a0) 123 | ld s4, 192(a0) 124 | ld s5, 200(a0) 125 | ld s6, 208(a0) 126 | ld s7, 216(a0) 127 | ld s8, 224(a0) 128 | ld s9, 232(a0) 129 | ld s10, 240(a0) 130 | ld s11, 248(a0) 131 | ld t3, 256(a0) 132 | ld t4, 264(a0) 133 | ld t5, 272(a0) 134 | ld t6, 280(a0) 135 | 136 | # restore user a0, and save TRAPFRAME in sscratch 137 | csrrw a0, sscratch, a0 138 | 139 | # return to user mode and user pc. 140 | # usertrapret() set up sstatus and sepc. 141 | sret 142 | -------------------------------------------------------------------------------- /kernel/trap.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "riscv.h" 5 | #include "spinlock.h" 6 | #include "proc.h" 7 | #include "defs.h" 8 | 9 | struct spinlock tickslock; 10 | uint ticks; 11 | 12 | extern char trampoline[], uservec[], userret[]; 13 | 14 | // in kernelvec.S, calls kerneltrap(). 15 | void kernelvec(); 16 | 17 | extern int devintr(); 18 | 19 | void 20 | trapinit(void) 21 | { 22 | initlock(&tickslock, "time"); 23 | } 24 | 25 | // set up to take exceptions and traps while in the kernel. 26 | void 27 | trapinithart(void) 28 | { 29 | w_stvec((uint64)kernelvec); 30 | } 31 | 32 | // 33 | // handle an interrupt, exception, or system call from user space. 34 | // called from trampoline.S 35 | // 36 | void 37 | usertrap(void) 38 | { 39 | int which_dev = 0; 40 | 41 | if((r_sstatus() & SSTATUS_SPP) != 0) 42 | panic("usertrap: not from user mode"); 43 | 44 | // send interrupts and exceptions to kerneltrap(), 45 | // since we're now in the kernel. 46 | w_stvec((uint64)kernelvec); 47 | 48 | struct proc *p = myproc(); 49 | 50 | // save user program counter. 51 | p->trapframe->epc = r_sepc(); 52 | 53 | if(r_scause() == 8){ 54 | // system call 55 | 56 | if(p->killed) 57 | exit(-1); 58 | 59 | // sepc points to the ecall instruction, 60 | // but we want to return to the next instruction. 61 | p->trapframe->epc += 4; 62 | 63 | // an interrupt will change sstatus &c registers, 64 | // so don't enable until done with those registers. 65 | intr_on(); 66 | 67 | syscall(); 68 | } else if((which_dev = devintr()) != 0){ 69 | // ok 70 | } else { 71 | printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); 72 | printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); 73 | p->killed = 1; 74 | } 75 | 76 | if(p->killed) 77 | exit(-1); 78 | 79 | // give up the CPU if this is a timer interrupt. 80 | if(which_dev == 2) 81 | yield(); 82 | 83 | usertrapret(); 84 | } 85 | 86 | // 87 | // return to user space 88 | // 89 | void 90 | usertrapret(void) 91 | { 92 | struct proc *p = myproc(); 93 | 94 | // we're about to switch the destination of traps from 95 | // kerneltrap() to usertrap(), so turn off interrupts until 96 | // we're back in user space, where usertrap() is correct. 97 | intr_off(); 98 | 99 | // send syscalls, interrupts, and exceptions to trampoline.S 100 | w_stvec(TRAMPOLINE + (uservec - trampoline)); 101 | 102 | // set up trapframe values that uservec will need when 103 | // the process next re-enters the kernel. 104 | p->trapframe->kernel_satp = r_satp(); // kernel page table 105 | p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack 106 | p->trapframe->kernel_trap = (uint64)usertrap; 107 | p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid() 108 | 109 | // set up the registers that trampoline.S's sret will use 110 | // to get to user space. 111 | 112 | // set S Previous Privilege mode to User. 113 | unsigned long x = r_sstatus(); 114 | x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode 115 | x |= SSTATUS_SPIE; // enable interrupts in user mode 116 | w_sstatus(x); 117 | 118 | // set S Exception Program Counter to the saved user pc. 119 | w_sepc(p->trapframe->epc); 120 | 121 | // tell trampoline.S the user page table to switch to. 122 | uint64 satp = MAKE_SATP(p->pagetable); 123 | 124 | // jump to trampoline.S at the top of memory, which 125 | // switches to the user page table, restores user registers, 126 | // and switches to user mode with sret. 127 | uint64 fn = TRAMPOLINE + (userret - trampoline); 128 | ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp); 129 | } 130 | 131 | // interrupts and exceptions from kernel code go here via kernelvec, 132 | // on whatever the current kernel stack is. 133 | void 134 | kerneltrap() 135 | { 136 | int which_dev = 0; 137 | uint64 sepc = r_sepc(); 138 | uint64 sstatus = r_sstatus(); 139 | uint64 scause = r_scause(); 140 | 141 | if((sstatus & SSTATUS_SPP) == 0) 142 | panic("kerneltrap: not from supervisor mode"); 143 | if(intr_get() != 0) 144 | panic("kerneltrap: interrupts enabled"); 145 | 146 | if((which_dev = devintr()) == 0){ 147 | printf("scause %p\n", scause); 148 | printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); 149 | panic("kerneltrap"); 150 | } 151 | 152 | // give up the CPU if this is a timer interrupt. 153 | if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) 154 | yield(); 155 | 156 | // the yield() may have caused some traps to occur, 157 | // so restore trap registers for use by kernelvec.S's sepc instruction. 158 | w_sepc(sepc); 159 | w_sstatus(sstatus); 160 | } 161 | 162 | void 163 | clockintr() 164 | { 165 | acquire(&tickslock); 166 | ticks++; 167 | wakeup(&ticks); 168 | release(&tickslock); 169 | } 170 | 171 | // check if it's an external interrupt or software interrupt, 172 | // and handle it. 173 | // returns 2 if timer interrupt, 174 | // 1 if other device, 175 | // 0 if not recognized. 176 | int 177 | devintr() 178 | { 179 | uint64 scause = r_scause(); 180 | 181 | if((scause & 0x8000000000000000L) && 182 | (scause & 0xff) == 9){ 183 | // this is a supervisor external interrupt, via PLIC. 184 | 185 | // irq indicates which device interrupted. 186 | int irq = plic_claim(); 187 | 188 | if(irq == UART0_IRQ){ 189 | uartintr(); 190 | } else if(irq == VIRTIO0_IRQ){ 191 | virtio_disk_intr(); 192 | } else if(irq){ 193 | printf("unexpected interrupt irq=%d\n", irq); 194 | } 195 | 196 | // the PLIC allows each device to raise at most one 197 | // interrupt at a time; tell the PLIC the device is 198 | // now allowed to interrupt again. 199 | if(irq) 200 | plic_complete(irq); 201 | 202 | return 1; 203 | } else if(scause == 0x8000000000000001L){ 204 | // software interrupt from a machine-mode timer interrupt, 205 | // forwarded by timervec in kernelvec.S. 206 | 207 | if(cpuid() == 0){ 208 | clockintr(); 209 | } 210 | 211 | // acknowledge the software interrupt by clearing 212 | // the SSIP bit in sip. 213 | w_sip(r_sip() & ~2); 214 | 215 | return 2; 216 | } else { 217 | return 0; 218 | } 219 | } 220 | 221 | -------------------------------------------------------------------------------- /kernel/types.h: -------------------------------------------------------------------------------- 1 | typedef unsigned int uint; 2 | typedef unsigned short ushort; 3 | typedef unsigned char uchar; 4 | 5 | typedef unsigned char uint8; 6 | typedef unsigned short uint16; 7 | typedef unsigned int uint32; 8 | typedef unsigned long uint64; 9 | 10 | typedef uint64 pde_t; 11 | -------------------------------------------------------------------------------- /kernel/uart.c: -------------------------------------------------------------------------------- 1 | // 2 | // low-level driver routines for 16550a UART. 3 | // 4 | 5 | #include "types.h" 6 | #include "param.h" 7 | #include "memlayout.h" 8 | #include "riscv.h" 9 | #include "spinlock.h" 10 | #include "proc.h" 11 | #include "defs.h" 12 | 13 | // the UART control registers are memory-mapped 14 | // at address UART0. this macro returns the 15 | // address of one of the registers. 16 | #define Reg(reg) ((volatile unsigned char *)(UART0 + reg)) 17 | 18 | // the UART control registers. 19 | // some have different meanings for 20 | // read vs write. 21 | // see http://byterunner.com/16550.html 22 | #define RHR 0 // receive holding register (for input bytes) 23 | #define THR 0 // transmit holding register (for output bytes) 24 | #define IER 1 // interrupt enable register 25 | #define IER_TX_ENABLE (1<<0) 26 | #define IER_RX_ENABLE (1<<1) 27 | #define FCR 2 // FIFO control register 28 | #define FCR_FIFO_ENABLE (1<<0) 29 | #define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs 30 | #define ISR 2 // interrupt status register 31 | #define LCR 3 // line control register 32 | #define LCR_EIGHT_BITS (3<<0) 33 | #define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate 34 | #define LSR 5 // line status register 35 | #define LSR_RX_READY (1<<0) // input is waiting to be read from RHR 36 | #define LSR_TX_IDLE (1<<5) // THR can accept another character to send 37 | 38 | #define ReadReg(reg) (*(Reg(reg))) 39 | #define WriteReg(reg, v) (*(Reg(reg)) = (v)) 40 | 41 | // the transmit output buffer. 42 | struct spinlock uart_tx_lock; 43 | #define UART_TX_BUF_SIZE 32 44 | char uart_tx_buf[UART_TX_BUF_SIZE]; 45 | int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++] 46 | int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++] 47 | 48 | extern volatile int panicked; // from printf.c 49 | 50 | void uartstart(); 51 | 52 | void 53 | uartinit(void) 54 | { 55 | // disable interrupts. 56 | WriteReg(IER, 0x00); 57 | 58 | // special mode to set baud rate. 59 | WriteReg(LCR, LCR_BAUD_LATCH); 60 | 61 | // LSB for baud rate of 38.4K. 62 | WriteReg(0, 0x03); 63 | 64 | // MSB for baud rate of 38.4K. 65 | WriteReg(1, 0x00); 66 | 67 | // leave set-baud mode, 68 | // and set word length to 8 bits, no parity. 69 | WriteReg(LCR, LCR_EIGHT_BITS); 70 | 71 | // reset and enable FIFOs. 72 | WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); 73 | 74 | // enable transmit and receive interrupts. 75 | WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); 76 | 77 | initlock(&uart_tx_lock, "uart"); 78 | } 79 | 80 | // add a character to the output buffer and tell the 81 | // UART to start sending if it isn't already. 82 | // blocks if the output buffer is full. 83 | // because it may block, it can't be called 84 | // from interrupts; it's only suitable for use 85 | // by write(). 86 | void 87 | uartputc(int c) 88 | { 89 | acquire(&uart_tx_lock); 90 | 91 | if(panicked){ 92 | for(;;) 93 | ; 94 | } 95 | 96 | while(1){ 97 | if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ 98 | // buffer is full. 99 | // wait for uartstart() to open up space in the buffer. 100 | sleep(&uart_tx_r, &uart_tx_lock); 101 | } else { 102 | uart_tx_buf[uart_tx_w] = c; 103 | uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE; 104 | uartstart(); 105 | release(&uart_tx_lock); 106 | return; 107 | } 108 | } 109 | } 110 | 111 | // alternate version of uartputc() that doesn't 112 | // use interrupts, for use by kernel printf() and 113 | // to echo characters. it spins waiting for the uart's 114 | // output register to be empty. 115 | void 116 | uartputc_sync(int c) 117 | { 118 | push_off(); 119 | 120 | if(panicked){ 121 | for(;;) 122 | ; 123 | } 124 | 125 | // wait for Transmit Holding Empty to be set in LSR. 126 | while((ReadReg(LSR) & LSR_TX_IDLE) == 0) 127 | ; 128 | WriteReg(THR, c); 129 | 130 | pop_off(); 131 | } 132 | 133 | // if the UART is idle, and a character is waiting 134 | // in the transmit buffer, send it. 135 | // caller must hold uart_tx_lock. 136 | // called from both the top- and bottom-half. 137 | void 138 | uartstart() 139 | { 140 | while(1){ 141 | if(uart_tx_w == uart_tx_r){ 142 | // transmit buffer is empty. 143 | return; 144 | } 145 | 146 | if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ 147 | // the UART transmit holding register is full, 148 | // so we cannot give it another byte. 149 | // it will interrupt when it's ready for a new byte. 150 | return; 151 | } 152 | 153 | int c = uart_tx_buf[uart_tx_r]; 154 | uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE; 155 | 156 | // maybe uartputc() is waiting for space in the buffer. 157 | wakeup(&uart_tx_r); 158 | 159 | WriteReg(THR, c); 160 | } 161 | } 162 | 163 | // read one input character from the UART. 164 | // return -1 if none is waiting. 165 | int 166 | uartgetc(void) 167 | { 168 | if(ReadReg(LSR) & 0x01){ 169 | // input data is ready. 170 | return ReadReg(RHR); 171 | } else { 172 | return -1; 173 | } 174 | } 175 | 176 | // handle a uart interrupt, raised because input has 177 | // arrived, or the uart is ready for more output, or 178 | // both. called from trap.c. 179 | void 180 | uartintr(void) 181 | { 182 | // read and process incoming characters. 183 | while(1){ 184 | int c = uartgetc(); 185 | if(c == -1) 186 | break; 187 | consoleintr(c); 188 | } 189 | 190 | // send buffered characters. 191 | acquire(&uart_tx_lock); 192 | uartstart(); 193 | release(&uart_tx_lock); 194 | } 195 | -------------------------------------------------------------------------------- /kernel/virtio.h: -------------------------------------------------------------------------------- 1 | // 2 | // virtio device definitions. 3 | // for both the mmio interface, and virtio descriptors. 4 | // only tested with qemu. 5 | // this is the "legacy" virtio interface. 6 | // 7 | // the virtio spec: 8 | // https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf 9 | // 10 | 11 | // virtio mmio control registers, mapped starting at 0x10001000. 12 | // from qemu virtio_mmio.h 13 | #define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976 14 | #define VIRTIO_MMIO_VERSION 0x004 // version; 1 is legacy 15 | #define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk 16 | #define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551 17 | #define VIRTIO_MMIO_DEVICE_FEATURES 0x010 18 | #define VIRTIO_MMIO_DRIVER_FEATURES 0x020 19 | #define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 // page size for PFN, write-only 20 | #define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only 21 | #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only 22 | #define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only 23 | #define VIRTIO_MMIO_QUEUE_ALIGN 0x03c // used ring alignment, write-only 24 | #define VIRTIO_MMIO_QUEUE_PFN 0x040 // physical page number for queue, read/write 25 | #define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit 26 | #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only 27 | #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only 28 | #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only 29 | #define VIRTIO_MMIO_STATUS 0x070 // read/write 30 | 31 | // status register bits, from qemu virtio_config.h 32 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 33 | #define VIRTIO_CONFIG_S_DRIVER 2 34 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 35 | #define VIRTIO_CONFIG_S_FEATURES_OK 8 36 | 37 | // device feature bits 38 | #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ 39 | #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ 40 | #define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ 41 | #define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ 42 | #define VIRTIO_F_ANY_LAYOUT 27 43 | #define VIRTIO_RING_F_INDIRECT_DESC 28 44 | #define VIRTIO_RING_F_EVENT_IDX 29 45 | 46 | // this many virtio descriptors. 47 | // must be a power of two. 48 | #define NUM 8 49 | 50 | struct VRingDesc { 51 | uint64 addr; 52 | uint32 len; 53 | uint16 flags; 54 | uint16 next; 55 | }; 56 | #define VRING_DESC_F_NEXT 1 // chained with another descriptor 57 | #define VRING_DESC_F_WRITE 2 // device writes (vs read) 58 | 59 | struct VRingUsedElem { 60 | uint32 id; // index of start of completed descriptor chain 61 | uint32 len; 62 | }; 63 | 64 | // for disk ops 65 | #define VIRTIO_BLK_T_IN 0 // read the disk 66 | #define VIRTIO_BLK_T_OUT 1 // write the disk 67 | 68 | struct UsedArea { 69 | uint16 flags; 70 | uint16 id; 71 | struct VRingUsedElem elems[NUM]; 72 | }; 73 | -------------------------------------------------------------------------------- /kernel/virtio_disk.c: -------------------------------------------------------------------------------- 1 | // 2 | // driver for qemu's virtio disk device. 3 | // uses qemu's mmio interface to virtio. 4 | // qemu presents a "legacy" virtio interface. 5 | // 6 | // qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 7 | // 8 | 9 | #include "types.h" 10 | #include "riscv.h" 11 | #include "defs.h" 12 | #include "param.h" 13 | #include "memlayout.h" 14 | #include "spinlock.h" 15 | #include "sleeplock.h" 16 | #include "fs.h" 17 | #include "buf.h" 18 | #include "virtio.h" 19 | 20 | // the address of virtio mmio register r. 21 | #define R(r) ((volatile uint32 *)(VIRTIO0 + (r))) 22 | 23 | static struct disk { 24 | // memory for virtio descriptors &c for queue 0. 25 | // this is a global instead of allocated because it must 26 | // be multiple contiguous pages, which kalloc() 27 | // doesn't support, and page aligned. 28 | char pages[2*PGSIZE]; 29 | struct VRingDesc *desc; 30 | uint16 *avail; 31 | struct UsedArea *used; 32 | 33 | // our own book-keeping. 34 | char free[NUM]; // is a descriptor free? 35 | uint16 used_idx; // we've looked this far in used[2..NUM]. 36 | 37 | // track info about in-flight operations, 38 | // for use when completion interrupt arrives. 39 | // indexed by first descriptor index of chain. 40 | struct { 41 | struct buf *b; 42 | char status; 43 | } info[NUM]; 44 | 45 | struct spinlock vdisk_lock; 46 | 47 | } __attribute__ ((aligned (PGSIZE))) disk; 48 | 49 | void 50 | virtio_disk_init(void) 51 | { 52 | uint32 status = 0; 53 | 54 | initlock(&disk.vdisk_lock, "virtio_disk"); 55 | 56 | if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || 57 | *R(VIRTIO_MMIO_VERSION) != 1 || 58 | *R(VIRTIO_MMIO_DEVICE_ID) != 2 || 59 | *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){ 60 | panic("could not find virtio disk"); 61 | } 62 | 63 | status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; 64 | *R(VIRTIO_MMIO_STATUS) = status; 65 | 66 | status |= VIRTIO_CONFIG_S_DRIVER; 67 | *R(VIRTIO_MMIO_STATUS) = status; 68 | 69 | // negotiate features 70 | uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES); 71 | features &= ~(1 << VIRTIO_BLK_F_RO); 72 | features &= ~(1 << VIRTIO_BLK_F_SCSI); 73 | features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE); 74 | features &= ~(1 << VIRTIO_BLK_F_MQ); 75 | features &= ~(1 << VIRTIO_F_ANY_LAYOUT); 76 | features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); 77 | features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); 78 | *R(VIRTIO_MMIO_DRIVER_FEATURES) = features; 79 | 80 | // tell device that feature negotiation is complete. 81 | status |= VIRTIO_CONFIG_S_FEATURES_OK; 82 | *R(VIRTIO_MMIO_STATUS) = status; 83 | 84 | // tell device we're completely ready. 85 | status |= VIRTIO_CONFIG_S_DRIVER_OK; 86 | *R(VIRTIO_MMIO_STATUS) = status; 87 | 88 | *R(VIRTIO_MMIO_GUEST_PAGE_SIZE) = PGSIZE; 89 | 90 | // initialize queue 0. 91 | *R(VIRTIO_MMIO_QUEUE_SEL) = 0; 92 | uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX); 93 | if(max == 0) 94 | panic("virtio disk has no queue 0"); 95 | if(max < NUM) 96 | panic("virtio disk max queue too short"); 97 | *R(VIRTIO_MMIO_QUEUE_NUM) = NUM; 98 | memset(disk.pages, 0, sizeof(disk.pages)); 99 | *R(VIRTIO_MMIO_QUEUE_PFN) = ((uint64)disk.pages) >> PGSHIFT; 100 | 101 | // desc = pages -- num * VRingDesc 102 | // avail = pages + 0x40 -- 2 * uint16, then num * uint16 103 | // used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem 104 | 105 | disk.desc = (struct VRingDesc *) disk.pages; 106 | disk.avail = (uint16*)(((char*)disk.desc) + NUM*sizeof(struct VRingDesc)); 107 | disk.used = (struct UsedArea *) (disk.pages + PGSIZE); 108 | 109 | for(int i = 0; i < NUM; i++) 110 | disk.free[i] = 1; 111 | 112 | // plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ. 113 | } 114 | 115 | // find a free descriptor, mark it non-free, return its index. 116 | static int 117 | alloc_desc() 118 | { 119 | for(int i = 0; i < NUM; i++){ 120 | if(disk.free[i]){ 121 | disk.free[i] = 0; 122 | return i; 123 | } 124 | } 125 | return -1; 126 | } 127 | 128 | // mark a descriptor as free. 129 | static void 130 | free_desc(int i) 131 | { 132 | if(i >= NUM) 133 | panic("virtio_disk_intr 1"); 134 | if(disk.free[i]) 135 | panic("virtio_disk_intr 2"); 136 | disk.desc[i].addr = 0; 137 | disk.free[i] = 1; 138 | wakeup(&disk.free[0]); 139 | } 140 | 141 | // free a chain of descriptors. 142 | static void 143 | free_chain(int i) 144 | { 145 | while(1){ 146 | free_desc(i); 147 | if(disk.desc[i].flags & VRING_DESC_F_NEXT) 148 | i = disk.desc[i].next; 149 | else 150 | break; 151 | } 152 | } 153 | 154 | static int 155 | alloc3_desc(int *idx) 156 | { 157 | for(int i = 0; i < 3; i++){ 158 | idx[i] = alloc_desc(); 159 | if(idx[i] < 0){ 160 | for(int j = 0; j < i; j++) 161 | free_desc(idx[j]); 162 | return -1; 163 | } 164 | } 165 | return 0; 166 | } 167 | 168 | void 169 | virtio_disk_rw(struct buf *b, int write) 170 | { 171 | uint64 sector = b->blockno * (BSIZE / 512); 172 | 173 | acquire(&disk.vdisk_lock); 174 | 175 | // the spec says that legacy block operations use three 176 | // descriptors: one for type/reserved/sector, one for 177 | // the data, one for a 1-byte status result. 178 | 179 | // allocate the three descriptors. 180 | int idx[3]; 181 | while(1){ 182 | if(alloc3_desc(idx) == 0) { 183 | break; 184 | } 185 | sleep(&disk.free[0], &disk.vdisk_lock); 186 | } 187 | 188 | // format the three descriptors. 189 | // qemu's virtio-blk.c reads them. 190 | 191 | struct virtio_blk_outhdr { 192 | uint32 type; 193 | uint32 reserved; 194 | uint64 sector; 195 | } buf0; 196 | 197 | if(write) 198 | buf0.type = VIRTIO_BLK_T_OUT; // write the disk 199 | else 200 | buf0.type = VIRTIO_BLK_T_IN; // read the disk 201 | buf0.reserved = 0; 202 | buf0.sector = sector; 203 | 204 | // buf0 is on a kernel stack, which is not direct mapped, 205 | // thus the call to kvmpa(). 206 | disk.desc[idx[0]].addr = (uint64) kvmpa((uint64) &buf0); 207 | disk.desc[idx[0]].len = sizeof(buf0); 208 | disk.desc[idx[0]].flags = VRING_DESC_F_NEXT; 209 | disk.desc[idx[0]].next = idx[1]; 210 | 211 | disk.desc[idx[1]].addr = (uint64) b->data; 212 | disk.desc[idx[1]].len = BSIZE; 213 | if(write) 214 | disk.desc[idx[1]].flags = 0; // device reads b->data 215 | else 216 | disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data 217 | disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT; 218 | disk.desc[idx[1]].next = idx[2]; 219 | 220 | disk.info[idx[0]].status = 0; 221 | disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status; 222 | disk.desc[idx[2]].len = 1; 223 | disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status 224 | disk.desc[idx[2]].next = 0; 225 | 226 | // record struct buf for virtio_disk_intr(). 227 | b->disk = 1; 228 | disk.info[idx[0]].b = b; 229 | 230 | // avail[0] is flags 231 | // avail[1] tells the device how far to look in avail[2...]. 232 | // avail[2...] are desc[] indices the device should process. 233 | // we only tell device the first index in our chain of descriptors. 234 | disk.avail[2 + (disk.avail[1] % NUM)] = idx[0]; 235 | __sync_synchronize(); 236 | disk.avail[1] = disk.avail[1] + 1; 237 | 238 | *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number 239 | 240 | // Wait for virtio_disk_intr() to say request has finished. 241 | while(b->disk == 1) { 242 | sleep(b, &disk.vdisk_lock); 243 | } 244 | 245 | disk.info[idx[0]].b = 0; 246 | free_chain(idx[0]); 247 | 248 | release(&disk.vdisk_lock); 249 | } 250 | 251 | void 252 | virtio_disk_intr() 253 | { 254 | acquire(&disk.vdisk_lock); 255 | 256 | while((disk.used_idx % NUM) != (disk.used->id % NUM)){ 257 | int id = disk.used->elems[disk.used_idx].id; 258 | 259 | if(disk.info[id].status != 0) 260 | panic("virtio_disk_intr status"); 261 | 262 | disk.info[id].b->disk = 0; // disk is done with buf 263 | wakeup(disk.info[id].b); 264 | 265 | disk.used_idx = (disk.used_idx + 1) % NUM; 266 | } 267 | *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; 268 | 269 | release(&disk.vdisk_lock); 270 | } 271 | -------------------------------------------------------------------------------- /kernel/vm.c: -------------------------------------------------------------------------------- 1 | #include "param.h" 2 | #include "types.h" 3 | #include "memlayout.h" 4 | #include "elf.h" 5 | #include "riscv.h" 6 | #include "defs.h" 7 | #include "fs.h" 8 | 9 | /* 10 | * the kernel's page table. 11 | */ 12 | pagetable_t kernel_pagetable; 13 | 14 | extern char etext[]; // kernel.ld sets this to end of kernel code. 15 | 16 | extern char trampoline[]; // trampoline.S 17 | 18 | /* 19 | * create a direct-map page table for the kernel. 20 | */ 21 | void 22 | kvminit() 23 | { 24 | kernel_pagetable = (pagetable_t) kalloc(); 25 | memset(kernel_pagetable, 0, PGSIZE); 26 | 27 | // uart registers 28 | kvmmap(UART0, UART0, PGSIZE, PTE_R | PTE_W); 29 | 30 | // virtio mmio disk interface 31 | kvmmap(VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); 32 | 33 | // CLINT 34 | kvmmap(CLINT, CLINT, 0x10000, PTE_R | PTE_W); 35 | 36 | // PLIC 37 | kvmmap(PLIC, PLIC, 0x400000, PTE_R | PTE_W); 38 | 39 | // map kernel text executable and read-only. 40 | kvmmap(KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X); 41 | 42 | // map kernel data and the physical RAM we'll make use of. 43 | kvmmap((uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W); 44 | 45 | // map the trampoline for trap entry/exit to 46 | // the highest virtual address in the kernel. 47 | kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); 48 | } 49 | 50 | // Switch h/w page table register to the kernel's page table, 51 | // and enable paging. 52 | void 53 | kvminithart() 54 | { 55 | w_satp(MAKE_SATP(kernel_pagetable)); 56 | sfence_vma(); 57 | } 58 | 59 | // Return the address of the PTE in page table pagetable 60 | // that corresponds to virtual address va. If alloc!=0, 61 | // create any required page-table pages. 62 | // 63 | // The risc-v Sv39 scheme has three levels of page-table 64 | // pages. A page-table page contains 512 64-bit PTEs. 65 | // A 64-bit virtual address is split into five fields: 66 | // 39..63 -- must be zero. 67 | // 30..38 -- 9 bits of level-2 index. 68 | // 21..29 -- 9 bits of level-1 index. 69 | // 12..20 -- 9 bits of level-0 index. 70 | // 0..11 -- 12 bits of byte offset within the page. 71 | pte_t * 72 | walk(pagetable_t pagetable, uint64 va, int alloc) 73 | { 74 | if(va >= MAXVA) 75 | panic("walk"); 76 | 77 | for(int level = 2; level > 0; level--) { 78 | pte_t *pte = &pagetable[PX(level, va)]; 79 | if(*pte & PTE_V) { 80 | pagetable = (pagetable_t)PTE2PA(*pte); 81 | } else { 82 | if(!alloc || (pagetable = (pde_t*)kalloc()) == 0) 83 | return 0; 84 | memset(pagetable, 0, PGSIZE); 85 | *pte = PA2PTE(pagetable) | PTE_V; 86 | } 87 | } 88 | return &pagetable[PX(0, va)]; 89 | } 90 | 91 | // Look up a virtual address, return the physical address, 92 | // or 0 if not mapped. 93 | // Can only be used to look up user pages. 94 | uint64 95 | walkaddr(pagetable_t pagetable, uint64 va) 96 | { 97 | pte_t *pte; 98 | uint64 pa; 99 | 100 | if(va >= MAXVA) 101 | return 0; 102 | 103 | pte = walk(pagetable, va, 0); 104 | if(pte == 0) 105 | return 0; 106 | if((*pte & PTE_V) == 0) 107 | return 0; 108 | if((*pte & PTE_U) == 0) 109 | return 0; 110 | pa = PTE2PA(*pte); 111 | return pa; 112 | } 113 | 114 | // add a mapping to the kernel page table. 115 | // only used when booting. 116 | // does not flush TLB or enable paging. 117 | void 118 | kvmmap(uint64 va, uint64 pa, uint64 sz, int perm) 119 | { 120 | if(mappages(kernel_pagetable, va, sz, pa, perm) != 0) 121 | panic("kvmmap"); 122 | } 123 | 124 | // translate a kernel virtual address to 125 | // a physical address. only needed for 126 | // addresses on the stack. 127 | // assumes va is page aligned. 128 | uint64 129 | kvmpa(uint64 va) 130 | { 131 | uint64 off = va % PGSIZE; 132 | pte_t *pte; 133 | uint64 pa; 134 | 135 | pte = walk(kernel_pagetable, va, 0); 136 | if(pte == 0) 137 | panic("kvmpa"); 138 | if((*pte & PTE_V) == 0) 139 | panic("kvmpa"); 140 | pa = PTE2PA(*pte); 141 | return pa+off; 142 | } 143 | 144 | // Create PTEs for virtual addresses starting at va that refer to 145 | // physical addresses starting at pa. va and size might not 146 | // be page-aligned. Returns 0 on success, -1 if walk() couldn't 147 | // allocate a needed page-table page. 148 | int 149 | mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) 150 | { 151 | uint64 a, last; 152 | pte_t *pte; 153 | 154 | a = PGROUNDDOWN(va); 155 | last = PGROUNDDOWN(va + size - 1); 156 | for(;;){ 157 | if((pte = walk(pagetable, a, 1)) == 0) 158 | return -1; 159 | if(*pte & PTE_V) 160 | panic("remap"); 161 | *pte = PA2PTE(pa) | perm | PTE_V; 162 | if(a == last) 163 | break; 164 | a += PGSIZE; 165 | pa += PGSIZE; 166 | } 167 | return 0; 168 | } 169 | 170 | // Remove npages of mappings starting from va. va must be 171 | // page-aligned. The mappings must exist. 172 | // Optionally free the physical memory. 173 | void 174 | uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) 175 | { 176 | uint64 a; 177 | pte_t *pte; 178 | 179 | if((va % PGSIZE) != 0) 180 | panic("uvmunmap: not aligned"); 181 | 182 | for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ 183 | if((pte = walk(pagetable, a, 0)) == 0) 184 | panic("uvmunmap: walk"); 185 | if((*pte & PTE_V) == 0) 186 | panic("uvmunmap: not mapped"); 187 | if(PTE_FLAGS(*pte) == PTE_V) 188 | panic("uvmunmap: not a leaf"); 189 | if(do_free){ 190 | uint64 pa = PTE2PA(*pte); 191 | kfree((void*)pa); 192 | } 193 | *pte = 0; 194 | } 195 | } 196 | 197 | // create an empty user page table. 198 | // returns 0 if out of memory. 199 | pagetable_t 200 | uvmcreate() 201 | { 202 | pagetable_t pagetable; 203 | pagetable = (pagetable_t) kalloc(); 204 | if(pagetable == 0) 205 | return 0; 206 | memset(pagetable, 0, PGSIZE); 207 | return pagetable; 208 | } 209 | 210 | // Load the user initcode into address 0 of pagetable, 211 | // for the very first process. 212 | // sz must be less than a page. 213 | void 214 | uvminit(pagetable_t pagetable, uchar *src, uint sz) 215 | { 216 | char *mem; 217 | 218 | if(sz >= PGSIZE) 219 | panic("inituvm: more than a page"); 220 | mem = kalloc(); 221 | memset(mem, 0, PGSIZE); 222 | mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); 223 | memmove(mem, src, sz); 224 | } 225 | 226 | // Allocate PTEs and physical memory to grow process from oldsz to 227 | // newsz, which need not be page aligned. Returns new size or 0 on error. 228 | uint64 229 | uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) 230 | { 231 | char *mem; 232 | uint64 a; 233 | 234 | if(newsz < oldsz) 235 | return oldsz; 236 | 237 | oldsz = PGROUNDUP(oldsz); 238 | for(a = oldsz; a < newsz; a += PGSIZE){ 239 | mem = kalloc(); 240 | if(mem == 0){ 241 | uvmdealloc(pagetable, a, oldsz); 242 | return 0; 243 | } 244 | memset(mem, 0, PGSIZE); 245 | if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){ 246 | kfree(mem); 247 | uvmdealloc(pagetable, a, oldsz); 248 | return 0; 249 | } 250 | } 251 | return newsz; 252 | } 253 | 254 | // Deallocate user pages to bring the process size from oldsz to 255 | // newsz. oldsz and newsz need not be page-aligned, nor does newsz 256 | // need to be less than oldsz. oldsz can be larger than the actual 257 | // process size. Returns the new process size. 258 | uint64 259 | uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) 260 | { 261 | if(newsz >= oldsz) 262 | return oldsz; 263 | 264 | if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){ 265 | int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE; 266 | uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1); 267 | } 268 | 269 | return newsz; 270 | } 271 | 272 | // Recursively free page-table pages. 273 | // All leaf mappings must already have been removed. 274 | void 275 | freewalk(pagetable_t pagetable) 276 | { 277 | // there are 2^9 = 512 PTEs in a page table. 278 | for(int i = 0; i < 512; i++){ 279 | pte_t pte = pagetable[i]; 280 | if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ 281 | // this PTE points to a lower-level page table. 282 | uint64 child = PTE2PA(pte); 283 | freewalk((pagetable_t)child); 284 | pagetable[i] = 0; 285 | } else if(pte & PTE_V){ 286 | panic("freewalk: leaf"); 287 | } 288 | } 289 | kfree((void*)pagetable); 290 | } 291 | 292 | // Free user memory pages, 293 | // then free page-table pages. 294 | void 295 | uvmfree(pagetable_t pagetable, uint64 sz) 296 | { 297 | if(sz > 0) 298 | uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1); 299 | freewalk(pagetable); 300 | } 301 | 302 | // Given a parent process's page table, copy 303 | // its memory into a child's page table. 304 | // Copies both the page table and the 305 | // physical memory. 306 | // returns 0 on success, -1 on failure. 307 | // frees any allocated pages on failure. 308 | int 309 | uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) 310 | { 311 | pte_t *pte; 312 | uint64 pa, i; 313 | uint flags; 314 | char *mem; 315 | 316 | for(i = 0; i < sz; i += PGSIZE){ 317 | if((pte = walk(old, i, 0)) == 0) 318 | panic("uvmcopy: pte should exist"); 319 | if((*pte & PTE_V) == 0) 320 | panic("uvmcopy: page not present"); 321 | pa = PTE2PA(*pte); 322 | flags = PTE_FLAGS(*pte); 323 | if((mem = kalloc()) == 0) 324 | goto err; 325 | memmove(mem, (char*)pa, PGSIZE); 326 | if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){ 327 | kfree(mem); 328 | goto err; 329 | } 330 | } 331 | return 0; 332 | 333 | err: 334 | uvmunmap(new, 0, i / PGSIZE, 1); 335 | return -1; 336 | } 337 | 338 | // mark a PTE invalid for user access. 339 | // used by exec for the user stack guard page. 340 | void 341 | uvmclear(pagetable_t pagetable, uint64 va) 342 | { 343 | pte_t *pte; 344 | 345 | pte = walk(pagetable, va, 0); 346 | if(pte == 0) 347 | panic("uvmclear"); 348 | *pte &= ~PTE_U; 349 | } 350 | 351 | // Copy from kernel to user. 352 | // Copy len bytes from src to virtual address dstva in a given page table. 353 | // Return 0 on success, -1 on error. 354 | int 355 | copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) 356 | { 357 | uint64 n, va0, pa0; 358 | 359 | while(len > 0){ 360 | va0 = PGROUNDDOWN(dstva); 361 | pa0 = walkaddr(pagetable, va0); 362 | if(pa0 == 0) 363 | return -1; 364 | n = PGSIZE - (dstva - va0); 365 | if(n > len) 366 | n = len; 367 | memmove((void *)(pa0 + (dstva - va0)), src, n); 368 | 369 | len -= n; 370 | src += n; 371 | dstva = va0 + PGSIZE; 372 | } 373 | return 0; 374 | } 375 | 376 | // Copy from user to kernel. 377 | // Copy len bytes to dst from virtual address srcva in a given page table. 378 | // Return 0 on success, -1 on error. 379 | int 380 | copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) 381 | { 382 | uint64 n, va0, pa0; 383 | 384 | while(len > 0){ 385 | va0 = PGROUNDDOWN(srcva); 386 | pa0 = walkaddr(pagetable, va0); 387 | if(pa0 == 0) 388 | return -1; 389 | n = PGSIZE - (srcva - va0); 390 | if(n > len) 391 | n = len; 392 | memmove(dst, (void *)(pa0 + (srcva - va0)), n); 393 | 394 | len -= n; 395 | dst += n; 396 | srcva = va0 + PGSIZE; 397 | } 398 | return 0; 399 | } 400 | 401 | // Copy a null-terminated string from user to kernel. 402 | // Copy bytes to dst from virtual address srcva in a given page table, 403 | // until a '\0', or max. 404 | // Return 0 on success, -1 on error. 405 | int 406 | copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) 407 | { 408 | uint64 n, va0, pa0; 409 | int got_null = 0; 410 | 411 | while(got_null == 0 && max > 0){ 412 | va0 = PGROUNDDOWN(srcva); 413 | pa0 = walkaddr(pagetable, va0); 414 | if(pa0 == 0) 415 | return -1; 416 | n = PGSIZE - (srcva - va0); 417 | if(n > max) 418 | n = max; 419 | 420 | char *p = (char *) (pa0 + (srcva - va0)); 421 | while(n > 0){ 422 | if(*p == '\0'){ 423 | *dst = '\0'; 424 | got_null = 1; 425 | break; 426 | } else { 427 | *dst = *p; 428 | } 429 | --n; 430 | --max; 431 | p++; 432 | dst++; 433 | } 434 | 435 | srcva = va0 + PGSIZE; 436 | } 437 | if(got_null){ 438 | return 0; 439 | } else { 440 | return -1; 441 | } 442 | } 443 | -------------------------------------------------------------------------------- /mkfs/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 "kernel/types.h" 10 | #include "kernel/fs.h" 11 | #include "kernel/stat.h" 12 | #include "kernel/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.magic = FSMAGIC; 98 | sb.size = xint(FSSIZE); 99 | sb.nblocks = xint(nblocks); 100 | sb.ninodes = xint(NINODES); 101 | sb.nlog = xint(nlog); 102 | sb.logstart = xint(2); 103 | sb.inodestart = xint(2+nlog); 104 | sb.bmapstart = xint(2+nlog+ninodeblocks); 105 | 106 | printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", 107 | nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); 108 | 109 | freeblock = nmeta; // the first free block that we can allocate 110 | 111 | for(i = 0; i < FSSIZE; i++) 112 | wsect(i, zeroes); 113 | 114 | memset(buf, 0, sizeof(buf)); 115 | memmove(buf, &sb, sizeof(sb)); 116 | wsect(1, buf); 117 | 118 | rootino = ialloc(T_DIR); 119 | assert(rootino == ROOTINO); 120 | 121 | bzero(&de, sizeof(de)); 122 | de.inum = xshort(rootino); 123 | strcpy(de.name, "."); 124 | iappend(rootino, &de, sizeof(de)); 125 | 126 | bzero(&de, sizeof(de)); 127 | de.inum = xshort(rootino); 128 | strcpy(de.name, ".."); 129 | iappend(rootino, &de, sizeof(de)); 130 | 131 | for(i = 2; i < argc; i++){ 132 | // get rid of "user/" 133 | char *shortname; 134 | if(strncmp(argv[i], "user/", 5) == 0) 135 | shortname = argv[i] + 5; 136 | else 137 | shortname = argv[i]; 138 | 139 | assert(index(shortname, '/') == 0); 140 | 141 | if((fd = open(argv[i], 0)) < 0){ 142 | perror(argv[i]); 143 | exit(1); 144 | } 145 | 146 | // Skip leading _ in name when writing to file system. 147 | // The binaries are named _rm, _cat, etc. to keep the 148 | // build operating system from trying to execute them 149 | // in place of system binaries like rm and cat. 150 | if(shortname[0] == '_') 151 | shortname += 1; 152 | 153 | inum = ialloc(T_FILE); 154 | 155 | bzero(&de, sizeof(de)); 156 | de.inum = xshort(inum); 157 | strncpy(de.name, shortname, DIRSIZ); 158 | iappend(rootino, &de, sizeof(de)); 159 | 160 | while((cc = read(fd, buf, sizeof(buf))) > 0) 161 | iappend(inum, buf, cc); 162 | 163 | close(fd); 164 | } 165 | 166 | // fix size of root inode dir 167 | rinode(rootino, &din); 168 | off = xint(din.size); 169 | off = ((off/BSIZE) + 1) * BSIZE; 170 | din.size = xint(off); 171 | winode(rootino, &din); 172 | 173 | balloc(freeblock); 174 | 175 | exit(0); 176 | } 177 | 178 | void 179 | wsect(uint sec, void *buf) 180 | { 181 | if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ 182 | perror("lseek"); 183 | exit(1); 184 | } 185 | if(write(fsfd, buf, BSIZE) != BSIZE){ 186 | perror("write"); 187 | exit(1); 188 | } 189 | } 190 | 191 | void 192 | winode(uint inum, struct dinode *ip) 193 | { 194 | char buf[BSIZE]; 195 | uint bn; 196 | struct dinode *dip; 197 | 198 | bn = IBLOCK(inum, sb); 199 | rsect(bn, buf); 200 | dip = ((struct dinode*)buf) + (inum % IPB); 201 | *dip = *ip; 202 | wsect(bn, buf); 203 | } 204 | 205 | void 206 | rinode(uint inum, struct dinode *ip) 207 | { 208 | char buf[BSIZE]; 209 | uint bn; 210 | struct dinode *dip; 211 | 212 | bn = IBLOCK(inum, sb); 213 | rsect(bn, buf); 214 | dip = ((struct dinode*)buf) + (inum % IPB); 215 | *ip = *dip; 216 | } 217 | 218 | void 219 | rsect(uint sec, void *buf) 220 | { 221 | if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ 222 | perror("lseek"); 223 | exit(1); 224 | } 225 | if(read(fsfd, buf, BSIZE) != BSIZE){ 226 | perror("read"); 227 | exit(1); 228 | } 229 | } 230 | 231 | uint 232 | ialloc(ushort type) 233 | { 234 | uint inum = freeinode++; 235 | struct dinode din; 236 | 237 | bzero(&din, sizeof(din)); 238 | din.type = xshort(type); 239 | din.nlink = xshort(1); 240 | din.size = xint(0); 241 | winode(inum, &din); 242 | return inum; 243 | } 244 | 245 | void 246 | balloc(int used) 247 | { 248 | uchar buf[BSIZE]; 249 | int i; 250 | 251 | printf("balloc: first %d blocks have been allocated\n", used); 252 | assert(used < BSIZE*8); 253 | bzero(buf, BSIZE); 254 | for(i = 0; i < used; i++){ 255 | buf[i/8] = buf[i/8] | (0x1 << (i%8)); 256 | } 257 | printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); 258 | wsect(sb.bmapstart, buf); 259 | } 260 | 261 | #define min(a, b) ((a) < (b) ? (a) : (b)) 262 | 263 | void 264 | iappend(uint inum, void *xp, int n) 265 | { 266 | char *p = (char*)xp; 267 | uint fbn, off, n1; 268 | struct dinode din; 269 | char buf[BSIZE]; 270 | uint indirect[NINDIRECT]; 271 | uint x; 272 | 273 | rinode(inum, &din); 274 | off = xint(din.size); 275 | // printf("append inum %d at off %d sz %d\n", inum, off, n); 276 | while(n > 0){ 277 | fbn = off / BSIZE; 278 | assert(fbn < MAXFILE); 279 | if(fbn < NDIRECT){ 280 | if(xint(din.addrs[fbn]) == 0){ 281 | din.addrs[fbn] = xint(freeblock++); 282 | } 283 | x = xint(din.addrs[fbn]); 284 | } else { 285 | if(xint(din.addrs[NDIRECT]) == 0){ 286 | din.addrs[NDIRECT] = xint(freeblock++); 287 | } 288 | rsect(xint(din.addrs[NDIRECT]), (char*)indirect); 289 | if(indirect[fbn - NDIRECT] == 0){ 290 | indirect[fbn - NDIRECT] = xint(freeblock++); 291 | wsect(xint(din.addrs[NDIRECT]), (char*)indirect); 292 | } 293 | x = xint(indirect[fbn-NDIRECT]); 294 | } 295 | n1 = min(n, (fbn + 1) * BSIZE - off); 296 | rsect(x, buf); 297 | bcopy(p, buf + off - (fbn * BSIZE), n1); 298 | wsect(x, buf); 299 | n -= n1; 300 | off += n1; 301 | p += n1; 302 | } 303 | din.size = xint(off); 304 | winode(inum, &din); 305 | } 306 | -------------------------------------------------------------------------------- /time.txt: -------------------------------------------------------------------------------- 1 | 5 2 | -------------------------------------------------------------------------------- /user/cat.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/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 | fprintf(2, "cat: write error\n"); 15 | exit(1); 16 | } 17 | } 18 | if(n < 0){ 19 | fprintf(2, "cat: read error\n"); 20 | exit(1); 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(0); 32 | } 33 | 34 | for(i = 1; i < argc; i++){ 35 | if((fd = open(argv[i], 0)) < 0){ 36 | fprintf(2, "cat: cannot open %s\n", argv[i]); 37 | exit(1); 38 | } 39 | cat(fd); 40 | close(fd); 41 | } 42 | exit(0); 43 | } 44 | -------------------------------------------------------------------------------- /user/echo.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | for(i = 1; i < argc; i++){ 11 | write(1, argv[i], strlen(argv[i])); 12 | if(i + 1 < argc){ 13 | write(1, " ", 1); 14 | } else { 15 | write(1, "\n", 1); 16 | } 17 | } 18 | exit(0); 19 | } 20 | -------------------------------------------------------------------------------- /user/find.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/fcntl.h" 3 | #include "kernel/fs.h" 4 | #include "kernel/stat.h" 5 | #include "user/user.h" 6 | 7 | /* retrieve the filename from whole path */ 8 | char *basename(char *pathname) { 9 | char *prev = 0; 10 | char *curr = strchr(pathname, '/'); 11 | while (curr != 0) { 12 | prev = curr; 13 | curr = strchr(curr + 1, '/'); 14 | } 15 | return prev; 16 | } 17 | 18 | /* recursive */ 19 | void find(char *curr_path, char *target) { 20 | char buf[512], *p; 21 | int fd; 22 | struct dirent de; 23 | struct stat st; 24 | if ((fd = open(curr_path, O_RDONLY)) < 0) { 25 | fprintf(2, "find: cannot open %s\n", curr_path); 26 | return; 27 | } 28 | 29 | if (fstat(fd, &st) < 0) { 30 | fprintf(2, "find: cannot stat %s\n", curr_path); 31 | close(fd); 32 | return; 33 | } 34 | 35 | switch (st.type) { 36 | 37 | case T_FILE: 38 | char *f_name = basename(curr_path); 39 | int match = 1; 40 | if (f_name == 0 || strcmp(f_name + 1, target) != 0) { 41 | match = 0; 42 | } 43 | if (match) 44 | printf("%s\n", curr_path); 45 | close(fd); 46 | break; 47 | 48 | case T_DIR: 49 | // make the next level pathname 50 | memset(buf, 0, sizeof(buf)); 51 | uint curr_path_len = strlen(curr_path); 52 | memcpy(buf, curr_path, curr_path_len); 53 | buf[curr_path_len] = '/'; 54 | p = buf + curr_path_len + 1; 55 | while (read(fd, &de, sizeof(de)) == sizeof(de)) { 56 | if (de.inum == 0 || strcmp(de.name, ".") == 0 || 57 | strcmp(de.name, "..") == 0) 58 | continue; 59 | memcpy(p, de.name, DIRSIZ); 60 | p[DIRSIZ] = 0; 61 | find(buf, target); // recurse 62 | } 63 | close(fd); 64 | break; 65 | } 66 | } 67 | 68 | int main(int argc, char *argv[]) { 69 | if (argc != 3) { 70 | fprintf(2, "usage: find [directory] [target filename]\n"); 71 | exit(1); 72 | } 73 | find(argv[1], argv[2]); 74 | exit(0); 75 | } 76 | -------------------------------------------------------------------------------- /user/forktest.c: -------------------------------------------------------------------------------- 1 | // Test that fork fails gracefully. 2 | // Tiny executable so that the limit can be filling the proc table. 3 | 4 | #include "kernel/types.h" 5 | #include "kernel/stat.h" 6 | #include "user/user.h" 7 | 8 | #define N 1000 9 | 10 | void 11 | print(const char *s) 12 | { 13 | write(1, s, strlen(s)); 14 | } 15 | 16 | void 17 | forktest(void) 18 | { 19 | int n, pid; 20 | 21 | print("fork test\n"); 22 | 23 | for(n=0; n 0; n--){ 37 | if(wait(0) < 0){ 38 | print("wait stopped early\n"); 39 | exit(1); 40 | } 41 | } 42 | 43 | if(wait(0) != -1){ 44 | print("wait got too many\n"); 45 | exit(1); 46 | } 47 | 48 | print("fork test OK\n"); 49 | } 50 | 51 | int 52 | main(void) 53 | { 54 | forktest(); 55 | exit(0); 56 | } 57 | -------------------------------------------------------------------------------- /user/grep.c: -------------------------------------------------------------------------------- 1 | // Simple grep. Only supports ^ . * $ operators. 2 | 3 | #include "kernel/types.h" 4 | #include "kernel/stat.h" 5 | #include "user/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(m > 0){ 30 | m -= p - buf; 31 | memmove(buf, p, m); 32 | } 33 | } 34 | } 35 | 36 | int 37 | main(int argc, char *argv[]) 38 | { 39 | int fd, i; 40 | char *pattern; 41 | 42 | if(argc <= 1){ 43 | fprintf(2, "usage: grep pattern [file ...]\n"); 44 | exit(1); 45 | } 46 | pattern = argv[1]; 47 | 48 | if(argc <= 2){ 49 | grep(pattern, 0); 50 | exit(0); 51 | } 52 | 53 | for(i = 2; i < argc; i++){ 54 | if((fd = open(argv[i], 0)) < 0){ 55 | printf("grep: cannot open %s\n", argv[i]); 56 | exit(1); 57 | } 58 | grep(pattern, fd); 59 | close(fd); 60 | } 61 | exit(0); 62 | } 63 | 64 | // Regexp matcher from Kernighan & Pike, 65 | // The Practice of Programming, Chapter 9. 66 | 67 | int matchhere(char*, char*); 68 | int matchstar(int, char*, char*); 69 | 70 | int 71 | match(char *re, char *text) 72 | { 73 | if(re[0] == '^') 74 | return matchhere(re+1, text); 75 | do{ // must look at empty string 76 | if(matchhere(re, text)) 77 | return 1; 78 | }while(*text++ != '\0'); 79 | return 0; 80 | } 81 | 82 | // matchhere: search for re at beginning of text 83 | int matchhere(char *re, char *text) 84 | { 85 | if(re[0] == '\0') 86 | return 1; 87 | if(re[1] == '*') 88 | return matchstar(re[0], re+2, text); 89 | if(re[0] == '$' && re[1] == '\0') 90 | return *text == '\0'; 91 | if(*text!='\0' && (re[0]=='.' || re[0]==*text)) 92 | return matchhere(re+1, text+1); 93 | return 0; 94 | } 95 | 96 | // matchstar: search for c*re at beginning of text 97 | int matchstar(int c, char *re, char *text) 98 | { 99 | do{ // a * matches zero or more instances 100 | if(matchhere(re, text)) 101 | return 1; 102 | }while(*text!='\0' && (*text++==c || c=='.')); 103 | return 0; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /user/grind.c: -------------------------------------------------------------------------------- 1 | // 2 | // run random system calls in parallel forever. 3 | // 4 | 5 | #include "kernel/param.h" 6 | #include "kernel/types.h" 7 | #include "kernel/stat.h" 8 | #include "user/user.h" 9 | #include "kernel/fs.h" 10 | #include "kernel/fcntl.h" 11 | #include "kernel/syscall.h" 12 | #include "kernel/memlayout.h" 13 | #include "kernel/riscv.h" 14 | 15 | // from FreeBSD. 16 | int 17 | do_rand(unsigned long *ctx) 18 | { 19 | /* 20 | * Compute x = (7^5 * x) mod (2^31 - 1) 21 | * without overflowing 31 bits: 22 | * (2^31 - 1) = 127773 * (7^5) + 2836 23 | * From "Random number generators: good ones are hard to find", 24 | * Park and Miller, Communications of the ACM, vol. 31, no. 10, 25 | * October 1988, p. 1195. 26 | */ 27 | long hi, lo, x; 28 | 29 | /* Transform to [1, 0x7ffffffe] range. */ 30 | x = (*ctx % 0x7ffffffe) + 1; 31 | hi = x / 127773; 32 | lo = x % 127773; 33 | x = 16807 * lo - 2836 * hi; 34 | if (x < 0) 35 | x += 0x7fffffff; 36 | /* Transform to [0, 0x7ffffffd] range. */ 37 | x--; 38 | *ctx = x; 39 | return (x); 40 | } 41 | 42 | unsigned long rand_next = 1; 43 | 44 | int 45 | rand(void) 46 | { 47 | return (do_rand(&rand_next)); 48 | } 49 | 50 | void 51 | go(int which_child) 52 | { 53 | int fd = -1; 54 | static char buf[999]; 55 | char *break0 = sbrk(0); 56 | uint64 iters = 0; 57 | 58 | mkdir("grindir"); 59 | if(chdir("grindir") != 0){ 60 | printf("chdir grindir failed\n"); 61 | exit(1); 62 | } 63 | chdir("/"); 64 | 65 | while(1){ 66 | iters++; 67 | if((iters % 500) == 0) 68 | write(1, which_child?"B":"A", 1); 69 | int what = rand() % 23; 70 | if(what == 1){ 71 | close(open("grindir/../a", O_CREATE|O_RDWR)); 72 | } else if(what == 2){ 73 | close(open("grindir/../grindir/../b", O_CREATE|O_RDWR)); 74 | } else if(what == 3){ 75 | unlink("grindir/../a"); 76 | } else if(what == 4){ 77 | if(chdir("grindir") != 0){ 78 | printf("chdir grindir failed\n"); 79 | exit(1); 80 | } 81 | unlink("../b"); 82 | chdir("/"); 83 | } else if(what == 5){ 84 | close(fd); 85 | fd = open("/grindir/../a", O_CREATE|O_RDWR); 86 | } else if(what == 6){ 87 | close(fd); 88 | fd = open("/./grindir/./../b", O_CREATE|O_RDWR); 89 | } else if(what == 7){ 90 | write(fd, buf, sizeof(buf)); 91 | } else if(what == 8){ 92 | read(fd, buf, sizeof(buf)); 93 | } else if(what == 9){ 94 | mkdir("grindir/../a"); 95 | close(open("a/../a/./a", O_CREATE|O_RDWR)); 96 | unlink("a/a"); 97 | } else if(what == 10){ 98 | mkdir("/../b"); 99 | close(open("grindir/../b/b", O_CREATE|O_RDWR)); 100 | unlink("b/b"); 101 | } else if(what == 11){ 102 | unlink("b"); 103 | link("../grindir/./../a", "../b"); 104 | } else if(what == 12){ 105 | unlink("../grindir/../a"); 106 | link(".././b", "/grindir/../a"); 107 | } else if(what == 13){ 108 | int pid = fork(); 109 | if(pid == 0){ 110 | exit(0); 111 | } else if(pid < 0){ 112 | printf("grind: fork failed\n"); 113 | exit(1); 114 | } 115 | wait(0); 116 | } else if(what == 14){ 117 | int pid = fork(); 118 | if(pid == 0){ 119 | fork(); 120 | fork(); 121 | exit(0); 122 | } else if(pid < 0){ 123 | printf("grind: fork failed\n"); 124 | exit(1); 125 | } 126 | wait(0); 127 | } else if(what == 15){ 128 | sbrk(6011); 129 | } else if(what == 16){ 130 | if(sbrk(0) > break0) 131 | sbrk(-(sbrk(0) - break0)); 132 | } else if(what == 17){ 133 | int pid = fork(); 134 | if(pid == 0){ 135 | close(open("a", O_CREATE|O_RDWR)); 136 | exit(0); 137 | } else if(pid < 0){ 138 | printf("grind: fork failed\n"); 139 | exit(1); 140 | } 141 | if(chdir("../grindir/..") != 0){ 142 | printf("chdir failed\n"); 143 | exit(1); 144 | } 145 | kill(pid); 146 | wait(0); 147 | } else if(what == 18){ 148 | int pid = fork(); 149 | if(pid == 0){ 150 | kill(getpid()); 151 | exit(0); 152 | } else if(pid < 0){ 153 | printf("grind: fork failed\n"); 154 | exit(1); 155 | } 156 | wait(0); 157 | } else if(what == 19){ 158 | int fds[2]; 159 | if(pipe(fds) < 0){ 160 | printf("grind: pipe failed\n"); 161 | exit(1); 162 | } 163 | int pid = fork(); 164 | if(pid == 0){ 165 | fork(); 166 | fork(); 167 | if(write(fds[1], "x", 1) != 1) 168 | printf("grind: pipe write failed\n"); 169 | char c; 170 | if(read(fds[0], &c, 1) != 1) 171 | printf("grind: pipe read failed\n"); 172 | exit(0); 173 | } else if(pid < 0){ 174 | printf("grind: fork failed\n"); 175 | exit(1); 176 | } 177 | close(fds[0]); 178 | close(fds[1]); 179 | wait(0); 180 | } else if(what == 20){ 181 | int pid = fork(); 182 | if(pid == 0){ 183 | unlink("a"); 184 | mkdir("a"); 185 | chdir("a"); 186 | unlink("../a"); 187 | fd = open("x", O_CREATE|O_RDWR); 188 | unlink("x"); 189 | exit(0); 190 | } else if(pid < 0){ 191 | printf("fork failed\n"); 192 | exit(1); 193 | } 194 | wait(0); 195 | } else if(what == 21){ 196 | unlink("c"); 197 | // should always succeed. check that there are free i-nodes, 198 | // file descriptors, blocks. 199 | int fd1 = open("c", O_CREATE|O_RDWR); 200 | if(fd1 < 0){ 201 | printf("create c failed\n"); 202 | exit(1); 203 | } 204 | if(write(fd1, "x", 1) != 1){ 205 | printf("write c failed\n"); 206 | exit(1); 207 | } 208 | struct stat st; 209 | if(fstat(fd1, &st) != 0){ 210 | printf("fstat failed\n"); 211 | exit(1); 212 | } 213 | if(st.size != 1){ 214 | printf("fstat reports wrong size %d\n", (int)st.size); 215 | exit(1); 216 | } 217 | if(st.ino > 200){ 218 | printf("fstat reports crazy i-number %d\n", st.ino); 219 | exit(1); 220 | } 221 | close(fd1); 222 | unlink("c"); 223 | } else if(what == 22){ 224 | // echo hi | cat 225 | int aa[2], bb[2]; 226 | if(pipe(aa) < 0){ 227 | fprintf(2, "pipe failed\n"); 228 | exit(1); 229 | } 230 | if(pipe(bb) < 0){ 231 | fprintf(2, "pipe failed\n"); 232 | exit(1); 233 | } 234 | int pid1 = fork(); 235 | if(pid1 == 0){ 236 | close(bb[0]); 237 | close(bb[1]); 238 | close(aa[0]); 239 | close(1); 240 | if(dup(aa[1]) != 1){ 241 | fprintf(2, "dup failed\n"); 242 | exit(1); 243 | } 244 | close(aa[1]); 245 | char *args[3] = { "echo", "hi", 0 }; 246 | exec("grindir/../echo", args); 247 | fprintf(2, "echo: not found\n"); 248 | exit(2); 249 | } else if(pid1 < 0){ 250 | fprintf(2, "fork failed\n"); 251 | exit(3); 252 | } 253 | int pid2 = fork(); 254 | if(pid2 == 0){ 255 | close(aa[1]); 256 | close(bb[0]); 257 | close(0); 258 | if(dup(aa[0]) != 0){ 259 | fprintf(2, "dup failed\n"); 260 | exit(4); 261 | } 262 | close(aa[0]); 263 | close(1); 264 | if(dup(bb[1]) != 1){ 265 | fprintf(2, "dup failed\n"); 266 | exit(5); 267 | } 268 | close(bb[1]); 269 | char *args[2] = { "cat", 0 }; 270 | exec("/cat", args); 271 | fprintf(2, "cat: not found\n"); 272 | exit(6); 273 | } else if(pid2 < 0){ 274 | fprintf(2, "fork failed\n"); 275 | exit(7); 276 | } 277 | close(aa[0]); 278 | close(aa[1]); 279 | close(bb[1]); 280 | char buf[3] = { 0, 0, 0 }; 281 | read(bb[0], buf+0, 1); 282 | read(bb[0], buf+1, 1); 283 | close(bb[0]); 284 | int st1, st2; 285 | wait(&st1); 286 | wait(&st2); 287 | if(st1 != 0 || st2 != 0 || strcmp(buf, "hi") != 0){ 288 | printf("exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); 289 | exit(1); 290 | } 291 | } 292 | } 293 | } 294 | 295 | void 296 | iter() 297 | { 298 | unlink("a"); 299 | unlink("b"); 300 | 301 | int pid1 = fork(); 302 | if(pid1 < 0){ 303 | printf("grind: fork failed\n"); 304 | exit(1); 305 | } 306 | if(pid1 == 0){ 307 | rand_next = 31; 308 | go(0); 309 | exit(0); 310 | } 311 | 312 | int pid2 = fork(); 313 | if(pid2 < 0){ 314 | printf("grind: fork failed\n"); 315 | exit(1); 316 | } 317 | if(pid2 == 0){ 318 | rand_next = 7177; 319 | go(1); 320 | exit(0); 321 | } 322 | 323 | int st1 = -1; 324 | wait(&st1); 325 | if(st1 != 0){ 326 | kill(pid1); 327 | kill(pid2); 328 | } 329 | int st2 = -1; 330 | wait(&st2); 331 | 332 | exit(0); 333 | } 334 | 335 | int 336 | main() 337 | { 338 | while(1){ 339 | int pid = fork(); 340 | if(pid == 0){ 341 | iter(); 342 | exit(0); 343 | } 344 | if(pid > 0){ 345 | wait(0); 346 | } 347 | sleep(20); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /user/init.c: -------------------------------------------------------------------------------- 1 | // init: The initial user-level program 2 | 3 | #include "kernel/types.h" 4 | #include "kernel/stat.h" 5 | #include "kernel/spinlock.h" 6 | #include "kernel/sleeplock.h" 7 | #include "kernel/fs.h" 8 | #include "kernel/file.h" 9 | #include "user/user.h" 10 | #include "kernel/fcntl.h" 11 | 12 | char *argv[] = { "sh", 0 }; 13 | 14 | int 15 | main(void) 16 | { 17 | int pid, wpid; 18 | 19 | if(open("console", O_RDWR) < 0){ 20 | mknod("console", CONSOLE, 0); 21 | open("console", O_RDWR); 22 | } 23 | dup(0); // stdout 24 | dup(0); // stderr 25 | 26 | for(;;){ 27 | printf("init: starting sh\n"); 28 | pid = fork(); 29 | if(pid < 0){ 30 | printf("init: fork failed\n"); 31 | exit(1); 32 | } 33 | if(pid == 0){ 34 | exec("sh", argv); 35 | printf("init: exec sh failed\n"); 36 | exit(1); 37 | } 38 | 39 | for(;;){ 40 | // this call to wait() returns if the shell exits, 41 | // or if a parentless process exits. 42 | wpid = wait((int *) 0); 43 | if(wpid == pid){ 44 | // the shell exited; restart it. 45 | break; 46 | } else if(wpid < 0){ 47 | printf("init: wait returned an error\n"); 48 | exit(1); 49 | } else { 50 | // it was a parentless process; do nothing. 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /user/initcode.S: -------------------------------------------------------------------------------- 1 | # Initial process that execs /init. 2 | # This code runs in user space. 3 | 4 | #include "syscall.h" 5 | 6 | # exec(init, argv) 7 | .globl start 8 | start: 9 | la a0, init 10 | la a1, argv 11 | li a7, SYS_exec 12 | ecall 13 | 14 | # for(;;) exit(); 15 | exit: 16 | li a7, SYS_exit 17 | ecall 18 | jal exit 19 | 20 | # char init[] = "/init\0"; 21 | init: 22 | .string "/init\0" 23 | 24 | # char *argv[] = { init, 0 }; 25 | .p2align 2 26 | argv: 27 | .long init 28 | .long 0 29 | -------------------------------------------------------------------------------- /user/kill.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/user.h" 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int i; 9 | 10 | if(argc < 2){ 11 | fprintf(2, "usage: kill pid...\n"); 12 | exit(1); 13 | } 14 | for(i=1; i= 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 | fprintf(2, "ls: cannot open %s\n", path); 35 | return; 36 | } 37 | 38 | if(fstat(fd, &st) < 0){ 39 | fprintf(2, "ls: cannot stat %s\n", path); 40 | close(fd); 41 | return; 42 | } 43 | 44 | switch(st.type){ 45 | case T_FILE: 46 | printf("%s %d %d %l\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("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("ls: cannot stat %s\n", buf); 64 | continue; 65 | } 66 | printf("%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(0); 81 | } 82 | for(i=1; i 6 | 7 | static char digits[] = "0123456789ABCDEF"; 8 | 9 | static void 10 | putc(int fd, char c) 11 | { 12 | write(fd, &c, 1); 13 | } 14 | 15 | static void 16 | printint(int fd, int xx, int base, int sgn) 17 | { 18 | char buf[16]; 19 | int i, neg; 20 | uint x; 21 | 22 | neg = 0; 23 | if(sgn && xx < 0){ 24 | neg = 1; 25 | x = -xx; 26 | } else { 27 | x = xx; 28 | } 29 | 30 | i = 0; 31 | do{ 32 | buf[i++] = digits[x % base]; 33 | }while((x /= base) != 0); 34 | if(neg) 35 | buf[i++] = '-'; 36 | 37 | while(--i >= 0) 38 | putc(fd, buf[i]); 39 | } 40 | 41 | static void 42 | printptr(int fd, uint64 x) { 43 | int i; 44 | putc(fd, '0'); 45 | putc(fd, 'x'); 46 | for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) 47 | putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]); 48 | } 49 | 50 | // Print to the given fd. Only understands %d, %x, %p, %s. 51 | void 52 | vprintf(int fd, const char *fmt, va_list ap) 53 | { 54 | char *s; 55 | int c, i, state; 56 | 57 | state = 0; 58 | for(i = 0; fmt[i]; i++){ 59 | c = fmt[i] & 0xff; 60 | if(state == 0){ 61 | if(c == '%'){ 62 | state = '%'; 63 | } else { 64 | putc(fd, c); 65 | } 66 | } else if(state == '%'){ 67 | if(c == 'd'){ 68 | printint(fd, va_arg(ap, int), 10, 1); 69 | } else if(c == 'l') { 70 | printint(fd, va_arg(ap, uint64), 10, 0); 71 | } else if(c == 'x') { 72 | printint(fd, va_arg(ap, int), 16, 0); 73 | } else if(c == 'p') { 74 | printptr(fd, va_arg(ap, uint64)); 75 | } else if(c == 's'){ 76 | s = va_arg(ap, char*); 77 | if(s == 0) 78 | s = "(null)"; 79 | while(*s != 0){ 80 | putc(fd, *s); 81 | s++; 82 | } 83 | } else if(c == 'c'){ 84 | putc(fd, va_arg(ap, uint)); 85 | } else if(c == '%'){ 86 | putc(fd, c); 87 | } else { 88 | // Unknown % sequence. Print it to draw attention. 89 | putc(fd, '%'); 90 | putc(fd, c); 91 | } 92 | state = 0; 93 | } 94 | } 95 | } 96 | 97 | void 98 | fprintf(int fd, const char *fmt, ...) 99 | { 100 | va_list ap; 101 | 102 | va_start(ap, fmt); 103 | vprintf(fd, fmt, ap); 104 | } 105 | 106 | void 107 | printf(const char *fmt, ...) 108 | { 109 | va_list ap; 110 | 111 | va_start(ap, fmt); 112 | vprintf(1, fmt, ap); 113 | } 114 | -------------------------------------------------------------------------------- /user/rm.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | if(argc < 2){ 11 | fprintf(2, "Usage: rm files...\n"); 12 | exit(1); 13 | } 14 | 15 | for(i = 1; i < argc; i++){ 16 | if(unlink(argv[i]) < 0){ 17 | fprintf(2, "rm: %s failed to delete\n", argv[i]); 18 | break; 19 | } 20 | } 21 | 22 | exit(0); 23 | } 24 | -------------------------------------------------------------------------------- /user/sh.c: -------------------------------------------------------------------------------- 1 | // Shell. 2 | 3 | #include "kernel/types.h" 4 | #include "user/user.h" 5 | #include "kernel/fcntl.h" 6 | 7 | // Parsed command representation 8 | #define EXEC 1 9 | #define REDIR 2 10 | #define PIPE 3 11 | #define LIST 4 12 | #define BACK 5 13 | 14 | #define MAXARGS 10 15 | 16 | struct cmd { 17 | int type; 18 | }; 19 | 20 | struct execcmd { 21 | int type; 22 | char *argv[MAXARGS]; 23 | char *eargv[MAXARGS]; 24 | }; 25 | 26 | struct redircmd { 27 | int type; 28 | struct cmd *cmd; 29 | char *file; 30 | char *efile; 31 | int mode; 32 | int fd; 33 | }; 34 | 35 | struct pipecmd { 36 | int type; 37 | struct cmd *left; 38 | struct cmd *right; 39 | }; 40 | 41 | struct listcmd { 42 | int type; 43 | struct cmd *left; 44 | struct cmd *right; 45 | }; 46 | 47 | struct backcmd { 48 | int type; 49 | struct cmd *cmd; 50 | }; 51 | 52 | int fork1(void); // Fork but panics on failure. 53 | void panic(char*); 54 | struct cmd *parsecmd(char*); 55 | 56 | // Execute cmd. Never returns. 57 | void 58 | runcmd(struct cmd *cmd) 59 | { 60 | int p[2]; 61 | struct backcmd *bcmd; 62 | struct execcmd *ecmd; 63 | struct listcmd *lcmd; 64 | struct pipecmd *pcmd; 65 | struct redircmd *rcmd; 66 | 67 | if(cmd == 0) 68 | exit(1); 69 | 70 | switch(cmd->type){ 71 | default: 72 | panic("runcmd"); 73 | 74 | case EXEC: 75 | ecmd = (struct execcmd*)cmd; 76 | if(ecmd->argv[0] == 0) 77 | exit(1); 78 | exec(ecmd->argv[0], ecmd->argv); 79 | fprintf(2, "exec %s failed\n", ecmd->argv[0]); 80 | break; 81 | 82 | case REDIR: 83 | rcmd = (struct redircmd*)cmd; 84 | close(rcmd->fd); 85 | if(open(rcmd->file, rcmd->mode) < 0){ 86 | fprintf(2, "open %s failed\n", rcmd->file); 87 | exit(1); 88 | } 89 | runcmd(rcmd->cmd); 90 | break; 91 | 92 | case LIST: 93 | lcmd = (struct listcmd*)cmd; 94 | if(fork1() == 0) 95 | runcmd(lcmd->left); 96 | wait(0); 97 | runcmd(lcmd->right); 98 | break; 99 | 100 | case PIPE: 101 | pcmd = (struct pipecmd*)cmd; 102 | if(pipe(p) < 0) 103 | panic("pipe"); 104 | if(fork1() == 0){ 105 | close(1); 106 | dup(p[1]); 107 | close(p[0]); 108 | close(p[1]); 109 | runcmd(pcmd->left); 110 | } 111 | if(fork1() == 0){ 112 | close(0); 113 | dup(p[0]); 114 | close(p[0]); 115 | close(p[1]); 116 | runcmd(pcmd->right); 117 | } 118 | close(p[0]); 119 | close(p[1]); 120 | wait(0); 121 | wait(0); 122 | break; 123 | 124 | case BACK: 125 | bcmd = (struct backcmd*)cmd; 126 | if(fork1() == 0) 127 | runcmd(bcmd->cmd); 128 | break; 129 | } 130 | exit(0); 131 | } 132 | 133 | int 134 | getcmd(char *buf, int nbuf) 135 | { 136 | fprintf(2, "$ "); 137 | memset(buf, 0, nbuf); 138 | gets(buf, nbuf); 139 | if(buf[0] == 0) // EOF 140 | return -1; 141 | return 0; 142 | } 143 | 144 | int 145 | main(void) 146 | { 147 | static char buf[100]; 148 | int fd; 149 | 150 | // Ensure that three file descriptors are open. 151 | while((fd = open("console", O_RDWR)) >= 0){ 152 | if(fd >= 3){ 153 | close(fd); 154 | break; 155 | } 156 | } 157 | 158 | // Read and run input commands. 159 | while(getcmd(buf, sizeof(buf)) >= 0){ 160 | if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ 161 | // Chdir must be called by the parent, not the child. 162 | buf[strlen(buf)-1] = 0; // chop \n 163 | if(chdir(buf+3) < 0) 164 | fprintf(2, "cannot cd %s\n", buf+3); 165 | continue; 166 | } 167 | if(fork1() == 0) 168 | runcmd(parsecmd(buf)); 169 | wait(0); 170 | } 171 | exit(0); 172 | } 173 | 174 | void 175 | panic(char *s) 176 | { 177 | fprintf(2, "%s\n", s); 178 | exit(1); 179 | } 180 | 181 | int 182 | fork1(void) 183 | { 184 | int pid; 185 | 186 | pid = fork(); 187 | if(pid == -1) 188 | panic("fork"); 189 | return pid; 190 | } 191 | 192 | //PAGEBREAK! 193 | // Constructors 194 | 195 | struct cmd* 196 | execcmd(void) 197 | { 198 | struct execcmd *cmd; 199 | 200 | cmd = malloc(sizeof(*cmd)); 201 | memset(cmd, 0, sizeof(*cmd)); 202 | cmd->type = EXEC; 203 | return (struct cmd*)cmd; 204 | } 205 | 206 | struct cmd* 207 | redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) 208 | { 209 | struct redircmd *cmd; 210 | 211 | cmd = malloc(sizeof(*cmd)); 212 | memset(cmd, 0, sizeof(*cmd)); 213 | cmd->type = REDIR; 214 | cmd->cmd = subcmd; 215 | cmd->file = file; 216 | cmd->efile = efile; 217 | cmd->mode = mode; 218 | cmd->fd = fd; 219 | return (struct cmd*)cmd; 220 | } 221 | 222 | struct cmd* 223 | pipecmd(struct cmd *left, struct cmd *right) 224 | { 225 | struct pipecmd *cmd; 226 | 227 | cmd = malloc(sizeof(*cmd)); 228 | memset(cmd, 0, sizeof(*cmd)); 229 | cmd->type = PIPE; 230 | cmd->left = left; 231 | cmd->right = right; 232 | return (struct cmd*)cmd; 233 | } 234 | 235 | struct cmd* 236 | listcmd(struct cmd *left, struct cmd *right) 237 | { 238 | struct listcmd *cmd; 239 | 240 | cmd = malloc(sizeof(*cmd)); 241 | memset(cmd, 0, sizeof(*cmd)); 242 | cmd->type = LIST; 243 | cmd->left = left; 244 | cmd->right = right; 245 | return (struct cmd*)cmd; 246 | } 247 | 248 | struct cmd* 249 | backcmd(struct cmd *subcmd) 250 | { 251 | struct backcmd *cmd; 252 | 253 | cmd = malloc(sizeof(*cmd)); 254 | memset(cmd, 0, sizeof(*cmd)); 255 | cmd->type = BACK; 256 | cmd->cmd = subcmd; 257 | return (struct cmd*)cmd; 258 | } 259 | //PAGEBREAK! 260 | // Parsing 261 | 262 | char whitespace[] = " \t\r\n\v"; 263 | char symbols[] = "<|>&;()"; 264 | 265 | int 266 | gettoken(char **ps, char *es, char **q, char **eq) 267 | { 268 | char *s; 269 | int ret; 270 | 271 | s = *ps; 272 | while(s < es && strchr(whitespace, *s)) 273 | s++; 274 | if(q) 275 | *q = s; 276 | ret = *s; 277 | switch(*s){ 278 | case 0: 279 | break; 280 | case '|': 281 | case '(': 282 | case ')': 283 | case ';': 284 | case '&': 285 | case '<': 286 | s++; 287 | break; 288 | case '>': 289 | s++; 290 | if(*s == '>'){ 291 | ret = '+'; 292 | s++; 293 | } 294 | break; 295 | default: 296 | ret = 'a'; 297 | while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) 298 | s++; 299 | break; 300 | } 301 | if(eq) 302 | *eq = s; 303 | 304 | while(s < es && strchr(whitespace, *s)) 305 | s++; 306 | *ps = s; 307 | return ret; 308 | } 309 | 310 | int 311 | peek(char **ps, char *es, char *toks) 312 | { 313 | char *s; 314 | 315 | s = *ps; 316 | while(s < es && strchr(whitespace, *s)) 317 | s++; 318 | *ps = s; 319 | return *s && strchr(toks, *s); 320 | } 321 | 322 | struct cmd *parseline(char**, char*); 323 | struct cmd *parsepipe(char**, char*); 324 | struct cmd *parseexec(char**, char*); 325 | struct cmd *nulterminate(struct cmd*); 326 | 327 | struct cmd* 328 | parsecmd(char *s) 329 | { 330 | char *es; 331 | struct cmd *cmd; 332 | 333 | es = s + strlen(s); 334 | cmd = parseline(&s, es); 335 | peek(&s, es, ""); 336 | if(s != es){ 337 | fprintf(2, "leftovers: %s\n", s); 338 | panic("syntax"); 339 | } 340 | nulterminate(cmd); 341 | return cmd; 342 | } 343 | 344 | struct cmd* 345 | parseline(char **ps, char *es) 346 | { 347 | struct cmd *cmd; 348 | 349 | cmd = parsepipe(ps, es); 350 | while(peek(ps, es, "&")){ 351 | gettoken(ps, es, 0, 0); 352 | cmd = backcmd(cmd); 353 | } 354 | if(peek(ps, es, ";")){ 355 | gettoken(ps, es, 0, 0); 356 | cmd = listcmd(cmd, parseline(ps, es)); 357 | } 358 | return cmd; 359 | } 360 | 361 | struct cmd* 362 | parsepipe(char **ps, char *es) 363 | { 364 | struct cmd *cmd; 365 | 366 | cmd = parseexec(ps, es); 367 | if(peek(ps, es, "|")){ 368 | gettoken(ps, es, 0, 0); 369 | cmd = pipecmd(cmd, parsepipe(ps, es)); 370 | } 371 | return cmd; 372 | } 373 | 374 | struct cmd* 375 | parseredirs(struct cmd *cmd, char **ps, char *es) 376 | { 377 | int tok; 378 | char *q, *eq; 379 | 380 | while(peek(ps, es, "<>")){ 381 | tok = gettoken(ps, es, 0, 0); 382 | if(gettoken(ps, es, &q, &eq) != 'a') 383 | panic("missing file for redirection"); 384 | switch(tok){ 385 | case '<': 386 | cmd = redircmd(cmd, q, eq, O_RDONLY, 0); 387 | break; 388 | case '>': 389 | cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1); 390 | break; 391 | case '+': // >> 392 | cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); 393 | break; 394 | } 395 | } 396 | return cmd; 397 | } 398 | 399 | struct cmd* 400 | parseblock(char **ps, char *es) 401 | { 402 | struct cmd *cmd; 403 | 404 | if(!peek(ps, es, "(")) 405 | panic("parseblock"); 406 | gettoken(ps, es, 0, 0); 407 | cmd = parseline(ps, es); 408 | if(!peek(ps, es, ")")) 409 | panic("syntax - missing )"); 410 | gettoken(ps, es, 0, 0); 411 | cmd = parseredirs(cmd, ps, es); 412 | return cmd; 413 | } 414 | 415 | struct cmd* 416 | parseexec(char **ps, char *es) 417 | { 418 | char *q, *eq; 419 | int tok, argc; 420 | struct execcmd *cmd; 421 | struct cmd *ret; 422 | 423 | if(peek(ps, es, "(")) 424 | return parseblock(ps, es); 425 | 426 | ret = execcmd(); 427 | cmd = (struct execcmd*)ret; 428 | 429 | argc = 0; 430 | ret = parseredirs(ret, ps, es); 431 | while(!peek(ps, es, "|)&;")){ 432 | if((tok=gettoken(ps, es, &q, &eq)) == 0) 433 | break; 434 | if(tok != 'a') 435 | panic("syntax"); 436 | cmd->argv[argc] = q; 437 | cmd->eargv[argc] = eq; 438 | argc++; 439 | if(argc >= MAXARGS) 440 | panic("too many args"); 441 | ret = parseredirs(ret, ps, es); 442 | } 443 | cmd->argv[argc] = 0; 444 | cmd->eargv[argc] = 0; 445 | return ret; 446 | } 447 | 448 | // NUL-terminate all the counted strings. 449 | struct cmd* 450 | nulterminate(struct cmd *cmd) 451 | { 452 | int i; 453 | struct backcmd *bcmd; 454 | struct execcmd *ecmd; 455 | struct listcmd *lcmd; 456 | struct pipecmd *pcmd; 457 | struct redircmd *rcmd; 458 | 459 | if(cmd == 0) 460 | return 0; 461 | 462 | switch(cmd->type){ 463 | case EXEC: 464 | ecmd = (struct execcmd*)cmd; 465 | for(i=0; ecmd->argv[i]; i++) 466 | *ecmd->eargv[i] = 0; 467 | break; 468 | 469 | case REDIR: 470 | rcmd = (struct redircmd*)cmd; 471 | nulterminate(rcmd->cmd); 472 | *rcmd->efile = 0; 473 | break; 474 | 475 | case PIPE: 476 | pcmd = (struct pipecmd*)cmd; 477 | nulterminate(pcmd->left); 478 | nulterminate(pcmd->right); 479 | break; 480 | 481 | case LIST: 482 | lcmd = (struct listcmd*)cmd; 483 | nulterminate(lcmd->left); 484 | nulterminate(lcmd->right); 485 | break; 486 | 487 | case BACK: 488 | bcmd = (struct backcmd*)cmd; 489 | nulterminate(bcmd->cmd); 490 | break; 491 | } 492 | return cmd; 493 | } 494 | -------------------------------------------------------------------------------- /user/sleep.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "user/user.h" 3 | 4 | int main(int argc, char *argv[]) { 5 | if (argc != 2) { 6 | fprintf(2, "usage: sleep [ticks num]\n"); 7 | exit(1); 8 | } 9 | // atoi sys call guarantees return an integer 10 | int ticks = atoi(argv[1]); 11 | int ret = sleep(ticks); 12 | exit(ret); 13 | } 14 | -------------------------------------------------------------------------------- /user/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 "kernel/types.h" 11 | #include "kernel/stat.h" 12 | #include "user/user.h" 13 | #include "kernel/fs.h" 14 | #include "kernel/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("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("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("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(0); 47 | 48 | exit(0); 49 | } 50 | -------------------------------------------------------------------------------- /user/ulib.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "kernel/fcntl.h" 4 | #include "user/user.h" 5 | 6 | char* 7 | strcpy(char *s, const char *t) 8 | { 9 | char *os; 10 | 11 | os = s; 12 | while((*s++ = *t++) != 0) 13 | ; 14 | return os; 15 | } 16 | 17 | int 18 | strcmp(const char *p, const char *q) 19 | { 20 | while(*p && *p == *q) 21 | p++, q++; 22 | return (uchar)*p - (uchar)*q; 23 | } 24 | 25 | uint 26 | strlen(const char *s) 27 | { 28 | int n; 29 | 30 | for(n = 0; s[n]; n++) 31 | ; 32 | return n; 33 | } 34 | 35 | void* 36 | memset(void *dst, int c, uint n) 37 | { 38 | char *cdst = (char *) dst; 39 | int i; 40 | for(i = 0; i < n; i++){ 41 | cdst[i] = c; 42 | } 43 | return dst; 44 | } 45 | 46 | char* 47 | strchr(const char *s, char c) 48 | { 49 | for(; *s; s++) 50 | if(*s == c) 51 | return (char*)s; 52 | return 0; 53 | } 54 | 55 | char* 56 | gets(char *buf, int max) 57 | { 58 | int i, cc; 59 | char c; 60 | 61 | for(i=0; i+1 < max; ){ 62 | cc = read(0, &c, 1); 63 | if(cc < 1) 64 | break; 65 | buf[i++] = c; 66 | if(c == '\n' || c == '\r') 67 | break; 68 | } 69 | buf[i] = '\0'; 70 | return buf; 71 | } 72 | 73 | int 74 | stat(const char *n, struct stat *st) 75 | { 76 | int fd; 77 | int r; 78 | 79 | fd = open(n, O_RDONLY); 80 | if(fd < 0) 81 | return -1; 82 | r = fstat(fd, st); 83 | close(fd); 84 | return r; 85 | } 86 | 87 | int 88 | atoi(const char *s) 89 | { 90 | int n; 91 | 92 | n = 0; 93 | while('0' <= *s && *s <= '9') 94 | n = n*10 + *s++ - '0'; 95 | return n; 96 | } 97 | 98 | void* 99 | memmove(void *vdst, const void *vsrc, int n) 100 | { 101 | char *dst; 102 | const char *src; 103 | 104 | dst = vdst; 105 | src = vsrc; 106 | if (src > dst) { 107 | while(n-- > 0) 108 | *dst++ = *src++; 109 | } else { 110 | dst += n; 111 | src += n; 112 | while(n-- > 0) 113 | *--dst = *--src; 114 | } 115 | return vdst; 116 | } 117 | 118 | int 119 | memcmp(const void *s1, const void *s2, uint n) 120 | { 121 | const char *p1 = s1, *p2 = s2; 122 | while (n-- > 0) { 123 | if (*p1 != *p2) { 124 | return *p1 - *p2; 125 | } 126 | p1++; 127 | p2++; 128 | } 129 | return 0; 130 | } 131 | 132 | void * 133 | memcpy(void *dst, const void *src, uint n) 134 | { 135 | return memmove(dst, src, n); 136 | } 137 | -------------------------------------------------------------------------------- /user/umalloc.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/user.h" 4 | #include "kernel/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/user.h: -------------------------------------------------------------------------------- 1 | struct stat; 2 | struct rtcdate; 3 | 4 | // system calls 5 | int fork(void); 6 | int exit(int) __attribute__((noreturn)); 7 | int wait(int*); 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 fprintf(int, const char*, ...); 34 | void printf(const char*, ...); 35 | char* gets(char*, int max); 36 | uint strlen(const char*); 37 | void* memset(void*, int, uint); 38 | void* malloc(uint); 39 | void free(void*); 40 | int atoi(const char*); 41 | int memcmp(const void *, const void *, uint); 42 | void *memcpy(void *, const void *, uint); 43 | -------------------------------------------------------------------------------- /user/usys.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Generate usys.S, the stubs for syscalls. 4 | 5 | print "# generated by usys.pl - do not edit\n"; 6 | 7 | print "#include \"kernel/syscall.h\"\n"; 8 | 9 | sub entry { 10 | my $name = shift; 11 | print ".global $name\n"; 12 | print "${name}:\n"; 13 | print " li a7, SYS_${name}\n"; 14 | print " ecall\n"; 15 | print " ret\n"; 16 | } 17 | 18 | entry("fork"); 19 | entry("exit"); 20 | entry("wait"); 21 | entry("pipe"); 22 | entry("read"); 23 | entry("write"); 24 | entry("close"); 25 | entry("kill"); 26 | entry("exec"); 27 | entry("open"); 28 | entry("mknod"); 29 | entry("unlink"); 30 | entry("fstat"); 31 | entry("link"); 32 | entry("mkdir"); 33 | entry("chdir"); 34 | entry("dup"); 35 | entry("getpid"); 36 | entry("sbrk"); 37 | entry("sleep"); 38 | entry("uptime"); 39 | -------------------------------------------------------------------------------- /user/wc.c: -------------------------------------------------------------------------------- 1 | #include "kernel/types.h" 2 | #include "kernel/stat.h" 3 | #include "user/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 a/b 3 | mkdir c 4 | echo hello > c/b 5 | echo hello > b 6 | find . b | xargs grep hello 7 | -------------------------------------------------------------------------------- /user/zombie.c: -------------------------------------------------------------------------------- 1 | // Create a zombie process that 2 | // must be reparented at exit. 3 | 4 | #include "kernel/types.h" 5 | #include "kernel/stat.h" 6 | #include "user/user.h" 7 | 8 | int 9 | main(void) 10 | { 11 | if(fork() > 0) 12 | sleep(5); // Let child exit before parent. 13 | exit(0); 14 | } 15 | --------------------------------------------------------------------------------