diff --git a/LICENSE b/LICENSE index af50cb2508331d748e7fa5340ec882720f24f1af..1ace9a3eb74ec7730715ab48264f6bfdd960a040 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The xv6 software is: -Copyright (c) 2006-2024 Frans Kaashoek, Robert Morris, Russ Cox, +Copyright (c) 2006-2019 Frans Kaashoek, Robert Morris, Russ Cox, Massachusetts Institute of Technology Permission is hereby granted, free of charge, to any person obtaining diff --git a/Makefile b/Makefile index 5e1e62b5c0449cdcf094211a497e6dc44da29f54..cd79933dd4b3afaef7de7957a69552b7e29567fc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -# To compile and run with a lab solution, set the lab name in conf/lab.mk -# (e.g., LAB=util). Run make grade to test solution with the lab's +# To compile and run with a lab solution, set the lab name in lab.mk +# (e.g., LB=util). Run make grade to test solution with the lab's # grade script (e.g., grade-lab-util). -include conf/lab.mk @@ -10,7 +10,12 @@ U=user OBJS = \ $K/entry.o \ + $K/start.o \ + $K/console.o \ + $K/printf.o \ + $K/uart.o \ $K/kalloc.o \ + $K/spinlock.o \ $K/string.o \ $K/main.o \ $K/vm.o \ @@ -32,19 +37,12 @@ OBJS = \ $K/plic.o \ $K/virtio_disk.o -OBJS_KCSAN = \ - $K/start.o \ - $K/console.o \ - $K/printf.o \ - $K/uart.o \ - $K/spinlock.o - -ifdef KCSAN -OBJS_KCSAN += \ - $K/kcsan.o +ifeq ($(LAB),pgtbl) +OBJS += \ + $K/vmcopyin.o endif -ifeq ($(LAB),lock) +ifeq ($(LAB),$(filter $(LAB), pgtbl lock)) OBJS += \ $K/stats.o\ $K/sprintf.o @@ -55,6 +53,7 @@ ifeq ($(LAB),net) OBJS += \ $K/e1000.o \ $K/net.o \ + $K/sysnet.o \ $K/pci.o endif @@ -85,7 +84,7 @@ LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 +CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb ifdef LAB LABUPPER = $(shell echo $(LAB) | tr a-z A-Z) @@ -95,14 +94,7 @@ endif CFLAGS += $(XCFLAGS) CFLAGS += -MD CFLAGS += -mcmodel=medany -# CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax -CFLAGS += -fno-common -nostdlib -CFLAGS += -fno-builtin-strncpy -fno-builtin-strncmp -fno-builtin-strlen -fno-builtin-memset -CFLAGS += -fno-builtin-memmove -fno-builtin-memcmp -fno-builtin-log -fno-builtin-bzero -CFLAGS += -fno-builtin-strchr -fno-builtin-exit -fno-builtin-malloc -fno-builtin-putc -CFLAGS += -fno-builtin-free -CFLAGS += -fno-builtin-memcpy -Wno-main -CFLAGS += -fno-builtin-printf -fno-builtin-fprintf -fno-builtin-vprintf +CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax CFLAGS += -I. CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) @@ -110,11 +102,6 @@ ifeq ($(LAB),net) CFLAGS += -DNET_TESTS_PORT=$(SERVERPORT) endif -ifdef KCSAN -CFLAGS += -DKCSAN -KCSANFLAG = -fsanitize=thread -fno-inline -endif - # Disable PIE when possible (for Ubuntu 16.10 toolchain) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) CFLAGS += -fno-pie -no-pie @@ -125,17 +112,11 @@ endif LDFLAGS = -z max-page-size=4096 -$K/kernel: $(OBJS) $(OBJS_KCSAN) $K/kernel.ld $U/initcode - $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJS_KCSAN) +$K/kernel: $(OBJS) $K/kernel.ld $U/initcode + $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym -$(OBJS): EXTRAFLAG := $(KCSANFLAG) - -$K/%.o: $K/%.c - $(CC) $(CFLAGS) $(EXTRAFLAG) -c -o $@ $< - - $U/initcode: $U/initcode.S $(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o @@ -147,12 +128,12 @@ tags: $(OBJS) _init ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o -ifeq ($(LAB),lock) +ifeq ($(LAB),$(filter $(LAB), pgtbl lock)) ULIB += $U/statistics.o endif _%: %.o $(ULIB) - $(LD) $(LDFLAGS) -T $U/user.ld -o $@ $^ + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ $(OBJDUMP) -S $@ > $*.asm $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym @@ -198,14 +179,7 @@ UPROGS=\ -ifeq ($(LAB),syscall) -UPROGS += \ - $U/_attack\ - $U/_attacktest\ - $U/_secret -endif - -ifeq ($(LAB),lock) +ifeq ($(LAB),$(filter $(LAB), pgtbl lock)) UPROGS += \ $U/_stats endif @@ -238,15 +212,10 @@ $U/_uthread: $U/uthread.o $U/uthread_switch.o $(ULIB) $(OBJDUMP) -S $U/_uthread > $U/uthread.asm ph: notxv6/ph.c - gcc -o ph -g -O2 $(XCFLAGS) notxv6/ph.c -pthread + gcc -o ph -g -O2 notxv6/ph.c -pthread barrier: notxv6/barrier.c - gcc -o barrier -g -O2 $(XCFLAGS) notxv6/barrier.c -pthread -endif - -ifeq ($(LAB),pgtbl) -UPROGS += \ - $U/_pgtbltest + gcc -o barrier -g -O2 notxv6/barrier.c -pthread endif ifeq ($(LAB),lock) @@ -261,14 +230,10 @@ UPROGS += \ endif -ifeq ($(LAB),mmap) -UPROGS += \ - $U/_mmaptest -endif ifeq ($(LAB),net) UPROGS += \ - $U/_nettest + $U/_nettests endif UEXTRA= @@ -280,18 +245,15 @@ endif fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS) mkfs/mkfs fs.img README $(UEXTRA) $(UPROGS) -newfs.img: - -mv -f fs.img fs.img.bk - -include kernel/*.d user/*.d -clean: - rm -rf *.tex *.dvi *.idx *.aux *.log *.ind *.ilg *.dSYM *.zip *.pcap \ +clean: + rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ */*.o */*.d */*.asm */*.sym \ - $U/initcode $U/initcode.out $U/usys.S $U/_* \ - $K/kernel \ - mkfs/mkfs fs.img fs.img.bk .gdbinit __pycache__ xv6.out* \ - ph barrier + $U/initcode $U/initcode.out $K/kernel fs.img \ + mkfs/mkfs .gdbinit \ + $U/usys.S \ + $(UPROGS) # try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) @@ -306,25 +268,18 @@ ifeq ($(LAB),fs) CPUS := 1 endif -FWDPORT1 = $(shell expr `id -u` % 5000 + 25999) -FWDPORT2 = $(shell expr `id -u` % 5000 + 30999) +FWDPORT = $(shell expr `id -u` % 5000 + 25999) QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic -QEMUOPTS += -global virtio-mmio.force-legacy=false QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 ifeq ($(LAB),net) -QEMUOPTS += -netdev user,id=net0,hostfwd=udp::$(FWDPORT1)-:2000,hostfwd=udp::$(FWDPORT2)-:2001 -object filter-dump,id=net0,netdev=net0,file=packets.pcap +QEMUOPTS += -netdev user,id=net0,hostfwd=udp::$(FWDPORT)-:2000 -object filter-dump,id=net0,netdev=net0,file=packets.pcap QEMUOPTS += -device e1000,netdev=net0,bus=pcie.0 endif -# makes a new fs.img -qemu: newfs.img $K/kernel fs.img - $(QEMU) $(QEMUOPTS) - -# runs with existing fs.img, if present -qemu-fs: $K/kernel fs.img +qemu: $K/kernel fs.img $(QEMU) $(QEMUOPTS) .gdbinit: .gdbinit.tmpl-riscv @@ -338,6 +293,11 @@ ifeq ($(LAB),net) # try to generate a unique port for the echo server SERVERPORT = $(shell expr `id -u` % 5000 + 25099) +server: + python3 server.py $(SERVERPORT) + +ping: + python3 ping.py $(FWDPORT) endif ## @@ -358,12 +318,24 @@ grade: ./grade-lab-$(LAB) $(GRADEFLAGS) ## -## FOR submissions +## FOR web handin ## diff: git diff origin/fs HEAD > commit.patch submit-check: + +WEBSUB := https://6828.scripts.mit.edu/2020/handin.py + +handin: tarball-pref myapi.key + @SUF=$(LAB); \ + curl -f -F file=@lab-$$SUF-handin.tar.gz -F key=\<myapi.key $(WEBSUB)/upload \ + > /dev/null || { \ + echo ; \ + echo Submit seems to have failed.; \ + echo Please go to $(WEBSUB)/ and upload the tarball manually.; } + +handin-check: @if ! test -d .git; then \ echo No .git directory, is this a git repository?; \ false; \ @@ -385,7 +357,37 @@ submit-check: test "$$r" = y; \ fi -zipball: clean submit-check - git archive --verbose --format zip --output lab.zip HEAD +UPSTREAM := $(shell git remote -v | grep -m 1 "xv6-labs-2020" | awk '{split($$0,a," "); print a[1]}') + +tarball: handin-check + git archive --format=tar HEAD | gzip > lab-$(LAB)-handin.tar.gz + +tarball-pref: handin-check + @SUF=$(LAB); \ + git archive --format=tar HEAD > lab-$$SUF-handin.tar; \ + git diff $(UPSTREAM)/$(LAB) > /tmp/lab-$$SUF-diff.patch; \ + tar -rf lab-$$SUF-handin.tar /tmp/lab-$$SUF-diff.patch; \ + gzip -c lab-$$SUF-handin.tar > lab-$$SUF-handin.tar.gz; \ + rm lab-$$SUF-handin.tar; \ + rm /tmp/lab-$$SUF-diff.patch; \ + +myapi.key: + @echo Get an API key for yourself by visiting $(WEBSUB)/ + @read -p "Please enter your API key: " k; \ + if test `echo "$$k" |tr -d '\n' |wc -c` = 32 ; then \ + TF=`mktemp -t tmp.XXXXXX`; \ + if test "x$$TF" != "x" ; then \ + echo "$$k" |tr -d '\n' > $$TF; \ + mv -f $$TF $@; \ + else \ + echo mktemp failed; \ + false; \ + fi; \ + else \ + echo Bad API key: $$k; \ + echo An API key should be 32 characters long.; \ + false; \ + fi; + -.PHONY: zipball clean grade submit-check +.PHONY: handin tarball tarball-pref clean grade handin-check diff --git a/README b/README index f583201a604de451b3f4ec6f395b53817d92cbaf..06035bbed7e90fb82b953286cb9fa7311fc56a7e 100644 --- a/README +++ b/README @@ -6,41 +6,40 @@ ACKNOWLEDGMENTS xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, -2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides -pointers to on-line resources for v6. +2000)). See also https://pdos.csail.mit.edu/6.828/, which +provides pointers to on-line resources for v6. The following people have made contributions: Russ Cox (context switching, locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin Clements. We are also grateful for the bug reports and patches contributed by -Takahiro Aoyagi, Marcelo Arroyo, Silas Boyd-Wickizer, Anton Burtsev, -carlclone, Ian Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, -Asami Doi,Wenyang Duan, eyalz800, Nelson Elhage, Saar Ettinger, Alice -Ferrazzi, Nathaniel Filardo, flespark, Peter Froehlich, Yakir Goaron, -Shivam Handa, Matt Harvey, Bryan Henry, jaichenhengjie, Jim Huang, -Matúš Jókay, John Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, -Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim -Kolontsov, Austin Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, -Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark -Morrissey, mtasm, Joel Nider, Hayato Ohhashi, OptimisticSide, -phosphagos, Harry Porter, Greg Price, RayAndrew, Jude Rich, segfault, -Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya Shigemitsu, snoire, -Taojie, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Alissa Tung, -Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, -Keiichi Watanabe, Lucas Wolf, Nicolas Wolovick, wxdao, Grant Wu, x653, -Jindong Zhang, Icenowy Zheng, ZhUyU1997, and Zou Chang Wei. +Silas Boyd-Wickizer, Anton Burtsev, Dan Cross, Cody Cutler, Mike CAT, +Tej Chajed, Asami Doi, eyalz800, , Nelson Elhage, Saar Ettinger, Alice +Ferrazzi, Nathaniel Filardo, Peter Froehlich, Yakir Goaron,Shivam +Handa, Bryan Henry, jaichenhengjie, Jim Huang, Alexander Kapshuk, +Anders Kaseorg, kehao95, Wolfgang Keller, Jonathan Kimmitt, Eddie +Kohler, Austin Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, +Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider, +Greg Price, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya +Shigemitsu, Takahiro, Cam Tenny, tyfkda, Rafael Ubal, Warren Toomey, +Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas +Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang +Wei. + +The code in the files that constitute xv6 is +Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox. ERROR REPORTS Please send errors and suggestions to Frans Kaashoek and Robert Morris -(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching -operating system for MIT's 6.1810, so we are more interested in +(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching +operating system for MIT's 6.S081, so we are more interested in simplifications and clarifications than new features. BUILDING AND RUNNING XV6 You will need a RISC-V "newlib" tool chain from https://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for -riscv64-softmmu. Once they are installed, and in your shell +riscv64-softmmu. Once they are installed, and in your shell search path, you can run "make qemu". diff --git a/grade-lab-fs b/grade-lab-fs index b110317b0333fb72def4fe0ac89a252959067d1a..f1762e1149ed96c4db812ac0d73d8a8927905957 100755 --- a/grade-lab-fs +++ b/grade-lab-fs @@ -9,9 +9,8 @@ r = Runner(save("xv6.out")) def test_bigfile(): r.run_qemu(shell_script([ 'bigfile' - ]), timeout=240) + ]), timeout=180) r.match('^wrote 65803 blocks$') - r.match('^reading bigfile$') r.match('^bigfile done; ok$') @test(0, "running symlinktest") @@ -31,7 +30,7 @@ def test_symlinktest_symlinks(): @test(19, "usertests") def test_usertests(): r.run_qemu(shell_script([ - 'usertests -q' + 'usertests' ]), timeout=360) r.match('^ALL TESTS PASSED$') diff --git a/gradelib.py b/gradelib.py index f0d49345a0c1c9c9f1b5ce31d65ce5457ac4c5be..e8d781446610a409f2a329a1e63ef2b6f06c3901 100644 --- a/gradelib.py +++ b/gradelib.py @@ -1,6 +1,6 @@ from __future__ import print_function -import sys, os, re, time, socket, select, subprocess, errno, shutil, random, string, json +import sys, os, re, time, socket, select, subprocess, errno, shutil, random, string from subprocess import check_call, Popen from optparse import OptionParser @@ -16,7 +16,6 @@ TESTS = [] TOTAL = POSSIBLE = 0 PART_TOTAL = PART_POSSIBLE = 0 CURRENT_TEST = None -GRADES = {} def test(points, title=None, parent=None): """Decorator for declaring test functions. If title is None, the @@ -32,7 +31,7 @@ def test(points, title=None, parent=None): title = " " + title def run_test(): - global TOTAL, POSSIBLE, CURRENT_TEST, GRADES + global TOTAL, POSSIBLE, CURRENT_TEST # Handle test dependencies if run_test.complete: @@ -69,9 +68,6 @@ def test(points, title=None, parent=None): print(" %s" % fail.replace("\n", "\n ")) else: TOTAL += points - if points: - GRADES[title] = 0 if fail else points - for callback in run_test.on_finish: callback(fail) CURRENT_TEST = None @@ -99,16 +95,6 @@ def end_part(name): show_part.title = "" TESTS.append(show_part) -def write_results(): - global options - if not options.results: - return - try: - with open(options.results, "w") as f: - f.write(json.dumps(GRADES)) - except OSError as e: - print("Provided a bad results path. Error:", e) - def run_tests(): """Set up for testing and run the registered test functions.""" @@ -119,7 +105,6 @@ def run_tests(): help="print commands") parser.add_option("--color", choices=["never", "always", "auto"], default="auto", help="never, always, or auto") - parser.add_option("--results", help="results file path") (options, args) = parser.parse_args() # Start with a full build to catch build errors @@ -135,7 +120,6 @@ def run_tests(): if not limit or any(l in test.title.lower() for l in limit): test() if not limit: - write_results() print("Score: %d/%d" % (TOTAL, POSSIBLE)) except KeyboardInterrupt: pass @@ -276,7 +260,6 @@ def check_time(): def check_answers(file, n=10): try: - print("") with open(file) as f: d = f.read().strip() if len(d) < n: diff --git a/kernel/console.c b/kernel/console.c index 05dc5260dbc422b43281cccffa79b7cd5768db7d..23a2d35f0fc2fbcdd263329c97824354ebc4a98e 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -27,7 +27,7 @@ // // send one character to the uart. -// called by printf(), and to echo input characters, +// called by printf, and to echo input characters, // but not from write(). // void @@ -45,8 +45,8 @@ struct { struct spinlock lock; // input -#define INPUT_BUF_SIZE 128 - char buf[INPUT_BUF_SIZE]; +#define INPUT_BUF 128 + char buf[INPUT_BUF]; uint r; // Read index uint w; // Write index uint e; // Edit index @@ -89,14 +89,14 @@ consoleread(int user_dst, uint64 dst, int n) // wait until interrupt handler has put some // input into cons.buffer. while(cons.r == cons.w){ - if(killed(myproc())){ + if(myproc()->killed){ release(&cons.lock); return -1; } sleep(&cons.r, &cons.lock); } - c = cons.buf[cons.r++ % INPUT_BUF_SIZE]; + c = cons.buf[cons.r++ % INPUT_BUF]; if(c == C('D')){ // end-of-file if(n < target){ @@ -143,29 +143,29 @@ consoleintr(int c) break; case C('U'): // Kill line. while(cons.e != cons.w && - cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){ + cons.buf[(cons.e-1) % INPUT_BUF] != '\n'){ cons.e--; consputc(BACKSPACE); } break; case C('H'): // Backspace - case '\x7f': // Delete key + case '\x7f': if(cons.e != cons.w){ cons.e--; consputc(BACKSPACE); } break; default: - if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){ + if(c != 0 && cons.e-cons.r < INPUT_BUF){ c = (c == '\r') ? '\n' : c; // echo back to the user. consputc(c); // store for consumption by consoleread(). - cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; + cons.buf[cons.e++ % INPUT_BUF] = c; - if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){ + if(c == '\n' || c == C('D') || cons.e == cons.r+INPUT_BUF){ // wake up consoleread() if a whole line (or end-of-file) // has arrived. cons.w = cons.e; diff --git a/kernel/date.h b/kernel/date.h new file mode 100644 index 0000000000000000000000000000000000000000..94aec4b77da8bc70f9cfaca3b201d1e132741d29 --- /dev/null +++ b/kernel/date.h @@ -0,0 +1,8 @@ +struct rtcdate { + uint second; + uint minute; + uint hour; + uint day; + uint month; + uint year; +}; diff --git a/kernel/defs.h b/kernel/defs.h index d1b6bb904946c7186a715f4abd06c6aa05996c5d..41098f41834e75f263ce22dc0bbee01999bfd03b 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -77,7 +77,7 @@ int piperead(struct pipe*, uint64, int); int pipewrite(struct pipe*, uint64, int); // printf.c -int printf(char*, ...) __attribute__ ((format (printf, 1, 2))); +void printf(char*, ...); void panic(char*) __attribute__((noreturn)); void printfinit(void); @@ -90,14 +90,13 @@ void proc_mapstacks(pagetable_t); pagetable_t proc_pagetable(struct proc *); void proc_freepagetable(pagetable_t, uint64); int kill(int); -int killed(struct proc*); -void setkilled(struct proc*); struct cpu* mycpu(void); struct cpu* getmycpu(void); struct proc* myproc(); void procinit(void); void scheduler(void) __attribute__((noreturn)); void sched(void); +void setproc(struct proc*); void sleep(void*, struct spinlock*); void userinit(void); int wait(uint64); @@ -134,9 +133,9 @@ int strncmp(const char*, const char*, uint); char* strncpy(char*, const char*, int); // syscall.c -void argint(int, int*); +int argint(int, int*); int argstr(int, char*, int); -void argaddr(int, uint64 *); +int argaddr(int, uint64 *); int fetchstr(uint64, char*, int); int fetchaddr(uint64, uint64*); void syscall(); @@ -161,14 +160,13 @@ void kvminithart(void); void kvmmap(pagetable_t, uint64, uint64, uint64, int); int mappages(pagetable_t, uint64, uint64, uint64, int); pagetable_t uvmcreate(void); -void uvmfirst(pagetable_t, uchar *, uint); -uint64 uvmalloc(pagetable_t, uint64, uint64, int); +void uvminit(pagetable_t, uchar *, uint); +uint64 uvmalloc(pagetable_t, uint64, uint64); uint64 uvmdealloc(pagetable_t, uint64, uint64); int uvmcopy(pagetable_t, pagetable_t, uint64); void uvmfree(pagetable_t, uint64); void uvmunmap(pagetable_t, uint64, uint64, int); void uvmclear(pagetable_t, uint64); -pte_t * walk(pagetable_t, uint64, int); uint64 walkaddr(pagetable_t, uint64); int copyout(pagetable_t, uint64, char *, uint64); int copyin(pagetable_t, char *, uint64, uint64); diff --git a/kernel/entry.S b/kernel/entry.S index 5ab365eb3a67c3917e9ef5fa11eca645c30b7ff2..b72ddbc63c4d85723d3fce663a7526891f6f3197 100644 --- a/kernel/entry.S +++ b/kernel/entry.S @@ -1,21 +1,20 @@ - # qemu -kernel loads the kernel at 0x80000000 - # and causes each hart (i.e. CPU) to jump there. + # qemu -kernel loads the kernel at 0x80000000 + # and causes each CPU to jump there. # kernel.ld causes the following code to # be placed at 0x80000000. .section .text -.global _entry _entry: - # set up a stack for C. + # set up a stack for C. # stack0 is declared in start.c, # with a 4096-byte stack per CPU. # sp = stack0 + (hartid * 4096) la sp, stack0 li a0, 1024*4 - csrr a1, mhartid + csrr a1, mhartid addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 - # jump to start() in start.c + # jump to start() in start.c call start spin: j spin diff --git a/kernel/exec.c b/kernel/exec.c index 6d7c452fef9631f57cc78fa1f0e78c395e5952e4..0e8762f9f779e64161410112e50f8366097c7c80 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -7,24 +7,14 @@ #include "defs.h" #include "elf.h" -static int loadseg(pde_t *, uint64, struct inode *, uint, uint); - -int flags2perm(int flags) -{ - int perm = 0; - if(flags & 0x1) - perm = PTE_X; - if(flags & 0x2) - perm |= PTE_W; - return perm; -} +static int loadseg(pde_t *pgdir, uint64 addr, struct inode *ip, uint offset, uint sz); int exec(char *path, char **argv) { char *s, *last; int i, off; - uint64 argc, sz = 0, sp, ustack[MAXARG], stackbase; + uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase; struct elfhdr elf; struct inode *ip; struct proghdr ph; @@ -42,7 +32,6 @@ exec(char *path, char **argv) // Check ELF header if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) goto bad; - if(elf.magic != ELF_MAGIC) goto bad; @@ -59,12 +48,12 @@ exec(char *path, char **argv) goto bad; if(ph.vaddr + ph.memsz < ph.vaddr) goto bad; - if(ph.vaddr % PGSIZE != 0) - goto bad; uint64 sz1; - if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, flags2perm(ph.flags))) == 0) + if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0) goto bad; sz = sz1; + if(ph.vaddr % PGSIZE != 0) + goto bad; if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0) goto bad; } @@ -75,17 +64,16 @@ exec(char *path, char **argv) p = myproc(); uint64 oldsz = p->sz; - // Allocate some pages at the next page boundary. - // Make the first inaccessible as a stack guard. - // Use the rest as the user stack. + // Allocate two pages at the next page boundary. + // Use the second as the user stack. sz = PGROUNDUP(sz); uint64 sz1; - if((sz1 = uvmalloc(pagetable, sz, sz + (USERSTACK+1)*PGSIZE, PTE_W)) == 0) + if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0) goto bad; sz = sz1; - uvmclear(pagetable, sz-(USERSTACK+1)*PGSIZE); + uvmclear(pagetable, sz-2*PGSIZE); sp = sz; - stackbase = sp - USERSTACK*PGSIZE; + stackbase = sp - PGSIZE; // Push argument strings, prepare rest of stack in ustack. for(argc = 0; argv[argc]; argc++) { @@ -150,6 +138,9 @@ loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz uint i, n; uint64 pa; + if((va % PGSIZE) != 0) + panic("loadseg: va must be page aligned"); + for(i = 0; i < sz; i += PGSIZE){ pa = walkaddr(pagetable, va + i); if(pa == 0) diff --git a/kernel/fs.c b/kernel/fs.c index c6bab157e3e2b6843d9e2335b5c00554e53a859e..f33553a991ffe54d887aa1f6deef659371de0a52 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -61,7 +61,6 @@ bzero(int dev, int bno) // Blocks. // Allocate a zeroed disk block. -// returns 0 if out of disk space. static uint balloc(uint dev) { @@ -83,8 +82,7 @@ balloc(uint dev) } brelse(bp); } - printf("balloc: out of blocks\n"); - return 0; + panic("balloc: out of blocks"); } // Free a disk block. @@ -111,13 +109,13 @@ bfree(int dev, uint b) // its size, the number of links referring to it, and the // list of blocks holding the file's content. // -// The inodes are laid out sequentially on disk at block -// sb.inodestart. Each inode has a number, indicating its +// The inodes are laid out sequentially on disk at +// sb.startinode. Each inode has a number, indicating its // position on the disk. // -// The kernel keeps a table of in-use inodes in memory +// The kernel keeps a cache of in-use inodes in memory // to provide a place for synchronizing access -// to inodes used by multiple processes. The in-memory +// to inodes used by multiple processes. The cached // inodes include book-keeping information that is // not stored on disk: ip->ref and ip->valid. // @@ -129,15 +127,15 @@ bfree(int dev, uint b) // is non-zero. ialloc() allocates, and iput() frees if // the reference and link counts have fallen to zero. // -// * Referencing in table: an entry in the inode table +// * Referencing in cache: an entry in the inode cache // is free if ip->ref is zero. Otherwise ip->ref tracks // the number of in-memory pointers to the entry (open // files and current directories). iget() finds or -// creates a table entry and increments its ref; iput() +// creates a cache entry and increments its ref; iput() // decrements ref. // // * Valid: the information (type, size, &c) in an inode -// table entry is only correct when ip->valid is 1. +// cache entry is only correct when ip->valid is 1. // ilock() reads the inode from // the disk and sets ip->valid, while iput() clears // ip->valid if ip->ref has fallen to zero. @@ -158,16 +156,16 @@ bfree(int dev, uint b) // and only lock it for short periods (e.g., in read()). // The separation also helps avoid deadlock and races during // pathname lookup. iget() increments ip->ref so that the inode -// stays in the table and pointers to it remain valid. +// stays cached and pointers to it remain valid. // // Many internal file system functions expect the caller to // have locked the inodes involved; this lets callers create // multi-step atomic operations. // -// The itable.lock spin-lock protects the allocation of itable +// The icache.lock spin-lock protects the allocation of icache // entries. Since ip->ref indicates whether an entry is free, // and ip->dev and ip->inum indicate which i-node an entry -// holds, one must hold itable.lock while using any of those fields. +// holds, one must hold icache.lock while using any of those fields. // // An ip->lock sleep-lock protects all ip-> fields other than ref, // dev, and inum. One must hold ip->lock in order to @@ -176,16 +174,16 @@ bfree(int dev, uint b) struct { struct spinlock lock; struct inode inode[NINODE]; -} itable; +} icache; void iinit() { int i = 0; - initlock(&itable.lock, "itable"); + initlock(&icache.lock, "icache"); for(i = 0; i < NINODE; i++) { - initsleeplock(&itable.inode[i].lock, "inode"); + initsleeplock(&icache.inode[i].lock, "inode"); } } @@ -193,8 +191,7 @@ static struct inode* iget(uint dev, uint inum); // Allocate an inode on device dev. // Mark it as allocated by giving it type type. -// Returns an unlocked but allocated and referenced inode, -// or NULL if there is no free inode. +// Returns an unlocked but allocated and referenced inode. struct inode* ialloc(uint dev, short type) { @@ -214,13 +211,12 @@ ialloc(uint dev, short type) } brelse(bp); } - printf("ialloc: no inodes\n"); - return 0; + panic("ialloc: no inodes"); } // Copy a modified in-memory inode to disk. // Must be called after every change to an ip->xxx field -// that lives on disk. +// that lives on disk, since i-node cache is write-through. // Caller must hold ip->lock. void iupdate(struct inode *ip) @@ -248,21 +244,21 @@ iget(uint dev, uint inum) { struct inode *ip, *empty; - acquire(&itable.lock); + acquire(&icache.lock); - // Is the inode already in the table? + // Is the inode already cached? empty = 0; - for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){ + for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){ if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){ ip->ref++; - release(&itable.lock); + release(&icache.lock); return ip; } if(empty == 0 && ip->ref == 0) // Remember empty slot. empty = ip; } - // Recycle an inode entry. + // Recycle an inode cache entry. if(empty == 0) panic("iget: no inodes"); @@ -271,7 +267,7 @@ iget(uint dev, uint inum) ip->inum = inum; ip->ref = 1; ip->valid = 0; - release(&itable.lock); + release(&icache.lock); return ip; } @@ -281,9 +277,9 @@ iget(uint dev, uint inum) struct inode* idup(struct inode *ip) { - acquire(&itable.lock); + acquire(&icache.lock); ip->ref++; - release(&itable.lock); + release(&icache.lock); return ip; } @@ -327,7 +323,7 @@ iunlock(struct inode *ip) } // Drop a reference to an in-memory inode. -// If that was the last reference, the inode table entry can +// If that was the last reference, the inode cache entry can // be recycled. // If that was the last reference and the inode has no links // to it, free the inode (and its content) on disk. @@ -336,7 +332,7 @@ iunlock(struct inode *ip) void iput(struct inode *ip) { - acquire(&itable.lock); + acquire(&icache.lock); if(ip->ref == 1 && ip->valid && ip->nlink == 0){ // inode has no links and no other references: truncate and free. @@ -345,7 +341,7 @@ iput(struct inode *ip) // so this acquiresleep() won't block (or deadlock). acquiresleep(&ip->lock); - release(&itable.lock); + release(&icache.lock); itrunc(ip); ip->type = 0; @@ -354,11 +350,11 @@ iput(struct inode *ip) releasesleep(&ip->lock); - acquire(&itable.lock); + acquire(&icache.lock); } ip->ref--; - release(&itable.lock); + release(&icache.lock); } // Common idiom: unlock, then put. @@ -378,7 +374,6 @@ iunlockput(struct inode *ip) // Return the disk block address of the nth block in inode ip. // If there is no such block, bmap allocates one. -// returns 0 if out of disk space. static uint bmap(struct inode *ip, uint bn) { @@ -386,32 +381,21 @@ bmap(struct inode *ip, uint bn) struct buf *bp; if(bn < NDIRECT){ - if((addr = ip->addrs[bn]) == 0){ - addr = balloc(ip->dev); - if(addr == 0) - return 0; - ip->addrs[bn] = addr; - } + if((addr = ip->addrs[bn]) == 0) + ip->addrs[bn] = addr = balloc(ip->dev); return addr; } bn -= NDIRECT; if(bn < NINDIRECT){ // Load indirect block, allocating if necessary. - if((addr = ip->addrs[NDIRECT]) == 0){ - addr = balloc(ip->dev); - if(addr == 0) - return 0; - ip->addrs[NDIRECT] = addr; - } + if((addr = ip->addrs[NDIRECT]) == 0) + ip->addrs[NDIRECT] = addr = balloc(ip->dev); bp = bread(ip->dev, addr); a = (uint*)bp->data; if((addr = a[bn]) == 0){ - addr = balloc(ip->dev); - if(addr){ - a[bn] = addr; - log_write(bp); - } + a[bn] = addr = balloc(ip->dev); + log_write(bp); } brelse(bp); return addr; @@ -480,10 +464,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) n = ip->size - off; for(tot=0; tot<n; tot+=m, off+=m, dst+=m){ - uint addr = bmap(ip, off/BSIZE); - if(addr == 0) - break; - bp = bread(ip->dev, addr); + bp = bread(ip->dev, bmap(ip, off/BSIZE)); m = min(n - tot, BSIZE - off%BSIZE); if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) { brelse(bp); @@ -514,10 +495,7 @@ writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) return -1; for(tot=0; tot<n; tot+=m, off+=m, src+=m){ - uint addr = bmap(ip, off/BSIZE); - if(addr == 0) - break; - bp = bread(ip->dev, addr); + bp = bread(ip->dev, bmap(ip, off/BSIZE)); m = min(n - tot, BSIZE - off%BSIZE); if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) { brelse(bp); @@ -575,7 +553,6 @@ dirlookup(struct inode *dp, char *name, uint *poff) } // Write a new directory entry (name, inum) into the directory dp. -// Returns 0 on success, -1 on failure (e.g. out of disk blocks). int dirlink(struct inode *dp, char *name, uint inum) { @@ -600,7 +577,7 @@ dirlink(struct inode *dp, char *name, uint inum) strncpy(de.name, name, DIRSIZ); de.inum = inum; if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de)) - return -1; + panic("dirlink"); return 0; } diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7eed4842dcd893ef0b74f1650a0fcaf0d38..fa6a0acaeef1115aec9cbec62e50caee44f4bbb2 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -39,7 +39,7 @@ freerange(void *pa_start, void *pa_end) kfree(p); } -// Free the page of physical memory pointed at by pa, +// Free the page of physical memory pointed at by v, // which normally should have been returned by a // call to kalloc(). (The exception is when // initializing the allocator; see kinit above.) diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S index 199f25590e4946c1f4017d93332117d903cae502..f42a36450cfb3f40c26938be1aacbaa304bf69f7 100644 --- a/kernel/kernelvec.S +++ b/kernel/kernelvec.S @@ -1,19 +1,17 @@ - # + # # interrupts and exceptions while in supervisor # mode come here. # - # the current stack is a kernel stack. - # push registers, call kerneltrap(). - # when kerneltrap() returns, restore registers, return. + # push all registers, call kerneltrap(), restore, return. # .globl kerneltrap .globl kernelvec .align 4 kernelvec: - # make room to save registers. + // make room to save registers. addi sp, sp, -256 - # save caller-saved registers. + // save the registers. sd ra, 0(sp) sd sp, 8(sp) sd gp, 16(sp) @@ -21,6 +19,8 @@ kernelvec: sd t0, 32(sp) sd t1, 40(sp) sd t2, 48(sp) + sd s0, 56(sp) + sd s1, 64(sp) sd a0, 72(sp) sd a1, 80(sp) sd a2, 88(sp) @@ -29,22 +29,34 @@ kernelvec: sd a5, 112(sp) sd a6, 120(sp) sd a7, 128(sp) + sd s2, 136(sp) + sd s3, 144(sp) + sd s4, 152(sp) + sd s5, 160(sp) + sd s6, 168(sp) + sd s7, 176(sp) + sd s8, 184(sp) + sd s9, 192(sp) + sd s10, 200(sp) + sd s11, 208(sp) sd t3, 216(sp) sd t4, 224(sp) sd t5, 232(sp) sd t6, 240(sp) - # call the C trap handler in trap.c + // call the C trap handler in trap.c call kerneltrap - # restore registers. + // restore registers. ld ra, 0(sp) ld sp, 8(sp) ld gp, 16(sp) - # not tp (contains hartid), in case we moved CPUs + // not this, in case we moved CPUs: ld tp, 24(sp) ld t0, 32(sp) ld t1, 40(sp) ld t2, 48(sp) + ld s0, 56(sp) + ld s1, 64(sp) ld a0, 72(sp) ld a1, 80(sp) ld a2, 88(sp) @@ -53,6 +65,16 @@ kernelvec: ld a5, 112(sp) ld a6, 120(sp) ld a7, 128(sp) + ld s2, 136(sp) + ld s3, 144(sp) + ld s4, 152(sp) + ld s5, 160(sp) + ld s6, 168(sp) + ld s7, 176(sp) + ld s8, 184(sp) + ld s9, 192(sp) + ld s10, 200(sp) + ld s11, 208(sp) ld t3, 216(sp) ld t4, 224(sp) ld t5, 232(sp) @@ -60,5 +82,40 @@ kernelvec: addi sp, sp, 256 - # return to whatever we were doing in the kernel. + // return to whatever we were doing in the kernel. sret + + # + # machine-mode timer interrupt. + # +.globl timervec +.align 4 +timervec: + # start.c has set up the memory that mscratch points to: + # scratch[0,8,16] : register save area. + # scratch[24] : address of CLINT's MTIMECMP register. + # scratch[32] : desired interval between interrupts. + + csrrw a0, mscratch, a0 + sd a1, 0(a0) + sd a2, 8(a0) + sd a3, 16(a0) + + # schedule the next timer interrupt + # by adding interval to mtimecmp. + ld a1, 24(a0) # CLINT_MTIMECMP(hart) + ld a2, 32(a0) # interval + ld a3, 0(a1) + add a3, a3, a2 + sd a3, 0(a1) + + # raise a supervisor software interrupt. + li a1, 2 + csrw sip, a1 + + ld a3, 16(a0) + ld a2, 8(a0) + ld a1, 0(a0) + csrrw a0, mscratch, a0 + + mret diff --git a/kernel/log.c b/kernel/log.c index 5b58306f8feeb85fc7d4f67dd5a539265bc29166..c8af5bd3574bb2606d82de1f783672cae36575cb 100644 --- a/kernel/log.c +++ b/kernel/log.c @@ -216,14 +216,14 @@ log_write(struct buf *b) { int i; - acquire(&log.lock); if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) panic("too big a transaction"); if (log.outstanding < 1) panic("log_write outside of trans"); + acquire(&log.lock); for (i = 0; i < log.lh.n; i++) { - if (log.lh.block[i] == b->blockno) // log absorption + if (log.lh.block[i] == b->blockno) // log absorbtion break; } log.lh.block[i] = b->blockno; diff --git a/kernel/main.c b/kernel/main.c index f0d3171d4ecef2e09aa48a4a1cfe21a5801b6f97..5d7ad49f0776c6a5c5c3c8eca700eaaa419a9ff3 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -25,7 +25,7 @@ main() plicinit(); // set up interrupt controller plicinithart(); // ask PLIC for device interrupts binit(); // buffer cache - iinit(); // inode table + iinit(); // inode cache fileinit(); // file table virtio_disk_init(); // emulated hard disk userinit(); // first user process diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 3ab2ace56cee4c2a7e8bfc6a1c9120e617655176..776f98ca6588b767b6c008850b258ab5dabe9cec 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,12 +25,20 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 +// core local interruptor (CLINT), which contains the timer. +#define CLINT 0x2000000L +#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) +#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. + // qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) #define PLIC_PENDING (PLIC + 0x1000) +#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) #define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) +#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) #define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) +#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) #define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) // the kernel expects there to be RAM diff --git a/kernel/param.h b/kernel/param.h index 07e6f172fecbfec157b0af29931368762b808f64..ed255eb1ce1396b7b5b6efcd1a58caa99c089639 100644 --- a/kernel/param.h +++ b/kernel/param.h @@ -13,21 +13,7 @@ #define MAXOPBLOCKS 10 // max # of blocks any FS op writes #define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log #define NBUF (MAXOPBLOCKS*3) // size of disk block cache -#ifdef LAB_FS #define FSSIZE 200000 // size of file system in blocks -#else -#ifdef LAB_LOCK -#define FSSIZE 10000 // size of file system in blocks -#else -#define FSSIZE 2000 // size of file system in blocks -#endif -#endif #define MAXPATH 128 // maximum file path name -#ifdef LAB_UTIL -#define USERSTACK 2 // user stack pages -#else -#define USERSTACK 1 // user stack pages -#endif - diff --git a/kernel/pipe.c b/kernel/pipe.c index f6b501a18414c29c1aaec79d092d55a36b76ac98..b6eefb94a3d0362234b874b3f9ebb210aeccff27 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -81,7 +81,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n) acquire(&pi->lock); while(i < n){ - if(pi->readopen == 0 || killed(pr)){ + if(pi->readopen == 0 || pr->killed){ release(&pi->lock); return -1; } @@ -111,7 +111,7 @@ piperead(struct pipe *pi, uint64 addr, int n) acquire(&pi->lock); while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty - if(killed(pr)){ + if(pr->killed){ release(&pi->lock); return -1; } diff --git a/kernel/plic.c b/kernel/plic.c index 4175db9145091ee9e704c16c8b22b1a976102a20..5acba391974eef5138d748c2c372fd38355b2962 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -21,9 +21,8 @@ plicinithart(void) { int hart = cpuid(); - // set enable bits for this hart's S-mode - // for the uart and virtio disk. - *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); + // set uart's enable bit for this hart's S-mode. + *(uint32*)PLIC_SENABLE(hart)= (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); // set this hart's S-mode priority threshold to 0. *(uint32*)PLIC_SPRIORITY(hart) = 0; diff --git a/kernel/printf.c b/kernel/printf.c index d20534c150123b346bdc54a3f6f486a142cc121b..e1347de6486538a2f2e27db91a4ef2d5daeb4899 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -26,13 +26,13 @@ static struct { static char digits[] = "0123456789abcdef"; static void -printint(long long xx, int base, int sign) +printint(int xx, int base, int sign) { char buf[16]; int i; - unsigned long long x; + uint x; - if(sign && (sign = (xx < 0))) + if(sign && (sign = xx < 0)) x = -xx; else x = xx; @@ -59,71 +59,30 @@ printptr(uint64 x) consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); } -// Print to the console. -int +// Print to the console. only understands %d, %x, %p, %s. +void printf(char *fmt, ...) { va_list ap; - int i, cx, c0, c1, c2, locking; + int i, c, locking; char *s; locking = pr.locking; if(locking) acquire(&pr.lock); + if (fmt == 0) + panic("null fmt"); + va_start(ap, fmt); - for(i = 0; (cx = fmt[i] & 0xff) != 0; i++){ - if(cx != '%'){ - consputc(cx); + for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ + if(c != '%'){ + consputc(c); continue; } - i++; - c0 = fmt[i+0] & 0xff; - c1 = c2 = 0; - if(c0) c1 = fmt[i+1] & 0xff; - if(c1) c2 = fmt[i+2] & 0xff; - if(c0 == 'd'){ - printint(va_arg(ap, int), 10, 1); - } else if(c0 == 'l' && c1 == 'd'){ - printint(va_arg(ap, uint64), 10, 1); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'd'){ - printint(va_arg(ap, uint64), 10, 1); - i += 2; - } else if(c0 == 'u'){ - printint(va_arg(ap, int), 10, 0); - } else if(c0 == 'l' && c1 == 'u'){ - printint(va_arg(ap, uint64), 10, 0); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'u'){ - printint(va_arg(ap, uint64), 10, 0); - i += 2; - } else if(c0 == 'x'){ - printint(va_arg(ap, int), 16, 0); - } else if(c0 == 'l' && c1 == 'x'){ - printint(va_arg(ap, uint64), 16, 0); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'x'){ - printint(va_arg(ap, uint64), 16, 0); - i += 2; - } else if(c0 == 'p'){ - printptr(va_arg(ap, uint64)); - } else if(c0 == 's'){ - if((s = va_arg(ap, char*)) == 0) - s = "(null)"; - for(; *s; s++) - consputc(*s); - } else if(c0 == '%'){ - consputc('%'); - } else if(c0 == 0){ + c = fmt[++i] & 0xff; + if(c == 0) break; - } else { - // Print unknown % sequence to draw attention. - consputc('%'); - consputc(c0); - } - -#if 0 switch(c){ case 'd': printint(va_arg(ap, int), 10, 1); @@ -149,14 +108,10 @@ printf(char *fmt, ...) consputc(c); break; } -#endif } - va_end(ap); if(locking) release(&pr.lock); - - return 0; } void @@ -164,7 +119,8 @@ panic(char *s) { pr.locking = 0; printf("panic: "); - printf("%s\n", s); + printf(s); + printf("\n"); panicked = 1; // freeze uart output from other CPUs for(;;) ; diff --git a/kernel/proc.c b/kernel/proc.c index fb5b604c85ece260a3238a594ec25c7f135548ef..ba1a9e3b5df52164b8cc7fa4d5baafe376423d32 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -16,22 +16,17 @@ int nextpid = 1; struct spinlock pid_lock; extern void forkret(void); +static void wakeup1(struct proc *chan); static void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S -// helps ensure that wakeups of wait()ing -// parents are not lost. helps obey the -// memory model when using p->parent. -// must be acquired before any p->lock. -struct spinlock wait_lock; // Allocate a page for each process's kernel stack. // Map it high in memory, followed by an invalid // guard page. void -proc_mapstacks(pagetable_t kpgtbl) -{ +proc_mapstacks(pagetable_t kpgtbl) { struct proc *p; for(p = proc; p < &proc[NPROC]; p++) { @@ -43,17 +38,15 @@ proc_mapstacks(pagetable_t kpgtbl) } } -// initialize the proc table. +// initialize the proc table at boot time. void procinit(void) { struct proc *p; initlock(&pid_lock, "nextpid"); - initlock(&wait_lock, "wait_lock"); for(p = proc; p < &proc[NPROC]; p++) { initlock(&p->lock, "proc"); - p->state = UNUSED; p->kstack = KSTACK((int) (p - proc)); } } @@ -71,8 +64,7 @@ cpuid() // Return this CPU's cpu struct. // Interrupts must be disabled. struct cpu* -mycpu(void) -{ +mycpu(void) { int id = cpuid(); struct cpu *c = &cpus[id]; return c; @@ -80,8 +72,7 @@ mycpu(void) // Return the current struct proc *, or zero if none. struct proc* -myproc(void) -{ +myproc(void) { push_off(); struct cpu *c = mycpu(); struct proc *p = c->proc; @@ -90,14 +81,14 @@ myproc(void) } int -allocpid() -{ +allocpid() { int pid; acquire(&pid_lock); pid = nextpid; nextpid = nextpid + 1; release(&pid_lock); + return pid; } @@ -122,16 +113,13 @@ allocproc(void) found: p->pid = allocpid(); - p->state = USED; // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ - freeproc(p); release(&p->lock); return 0; } - // An empty user page table. p->pagetable = proc_pagetable(p); if(p->pagetable == 0){ @@ -171,8 +159,8 @@ freeproc(struct proc *p) p->state = UNUSED; } -// Create a user page table for a given process, with no user memory, -// but with trampoline and trapframe pages. +// Create a user page table for a given process, +// with no user memory, but with trampoline pages. pagetable_t proc_pagetable(struct proc *p) { @@ -193,8 +181,7 @@ proc_pagetable(struct proc *p) return 0; } - // map the trapframe page just below the trampoline page, for - // trampoline.S. + // map the trapframe just below TRAMPOLINE, for trampoline.S. if(mappages(pagetable, TRAPFRAME, PGSIZE, (uint64)(p->trapframe), PTE_R | PTE_W) < 0){ uvmunmap(pagetable, TRAMPOLINE, 1, 0); @@ -202,7 +189,6 @@ proc_pagetable(struct proc *p) return 0; } - return pagetable; } @@ -217,8 +203,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz) } // a user program that calls exec("/init") -// assembled from ../user/initcode.S -// od -t xC ../user/initcode +// od -t xC initcode uchar initcode[] = { 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02, @@ -238,9 +223,9 @@ userinit(void) p = allocproc(); initproc = p; - // allocate one user page and copy initcode's instructions + // allocate one user page and copy init's instructions // and data into it. - uvmfirst(p->pagetable, initcode, sizeof(initcode)); + uvminit(p->pagetable, initcode, sizeof(initcode)); p->sz = PGSIZE; // prepare for the very first "return" from kernel to user. @@ -260,13 +245,12 @@ userinit(void) int growproc(int n) { - uint64 sz; + uint sz; struct proc *p = myproc(); sz = p->sz; - if(n > 0){ - if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) { + if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) { return -1; } } else if(n < 0){ @@ -289,7 +273,7 @@ fork(void) if((np = allocproc()) == 0){ return -1; } - + // Copy user memory from parent to child. if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){ freeproc(np); @@ -298,6 +282,7 @@ fork(void) } np->sz = p->sz; + np->parent = p; // copy saved user registers. *(np->trapframe) = *(p->trapframe); @@ -315,13 +300,6 @@ fork(void) pid = np->pid; - release(&np->lock); - - acquire(&wait_lock); - np->parent = p; - release(&wait_lock); - - acquire(&np->lock); np->state = RUNNABLE; release(&np->lock); @@ -330,16 +308,27 @@ fork(void) } // Pass p's abandoned children to init. -// Caller must hold wait_lock. +// Caller must hold p->lock. void reparent(struct proc *p) { struct proc *pp; for(pp = proc; pp < &proc[NPROC]; pp++){ + // this code uses pp->parent without holding pp->lock. + // acquiring the lock first could cause a deadlock + // if pp or a child of pp were also in exit() + // and about to try to lock p. if(pp->parent == p){ + // pp->parent can't change between the check and the acquire() + // because only the parent changes it, and we're the parent. + acquire(&pp->lock); pp->parent = initproc; - wakeup(initproc); + // we should wake up init here, but that would require + // initproc->lock, which would be a deadlock, since we hold + // the lock on one of init's children (pp). this is why + // exit() always wakes init (before acquiring any locks). + release(&pp->lock); } } } @@ -364,26 +353,46 @@ exit(int status) } } - begin_op(); iput(p->cwd); end_op(); p->cwd = 0; - acquire(&wait_lock); + // we might re-parent a child to init. we can't be precise about + // waking up init, since we can't acquire its lock once we've + // acquired any other proc lock. so wake up init whether that's + // necessary or not. init may miss this wakeup, but that seems + // harmless. + acquire(&initproc->lock); + wakeup1(initproc); + release(&initproc->lock); + + // grab a copy of p->parent, to ensure that we unlock the same + // parent we locked. in case our parent gives us away to init while + // we're waiting for the parent lock. we may then race with an + // exiting parent, but the result will be a harmless spurious wakeup + // to a dead or wrong process; proc structs are never re-allocated + // as anything else. + acquire(&p->lock); + struct proc *original_parent = p->parent; + release(&p->lock); + + // we need the parent's lock in order to wake it up from wait(). + // the parent-then-child rule says we have to lock it first. + acquire(&original_parent->lock); + + acquire(&p->lock); // Give any children to init. reparent(p); // Parent might be sleeping in wait(). - wakeup(p->parent); - - acquire(&p->lock); + wakeup1(original_parent); p->xstate = status; p->state = ZOMBIE; - release(&wait_lock); + release(&original_parent->lock); // Jump into the scheduler, never to return. sched(); @@ -395,47 +404,52 @@ exit(int status) int wait(uint64 addr) { - struct proc *pp; + struct proc *np; int havekids, pid; struct proc *p = myproc(); - acquire(&wait_lock); + // hold p->lock for the whole time to avoid lost + // wakeups from a child's exit(). + acquire(&p->lock); for(;;){ // Scan through table looking for exited children. havekids = 0; - for(pp = proc; pp < &proc[NPROC]; pp++){ - if(pp->parent == p){ - // make sure the child isn't still in exit() or swtch(). - acquire(&pp->lock); - + for(np = proc; np < &proc[NPROC]; np++){ + // this code uses np->parent without holding np->lock. + // acquiring the lock first would cause a deadlock, + // since np might be an ancestor, and we already hold p->lock. + if(np->parent == p){ + // np->parent can't change between the check and the acquire() + // because only the parent changes it, and we're the parent. + acquire(&np->lock); havekids = 1; - if(pp->state == ZOMBIE){ + if(np->state == ZOMBIE){ // Found one. - pid = pp->pid; - if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate, - sizeof(pp->xstate)) < 0) { - release(&pp->lock); - release(&wait_lock); + pid = np->pid; + if(addr != 0 && copyout(p->pagetable, addr, (char *)&np->xstate, + sizeof(np->xstate)) < 0) { + release(&np->lock); + release(&p->lock); return -1; } - freeproc(pp); - release(&pp->lock); - release(&wait_lock); + freeproc(np); + release(&np->lock); + release(&p->lock); return pid; } - release(&pp->lock); + release(&np->lock); } } // No point waiting if we don't have any children. - if(!havekids || killed(p)){ - release(&wait_lock); + if(!havekids || p->killed){ + release(&p->lock); return -1; } // Wait for a child to exit. - sleep(p, &wait_lock); //DOC: wait-sleep + sleep(p, &p->lock); //DOC: wait-sleep } } @@ -451,14 +465,12 @@ scheduler(void) { struct proc *p; struct cpu *c = mycpu(); - + c->proc = 0; for(;;){ - // The most recent process to run may have had interrupts - // turned off; enable them to avoid a deadlock if all - // processes are waiting. + // Avoid deadlock by ensuring that devices can interrupt. intr_on(); - + int nproc = 0; for(p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); @@ -471,7 +483,6 @@ scheduler(void) // before jumping back to us. p->state = RUNNING; c->proc = p; - swtch(&c->context, &p->context); // Process is done running for now. @@ -481,11 +492,8 @@ scheduler(void) release(&p->lock); } if(nproc <= 2) { // only init and sh exist - // nothing to run; stop running on this core until an interrupt. intr_on(); -#ifndef LAB_FS asm volatile("wfi"); -#endif } } } @@ -534,7 +542,7 @@ void forkret(void) { static int first = 1; - + // Still holding p->lock from scheduler. release(&myproc()->lock); @@ -542,11 +550,8 @@ forkret(void) // File system initialization must be run in the context of a // regular process (e.g., because it calls sleep), and thus cannot // be run from main(). - fsinit(ROOTDEV); - first = 0; - // ensure other cores see first=0. - __sync_synchronize(); + fsinit(ROOTDEV); } usertrapret(); @@ -565,9 +570,10 @@ sleep(void *chan, struct spinlock *lk) // guaranteed that we won't miss any wakeup // (wakeup locks p->lock), // so it's okay to release lk. - - acquire(&p->lock); //DOC: sleeplock1 - release(lk); + if(lk != &p->lock){ //DOC: sleeplock0 + acquire(&p->lock); //DOC: sleeplock1 + release(lk); + } // Go to sleep. p->chan = chan; @@ -579,8 +585,10 @@ sleep(void *chan, struct spinlock *lk) p->chan = 0; // Reacquire original lock. - release(&p->lock); - acquire(lk); + if(lk != &p->lock){ + release(&p->lock); + acquire(lk); + } } // Wake up all processes sleeping on chan. @@ -591,13 +599,23 @@ wakeup(void *chan) struct proc *p; for(p = proc; p < &proc[NPROC]; p++) { - if(p != myproc()){ - acquire(&p->lock); - if(p->state == SLEEPING && p->chan == chan) { - p->state = RUNNABLE; - } - release(&p->lock); + acquire(&p->lock); + if(p->state == SLEEPING && p->chan == chan) { + p->state = RUNNABLE; } + release(&p->lock); + } +} + +// Wake up p if it is sleeping in wait(); used by exit(). +// Caller must hold p->lock. +static void +wakeup1(struct proc *p) +{ + if(!holding(&p->lock)) + panic("wakeup1"); + if(p->chan == p && p->state == SLEEPING) { + p->state = RUNNABLE; } } @@ -625,25 +643,6 @@ kill(int pid) return -1; } -void -setkilled(struct proc *p) -{ - acquire(&p->lock); - p->killed = 1; - release(&p->lock); -} - -int -killed(struct proc *p) -{ - int k; - - acquire(&p->lock); - k = p->killed; - release(&p->lock); - return k; -} - // Copy to either a user address, or kernel address, // depending on usr_dst. // Returns 0 on success, -1 on error. @@ -682,7 +681,6 @@ procdump(void) { static char *states[] = { [UNUSED] "unused", - [USED] "used", [SLEEPING] "sleep ", [RUNNABLE] "runble", [RUNNING] "run ", @@ -703,6 +701,3 @@ procdump(void) printf("\n"); } } - - - diff --git a/kernel/proc.h b/kernel/proc.h index d021857a3430c1b4bfb7792ad616ec986ef3874b..9c16ea726d05735e23d0d2f74168c483c017be60 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -31,6 +31,7 @@ extern struct cpu cpus[NCPU]; // per-process data for the trap handling code in trampoline.S. // sits in a page by itself just under the trampoline page in the // user page table. not specially mapped in the kernel page table. +// the sscratch register points here. // uservec in trampoline.S saves user registers in the trapframe, // then initializes registers from the trapframe's // kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap. @@ -79,7 +80,7 @@ struct trapframe { /* 280 */ uint64 t6; }; -enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; +enum procstate { UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; // Per-process state struct proc { @@ -87,14 +88,12 @@ struct proc { // p->lock must be held when using these: enum procstate state; // Process state + struct proc *parent; // Parent process void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID - // wait_lock must be held when using this: - struct proc *parent; // Parent process - // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) diff --git a/kernel/ramdisk.c b/kernel/ramdisk.c new file mode 100644 index 0000000000000000000000000000000000000000..eb60ee7a53c1da3617145443d277010e4f41b200 --- /dev/null +++ b/kernel/ramdisk.c @@ -0,0 +1,45 @@ +// +// ramdisk that uses the disk image loaded by qemu -initrd fs.img +// + +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "buf.h" + +void +ramdiskinit(void) +{ +} + +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. +void +ramdiskrw(struct buf *b) +{ + if(!holdingsleep(&b->lock)) + panic("ramdiskrw: buf not locked"); + if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) + panic("ramdiskrw: nothing to do"); + + if(b->blockno >= FSSIZE) + panic("ramdiskrw: blockno too big"); + + uint64 diskaddr = b->blockno * BSIZE; + char *addr = (char *)RAMDISK + diskaddr; + + if(b->flags & B_DIRTY){ + // write + memmove(addr, b->data, BSIZE); + b->flags &= ~B_DIRTY; + } else { + // read + memmove(b->data, addr, BSIZE); + b->flags |= B_VALID; + } +} diff --git a/kernel/riscv.h b/kernel/riscv.h index f7aaa8a2b8386e14b2783883113d6a687b04d8b1..0aec0036c664a6d88afb06c75e0fff8d4002649d 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -1,5 +1,3 @@ -#ifndef __ASSEMBLER__ - // which hart (core) is this? static inline uint64 r_mhartid() @@ -96,7 +94,9 @@ w_sie(uint64 x) } // Machine-mode Interrupt Enable -#define MIE_STIE (1L << 5) // supervisor timer +#define MIE_MEIE (1L << 11) // external +#define MIE_MTIE (1L << 7) // timer +#define MIE_MSIE (1L << 3) // software static inline uint64 r_mie() { @@ -111,7 +111,7 @@ w_mie(uint64 x) asm volatile("csrw mie, %0" : : "r" (x)); } -// supervisor exception program counter, holds the +// machine exception program counter, holds the // instruction address to which a return from // exception will go. static inline void @@ -174,51 +174,11 @@ r_stvec() return x; } -// Supervisor Timer Comparison Register -static inline uint64 -r_stimecmp() -{ - uint64 x; - // asm volatile("csrr %0, stimecmp" : "=r" (x) ); - asm volatile("csrr %0, 0x14d" : "=r" (x) ); - return x; -} - -static inline void -w_stimecmp(uint64 x) -{ - // asm volatile("csrw stimecmp, %0" : : "r" (x)); - asm volatile("csrw 0x14d, %0" : : "r" (x)); -} - -// Machine Environment Configuration Register -static inline uint64 -r_menvcfg() -{ - uint64 x; - // asm volatile("csrr %0, menvcfg" : "=r" (x) ); - asm volatile("csrr %0, 0x30a" : "=r" (x) ); - return x; -} - +// Machine-mode interrupt vector static inline void -w_menvcfg(uint64 x) -{ - // asm volatile("csrw menvcfg, %0" : : "r" (x)); - asm volatile("csrw 0x30a, %0" : : "r" (x)); -} - -// Physical Memory Protection -static inline void -w_pmpcfg0(uint64 x) -{ - asm volatile("csrw pmpcfg0, %0" : : "r" (x)); -} - -static inline void -w_pmpaddr0(uint64 x) +w_mtvec(uint64 x) { - asm volatile("csrw pmpaddr0, %0" : : "r" (x)); + asm volatile("csrw mtvec, %0" : : "r" (x)); } // use riscv's sv39 page table scheme. @@ -242,6 +202,19 @@ r_satp() return x; } +// Supervisor Scratch register, for early trap handler in trampoline.S. +static inline void +w_sscratch(uint64 x) +{ + asm volatile("csrw sscratch, %0" : : "r" (x)); +} + +static inline void +w_mscratch(uint64 x) +{ + asm volatile("csrw mscratch, %0" : : "r" (x)); +} + // Supervisor Trap Cause static inline uint64 r_scause() @@ -314,7 +287,7 @@ r_sp() return x; } -// read and write tp, the thread pointer, which xv6 uses to hold +// read and write tp, the thread pointer, which holds // this core's hartid (core number), the index into cpus[]. static inline uint64 r_tp() @@ -346,10 +319,6 @@ sfence_vma() asm volatile("sfence.vma zero, zero"); } -typedef uint64 pte_t; -typedef uint64 *pagetable_t; // 512 PTEs - -#endif // __ASSEMBLER__ #define PGSIZE 4096 // bytes per page #define PGSHIFT 12 // bits of offset within a page @@ -361,7 +330,7 @@ typedef uint64 *pagetable_t; // 512 PTEs #define PTE_R (1L << 1) #define PTE_W (1L << 2) #define PTE_X (1L << 3) -#define PTE_U (1L << 4) // user can access +#define PTE_U (1L << 4) // 1 -> user can access // shift a physical address to the right place for a PTE. #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) @@ -380,3 +349,6 @@ typedef uint64 *pagetable_t; // 512 PTEs // Sv39, to avoid having to sign-extend virtual addresses // that have the high bit set. #define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) + +typedef uint64 pte_t; +typedef uint64 *pagetable_t; // 512 PTEs diff --git a/kernel/start.c b/kernel/start.c index 9ee35f1dfcf1308bd5984fdfaa819d45806f5220..18766809161803719ccc0c76bb0178171ad09f95 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -10,6 +10,12 @@ void timerinit(); // entry.S needs one stack per CPU. __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; +// a scratch area per CPU for machine-mode timer interrupts. +uint64 timer_scratch[NCPU][5]; + +// assembly code in kernelvec.S for machine-mode timer interrupt. +extern void timervec(); + // entry.S jumps here in machine mode on stack0. void start() @@ -32,11 +38,6 @@ start() w_mideleg(0xffff); w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); - // configure Physical Memory Protection to give supervisor mode - // access to all of physical memory. - w_pmpaddr0(0x3fffffffffffffull); - w_pmpcfg0(0xf); - // ask for clock interrupts. timerinit(); @@ -48,19 +49,35 @@ start() asm volatile("mret"); } -// ask each hart to generate timer interrupts. +// set up to receive timer interrupts in machine mode, +// which arrive at timervec in kernelvec.S, +// which turns them into software interrupts for +// devintr() in trap.c. void timerinit() { - // enable supervisor-mode timer interrupts. - w_mie(r_mie() | MIE_STIE); - - // enable the sstc extension (i.e. stimecmp). - w_menvcfg(r_menvcfg() | (1L << 63)); - - // allow supervisor to use stimecmp and time. - w_mcounteren(r_mcounteren() | 2); - - // ask for the very first timer interrupt. - w_stimecmp(r_time() + 1000000); + // each CPU has a separate source of timer interrupts. + int id = r_mhartid(); + + // ask the CLINT for a timer interrupt. + int interval = 1000000; // cycles; about 1/10th second in qemu. + *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + uint64 *scratch = &timer_scratch[id][0]; + scratch[3] = CLINT_MTIMECMP(id); + scratch[4] = interval; + w_mscratch((uint64)scratch); + + // set the machine-mode trap handler. + w_mtvec((uint64)timervec); + + // enable machine-mode interrupts. + w_mstatus(r_mstatus() | MSTATUS_MIE); + + // enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); } diff --git a/kernel/string.c b/kernel/string.c index 153536fc11c03cf1dbd968e7f335af9f4d027a09..d99e61203912e3bafb1f0e3f652d62f29d910387 100644 --- a/kernel/string.c +++ b/kernel/string.c @@ -33,9 +33,6 @@ memmove(void *dst, const void *src, uint n) const char *s; char *d; - if(n == 0) - return dst; - s = src; d = dst; if(s < d && s + n > d){ diff --git a/kernel/syscall.c b/kernel/syscall.c index ed654094cbe24566803fed436126b7773a0b0541..c1b3670db9d39f5570df45da08ab392eb74f2338 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -12,7 +12,7 @@ int fetchaddr(uint64 addr, uint64 *ip) { struct proc *p = myproc(); - if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow + if(addr >= p->sz || addr+sizeof(uint64) > p->sz) return -1; if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) return -1; @@ -25,8 +25,9 @@ int fetchstr(uint64 addr, char *buf, int max) { struct proc *p = myproc(); - if(copyinstr(p->pagetable, buf, addr, max) < 0) - return -1; + int err = copyinstr(p->pagetable, buf, addr, max); + if(err < 0) + return err; return strlen(buf); } @@ -53,19 +54,21 @@ argraw(int n) } // Fetch the nth 32-bit system call argument. -void +int argint(int n, int *ip) { *ip = argraw(n); + return 0; } // Retrieve an argument as a pointer. // Doesn't check for legality, since // copyin/copyout will do that. -void +int argaddr(int n, uint64 *ip) { *ip = argraw(n); + return 0; } // Fetch the nth word-sized system call argument as a null-terminated string. @@ -75,35 +78,33 @@ int argstr(int n, char *buf, int max) { uint64 addr; - argaddr(n, &addr); + if(argaddr(n, &addr) < 0) + return -1; return fetchstr(addr, buf, max); } -// Prototypes for the functions that handle system calls. -extern uint64 sys_fork(void); -extern uint64 sys_exit(void); -extern uint64 sys_wait(void); -extern uint64 sys_pipe(void); -extern uint64 sys_read(void); -extern uint64 sys_kill(void); -extern uint64 sys_exec(void); -extern uint64 sys_fstat(void); extern uint64 sys_chdir(void); +extern uint64 sys_close(void); extern uint64 sys_dup(void); +extern uint64 sys_exec(void); +extern uint64 sys_exit(void); +extern uint64 sys_fork(void); +extern uint64 sys_fstat(void); extern uint64 sys_getpid(void); +extern uint64 sys_kill(void); +extern uint64 sys_link(void); +extern uint64 sys_mkdir(void); +extern uint64 sys_mknod(void); +extern uint64 sys_open(void); +extern uint64 sys_pipe(void); +extern uint64 sys_read(void); extern uint64 sys_sbrk(void); extern uint64 sys_sleep(void); -extern uint64 sys_uptime(void); -extern uint64 sys_open(void); -extern uint64 sys_write(void); -extern uint64 sys_mknod(void); extern uint64 sys_unlink(void); -extern uint64 sys_link(void); -extern uint64 sys_mkdir(void); -extern uint64 sys_close(void); +extern uint64 sys_wait(void); +extern uint64 sys_write(void); +extern uint64 sys_uptime(void); -// An array mapping syscall numbers from syscall.h -// to the function that handles the system call. static uint64 (*syscalls[])(void) = { [SYS_fork] sys_fork, [SYS_exit] sys_exit, @@ -136,8 +137,6 @@ syscall(void) num = p->trapframe->a7; if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { - // Use num to lookup the system call function for num, call it, - // and store its return value in p->trapframe->a0 p->trapframe->a0 = syscalls[num](); } else { printf("%d %s: unknown sys call %d\n", diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 16b668cb3a87d9256387599aead5bb1a8aa59589..5dc453b906f3353d800ec9c4b725375a2edeacd7 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -24,7 +24,8 @@ argfd(int n, int *pfd, struct file **pf) int fd; struct file *f; - argint(n, &fd); + if(argint(n, &fd) < 0) + return -1; if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0) return -1; if(pfd) @@ -72,9 +73,7 @@ sys_read(void) int n; uint64 p; - argaddr(1, &p); - argint(2, &n); - if(argfd(0, 0, &f) < 0) + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0) return -1; return fileread(f, p, n); } @@ -85,10 +84,8 @@ sys_write(void) struct file *f; int n; uint64 p; - - argaddr(1, &p); - argint(2, &n); - if(argfd(0, 0, &f) < 0) + + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0) return -1; return filewrite(f, p, n); @@ -113,8 +110,7 @@ sys_fstat(void) struct file *f; uint64 st; // user pointer to struct stat - argaddr(1, &st); - if(argfd(0, 0, &f) < 0) + if(argfd(0, 0, &f) < 0 || argaddr(1, &st) < 0) return -1; return filestat(f, st); } @@ -262,10 +258,8 @@ create(char *path, short type, short major, short minor) return 0; } - if((ip = ialloc(dp->dev, type)) == 0){ - iunlockput(dp); - return 0; - } + if((ip = ialloc(dp->dev, type)) == 0) + panic("create: ialloc"); ilock(ip); ip->major = major; @@ -274,31 +268,19 @@ create(char *path, short type, short major, short minor) iupdate(ip); if(type == T_DIR){ // Create . and .. entries. + dp->nlink++; // for ".." + iupdate(dp); // No ip->nlink++ for ".": avoid cyclic ref count. if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) - goto fail; + panic("create dots"); } if(dirlink(dp, name, ip->inum) < 0) - goto fail; - - if(type == T_DIR){ - // now that success is guaranteed: - dp->nlink++; // for ".." - iupdate(dp); - } + panic("create: dirlink"); iunlockput(dp); return ip; - - fail: - // something went wrong. de-allocate ip. - ip->nlink = 0; - iupdate(ip); - iunlockput(ip); - iunlockput(dp); - return 0; } uint64 @@ -310,8 +292,7 @@ sys_open(void) struct inode *ip; int n; - argint(1, &omode); - if((n = argstr(0, path, MAXPATH)) < 0) + if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0) return -1; begin_op(); @@ -394,9 +375,9 @@ sys_mknod(void) int major, minor; begin_op(); - argint(1, &major); - argint(2, &minor); if((argstr(0, path, MAXPATH)) < 0 || + argint(1, &major) < 0 || + argint(2, &minor) < 0 || (ip = create(path, T_DEVICE, major, minor)) == 0){ end_op(); return -1; @@ -438,8 +419,7 @@ sys_exec(void) int i; uint64 uargv, uarg; - argaddr(1, &uargv); - if(argstr(0, path, MAXPATH) < 0) { + if(argstr(0, path, MAXPATH) < 0 || argaddr(1, &uargv) < 0){ return -1; } memset(argv, 0, sizeof(argv)); @@ -482,7 +462,8 @@ sys_pipe(void) int fd0, fd1; struct proc *p = myproc(); - argaddr(0, &fdarray); + if(argaddr(0, &fdarray) < 0) + return -1; if(pipealloc(&rf, &wf) < 0) return -1; fd0 = -1; diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3b4d5bd86338fc90086bce8b56d8f57e84df4d20..e8bcda971ebad2c2b3801fb1d11670a2f37e9483 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -1,6 +1,7 @@ #include "types.h" #include "riscv.h" #include "defs.h" +#include "date.h" #include "param.h" #include "memlayout.h" #include "spinlock.h" @@ -10,7 +11,8 @@ uint64 sys_exit(void) { int n; - argint(0, &n); + if(argint(0, &n) < 0) + return -1; exit(n); return 0; // not reached } @@ -31,17 +33,19 @@ uint64 sys_wait(void) { uint64 p; - argaddr(0, &p); + if(argaddr(0, &p) < 0) + return -1; return wait(p); } uint64 sys_sbrk(void) { - uint64 addr; + int addr; int n; - argint(0, &n); + if(argint(0, &n) < 0) + return -1; addr = myproc()->sz; if(growproc(n) < 0) return -1; @@ -54,13 +58,12 @@ sys_sleep(void) int n; uint ticks0; - argint(0, &n); - if(n < 0) - n = 0; + if(argint(0, &n) < 0) + return -1; acquire(&tickslock); ticks0 = ticks; while(ticks - ticks0 < n){ - if(killed(myproc())){ + if(myproc()->killed){ release(&tickslock); return -1; } @@ -75,7 +78,8 @@ sys_kill(void) { int pid; - argint(0, &pid); + if(argint(0, &pid) < 0) + return -1; return kill(pid); } diff --git a/kernel/trampoline.S b/kernel/trampoline.S index 693f8a113906ee74cbc2eb85654d5b9e15968b63..fabaaf921a3e48000eefcc225d8baf24e0fad769 100644 --- a/kernel/trampoline.S +++ b/kernel/trampoline.S @@ -1,21 +1,15 @@ + # + # code to switch between user and kernel space. # - # low-level code to handle traps from user space into - # the kernel, and returns from kernel to user. - # - # the kernel maps the page holding this code - # at the same virtual address (TRAMPOLINE) - # in user and kernel space so that it continues - # to work when it switches page tables. - # kernel.ld causes this code to start at - # a page boundary. + # this code is mapped at the same virtual address + # (TRAMPOLINE) in user and kernel space so that + # it continues to work when it switches page tables. + # + # kernel.ld causes this to be aligned + # to a page boundary. # - -#include "riscv.h" -#include "memlayout.h" - -.section trampsec + .section trampsec .globl trampoline -.globl usertrap trampoline: .align 4 .globl uservec @@ -26,16 +20,14 @@ uservec: # in supervisor mode, but with a # user page table. # - - # save user a0 in sscratch so - # a0 can be used to get at TRAPFRAME. - csrw sscratch, a0 - - # each process has a separate p->trapframe memory area, - # but it's mapped to the same virtual address - # (TRAPFRAME) in every process's user page table. - li a0, TRAPFRAME + # sscratch points to where the process's p->trapframe is + # mapped into user space, at TRAPFRAME. + # + # swap a0 and sscratch + # so that a0 is TRAPFRAME + csrrw a0, sscratch, a0 + # save the user registers in TRAPFRAME sd ra, 40(a0) sd sp, 48(a0) @@ -72,44 +64,42 @@ uservec: csrr t0, sscratch sd t0, 112(a0) - # initialize kernel stack pointer, from p->trapframe->kernel_sp + # restore kernel stack pointer from p->trapframe->kernel_sp ld sp, 8(a0) # make tp hold the current hartid, from p->trapframe->kernel_hartid ld tp, 32(a0) - # load the address of usertrap(), from p->trapframe->kernel_trap + # load the address of usertrap(), p->trapframe->kernel_trap ld t0, 16(a0) - # fetch the kernel page table address, from p->trapframe->kernel_satp. + # restore kernel page table from p->trapframe->kernel_satp ld t1, 0(a0) - - # wait for any previous memory operations to complete, so that - # they use the user page table. - sfence.vma zero, zero - - # install the kernel page table. csrw satp, t1 - - # flush now-stale user entries from the TLB. sfence.vma zero, zero + # a0 is no longer valid, since the kernel page + # table does not specially map p->tf. + # jump to usertrap(), which does not return jr t0 .globl userret userret: - # userret(pagetable) - # called by usertrapret() in trap.c to + # userret(TRAPFRAME, pagetable) # switch from kernel to user. - # a0: user page table, for satp. + # usertrapret() calls here. + # a0: TRAPFRAME, in user page table. + # a1: user page table, for satp. # switch to the user page table. - sfence.vma zero, zero - csrw satp, a0 + csrw satp, a1 sfence.vma zero, zero - li a0, TRAPFRAME + # put the saved user a0 in sscratch, so we + # can swap it with our a0 (TRAPFRAME) in the last step. + ld t0, 112(a0) + csrw sscratch, t0 # restore all but a0 from TRAPFRAME ld ra, 40(a0) @@ -143,8 +133,8 @@ userret: ld t5, 272(a0) ld t6, 280(a0) - # restore user a0 - ld a0, 112(a0) + # restore user a0, and save TRAPFRAME in sscratch + csrrw a0, sscratch, a0 # return to user mode and user pc. # usertrapret() set up sstatus and sepc. diff --git a/kernel/trap.c b/kernel/trap.c index d454a7d8ba5f0674b559811f86e92f2b73204beb..a63249e4e0e3fc829e6a23bc15242c8efcc4542c 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -53,27 +53,27 @@ usertrap(void) if(r_scause() == 8){ // system call - if(killed(p)) + if(p->killed) exit(-1); // sepc points to the ecall instruction, // but we want to return to the next instruction. p->trapframe->epc += 4; - // an interrupt will change sepc, scause, and sstatus, - // so enable only now that we're done with those registers. + // an interrupt will change sstatus &c registers, + // so don't enable until done with those registers. intr_on(); syscall(); } else if((which_dev = devintr()) != 0){ // ok } else { - printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid); - printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval()); - setkilled(p); + printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); + printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); + p->killed = 1; } - if(killed(p)) + if(p->killed) exit(-1); // give up the CPU if this is a timer interrupt. @@ -96,12 +96,11 @@ usertrapret(void) // we're back in user space, where usertrap() is correct. intr_off(); - // send syscalls, interrupts, and exceptions to uservec in trampoline.S - uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline); - w_stvec(trampoline_uservec); + // send syscalls, interrupts, and exceptions to trampoline.S + w_stvec(TRAMPOLINE + (uservec - trampoline)); // set up trapframe values that uservec will need when - // the process next traps into the kernel. + // the process next re-enters the kernel. p->trapframe->kernel_satp = r_satp(); // kernel page table p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack p->trapframe->kernel_trap = (uint64)usertrap; @@ -122,11 +121,11 @@ usertrapret(void) // tell trampoline.S the user page table to switch to. uint64 satp = MAKE_SATP(p->pagetable); - // jump to userret in trampoline.S at the top of memory, which + // jump to trampoline.S at the top of memory, which // switches to the user page table, restores user registers, // and switches to user mode with sret. - uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline); - ((void (*)(uint64))trampoline_userret)(satp); + uint64 fn = TRAMPOLINE + (userret - trampoline); + ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp); } // interrupts and exceptions from kernel code go here via kernelvec, @@ -145,13 +144,13 @@ kerneltrap() panic("kerneltrap: interrupts enabled"); if((which_dev = devintr()) == 0){ - // interrupt or trap from an unknown source - printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval()); + printf("scause %p\n", scause); + printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); panic("kerneltrap"); } // give up the CPU if this is a timer interrupt. - if(which_dev == 2 && myproc() != 0) + if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) yield(); // the yield() may have caused some traps to occur, @@ -163,17 +162,10 @@ kerneltrap() void clockintr() { - if(cpuid() == 0){ - acquire(&tickslock); - ticks++; - wakeup(&ticks); - release(&tickslock); - } - - // ask for the next timer interrupt. this also clears - // the interrupt request. 1000000 is about a tenth - // of a second. - w_stimecmp(r_time() + 1000000); + acquire(&tickslock); + ticks++; + wakeup(&ticks); + release(&tickslock); } // check if it's an external interrupt or software interrupt, @@ -186,7 +178,8 @@ devintr() { uint64 scause = r_scause(); - if(scause == 0x8000000000000009L){ + if((scause & 0x8000000000000000L) && + (scause & 0xff) == 9){ // this is a supervisor external interrupt, via PLIC. // irq indicates which device interrupted. @@ -207,9 +200,18 @@ devintr() plic_complete(irq); return 1; - } else if(scause == 0x8000000000000005L){ - // timer interrupt. - clockintr(); + } else if(scause == 0x8000000000000001L){ + // software interrupt from a machine-mode timer interrupt, + // forwarded by timervec in kernelvec.S. + + if(cpuid() == 0){ + clockintr(); + } + + // acknowledge the software interrupt by clearing + // the SSIP bit in sip. + w_sip(r_sip() & ~2); + return 2; } else { return 0; diff --git a/kernel/uart.c b/kernel/uart.c index 83846ad9244cee15f59f8506c4450ca94dc96a75..3c1dc3437fa6b081e0ea2ecc0c0be3da65ad3b2d 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -13,7 +13,7 @@ // the UART control registers are memory-mapped // at address UART0. this macro returns the // address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) +#define Reg(reg) ((volatile unsigned char *)(UART0 + reg)) // the UART control registers. // some have different meanings for @@ -43,7 +43,7 @@ struct spinlock uart_tx_lock; #define UART_TX_BUF_SIZE 32 char uart_tx_buf[UART_TX_BUF_SIZE]; uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] -uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE] +uint64 uart_tx_r; // read next from uart_tx_buf[uar_tx_r % UART_TX_BUF_SIZE] extern volatile int panicked; // from printf.c @@ -92,18 +92,22 @@ uartputc(int c) for(;;) ; } - while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ - // buffer is full. - // wait for uartstart() to open up space in the buffer. - sleep(&uart_tx_r, &uart_tx_lock); + + while(1){ + if(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ + // buffer is full. + // wait for uartstart() to open up space in the buffer. + sleep(&uart_tx_r, &uart_tx_lock); + } else { + uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; + uart_tx_w += 1; + uartstart(); + release(&uart_tx_lock); + return; + } } - uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; - uart_tx_w += 1; - uartstart(); - release(&uart_tx_lock); } - // alternate version of uartputc() that doesn't // use interrupts, for use by kernel printf() and // to echo characters. it spins waiting for the uart's @@ -136,7 +140,6 @@ uartstart() while(1){ if(uart_tx_w == uart_tx_r){ // transmit buffer is empty. - ReadReg(ISR); return; } @@ -172,7 +175,7 @@ uartgetc(void) // handle a uart interrupt, raised because input has // arrived, or the uart is ready for more output, or -// both. called from devintr(). +// both. called from trap.c. void uartintr(void) { diff --git a/kernel/virtio.h b/kernel/virtio.h index 96272b46d796c8f17de324135c4f890257994ecf..f1dc520c30f4777ed50b2d17d1b6a3ec83f8bc96 100644 --- a/kernel/virtio.h +++ b/kernel/virtio.h @@ -2,6 +2,7 @@ // virtio device definitions. // for both the mmio interface, and virtio descriptors. // only tested with qemu. +// this is the "legacy" virtio interface. // // the virtio spec: // https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf @@ -10,25 +11,22 @@ // virtio mmio control registers, mapped starting at 0x10001000. // from qemu virtio_mmio.h #define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976 -#define VIRTIO_MMIO_VERSION 0x004 // version; should be 2 +#define VIRTIO_MMIO_VERSION 0x004 // version; 1 is legacy #define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk #define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551 #define VIRTIO_MMIO_DEVICE_FEATURES 0x010 #define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 // page size for PFN, write-only #define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only #define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only +#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c // used ring alignment, write-only +#define VIRTIO_MMIO_QUEUE_PFN 0x040 // physical page number for queue, read/write #define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only #define VIRTIO_MMIO_STATUS 0x070 // read/write -#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 // physical address for descriptor table, write-only -#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 -#define VIRTIO_MMIO_DRIVER_DESC_LOW 0x090 // physical address for available ring, write-only -#define VIRTIO_MMIO_DRIVER_DESC_HIGH 0x094 -#define VIRTIO_MMIO_DEVICE_DESC_LOW 0x0a0 // physical address for used ring, write-only -#define VIRTIO_MMIO_DEVICE_DESC_HIGH 0x0a4 // status register bits, from qemu virtio_config.h #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 diff --git a/kernel/virtio_disk.c b/kernel/virtio_disk.c index ae6c164f0859d8796cb46cf97c867b11b9891f91..cca44cbd081357b5fe57dd8b1c1a90424e959c78 100644 --- a/kernel/virtio_disk.c +++ b/kernel/virtio_disk.c @@ -1,6 +1,7 @@ // // driver for qemu's virtio disk device. // uses qemu's mmio interface to virtio. +// qemu presents a "legacy" virtio interface. // // qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 // @@ -20,22 +21,36 @@ #define R(r) ((volatile uint32 *)(VIRTIO0 + (r))) static struct disk { - // a set (not a ring) of DMA descriptors, with which the - // driver tells the device where to read and write individual - // disk operations. there are NUM descriptors. + // the virtio driver and device mostly communicate through a set of + // structures in RAM. pages[] allocates that memory. pages[] is a + // global (instead of calls to kalloc()) because it must consist of + // two contiguous pages of page-aligned physical memory. + char pages[2*PGSIZE]; + + // pages[] is divided into three regions (descriptors, avail, and + // used), as explained in Section 2.6 of the virtio specification + // for the legacy interface. + // https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf + + // the first region of pages[] is a set (not a ring) of DMA + // descriptors, with which the driver tells the device where to read + // and write individual disk operations. there are NUM descriptors. // most commands consist of a "chain" (a linked list) of a couple of // these descriptors. + // points into pages[]. struct virtq_desc *desc; - // a ring in which the driver writes descriptor numbers + // next is a ring in which the driver writes descriptor numbers // that the driver would like the device to process. it only // includes the head descriptor of each chain. the ring has // NUM elements. + // points into pages[]. struct virtq_avail *avail; - // a ring in which the device writes descriptor numbers that + // finally a ring in which the device writes descriptor numbers that // the device has finished processing (just the head of each chain). // there are NUM used ring entries. + // points into pages[]. struct virtq_used *used; // our own book-keeping. @@ -56,7 +71,7 @@ static struct disk { struct spinlock vdisk_lock; -} disk; +} __attribute__ ((aligned (PGSIZE))) disk; void virtio_disk_init(void) @@ -66,20 +81,15 @@ virtio_disk_init(void) initlock(&disk.vdisk_lock, "virtio_disk"); if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || - *R(VIRTIO_MMIO_VERSION) != 2 || + *R(VIRTIO_MMIO_VERSION) != 1 || *R(VIRTIO_MMIO_DEVICE_ID) != 2 || *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){ panic("could not find virtio disk"); } - // reset device - *R(VIRTIO_MMIO_STATUS) = status; - - // set ACKNOWLEDGE status bit status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; *R(VIRTIO_MMIO_STATUS) = status; - // set DRIVER status bit status |= VIRTIO_CONFIG_S_DRIVER; *R(VIRTIO_MMIO_STATUS) = status; @@ -98,57 +108,35 @@ virtio_disk_init(void) status |= VIRTIO_CONFIG_S_FEATURES_OK; *R(VIRTIO_MMIO_STATUS) = status; - // re-read status to ensure FEATURES_OK is set. - status = *R(VIRTIO_MMIO_STATUS); - if(!(status & VIRTIO_CONFIG_S_FEATURES_OK)) - panic("virtio disk FEATURES_OK unset"); + // tell device we're completely ready. + status |= VIRTIO_CONFIG_S_DRIVER_OK; + *R(VIRTIO_MMIO_STATUS) = status; + + *R(VIRTIO_MMIO_GUEST_PAGE_SIZE) = PGSIZE; // initialize queue 0. *R(VIRTIO_MMIO_QUEUE_SEL) = 0; - - // ensure queue 0 is not in use. - if(*R(VIRTIO_MMIO_QUEUE_READY)) - panic("virtio disk should not be ready"); - - // check maximum queue size. uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX); if(max == 0) panic("virtio disk has no queue 0"); if(max < NUM) panic("virtio disk max queue too short"); - - // allocate and zero queue memory. - disk.desc = kalloc(); - disk.avail = kalloc(); - disk.used = kalloc(); - if(!disk.desc || !disk.avail || !disk.used) - panic("virtio disk kalloc"); - memset(disk.desc, 0, PGSIZE); - memset(disk.avail, 0, PGSIZE); - memset(disk.used, 0, PGSIZE); - - // set queue size. *R(VIRTIO_MMIO_QUEUE_NUM) = NUM; + memset(disk.pages, 0, sizeof(disk.pages)); + *R(VIRTIO_MMIO_QUEUE_PFN) = ((uint64)disk.pages) >> PGSHIFT; - // write physical addresses. - *R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc; - *R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32; - *R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail; - *R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32; - *R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used; - *R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32; + // desc = pages -- num * virtq_desc + // avail = pages + 0x40 -- 2 * uint16, then num * uint16 + // used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem - // queue is ready. - *R(VIRTIO_MMIO_QUEUE_READY) = 0x1; + disk.desc = (struct virtq_desc *) disk.pages; + disk.avail = (struct virtq_avail *)(disk.pages + NUM*sizeof(struct virtq_desc)); + disk.used = (struct virtq_used *) (disk.pages + PGSIZE); // all NUM descriptors start out unused. for(int i = 0; i < NUM; i++) disk.free[i] = 1; - // tell device we're completely ready. - status |= VIRTIO_CONFIG_S_DRIVER_OK; - *R(VIRTIO_MMIO_STATUS) = status; - // plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ. } diff --git a/kernel/vm.c b/kernel/vm.c index 62421a24f32ecd69bee06db5da9fdc76cd58def6..b47f1118326ae35cebb50fbbe48a4ac3e770d24e 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -31,7 +31,7 @@ kvmmake(void) kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); // PLIC - kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W); + kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W); // map kernel text executable and read-only. kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X); @@ -43,7 +43,7 @@ kvmmake(void) // the highest virtual address in the kernel. kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); - // allocate and map a kernel stack for each process. + // map kernel stacks proc_mapstacks(kpgtbl); return kpgtbl; @@ -61,12 +61,7 @@ kvminit(void) void kvminithart() { - // wait for any previous writes to the page table memory to finish. - sfence_vma(); - w_satp(MAKE_SATP(kernel_pagetable)); - - // flush stale entries from the TLB. sfence_vma(); } @@ -136,9 +131,8 @@ kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm) } // Create PTEs for virtual addresses starting at va that refer to -// physical addresses starting at pa. -// va and size MUST be page-aligned. -// Returns 0 on success, -1 if walk() couldn't +// physical addresses starting at pa. va and size might not +// be page-aligned. Returns 0 on success, -1 if walk() couldn't // allocate a needed page-table page. int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) @@ -146,22 +140,13 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) uint64 a, last; pte_t *pte; - if((va % PGSIZE) != 0) - panic("mappages: va not aligned"); - - if((size % PGSIZE) != 0) - panic("mappages: size not aligned"); - - if(size == 0) - panic("mappages: size"); - - a = va; - last = va + size - PGSIZE; + a = PGROUNDDOWN(va); + last = PGROUNDDOWN(va + size - 1); for(;;){ if((pte = walk(pagetable, a, 1)) == 0) return -1; if(*pte & PTE_V) - panic("mappages: remap"); + panic("remap"); *pte = PA2PTE(pa) | perm | PTE_V; if(a == last) break; @@ -215,12 +200,12 @@ uvmcreate() // for the very first process. // sz must be less than a page. void -uvmfirst(pagetable_t pagetable, uchar *src, uint sz) +uvminit(pagetable_t pagetable, uchar *src, uint sz) { char *mem; if(sz >= PGSIZE) - panic("uvmfirst: more than a page"); + panic("inituvm: more than a page"); mem = kalloc(); memset(mem, 0, PGSIZE); mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); @@ -230,7 +215,7 @@ uvmfirst(pagetable_t pagetable, uchar *src, uint sz) // Allocate PTEs and physical memory to grow process from oldsz to // newsz, which need not be page aligned. Returns new size or 0 on error. uint64 -uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) +uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) { char *mem; uint64 a; @@ -246,7 +231,7 @@ uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) return 0; } memset(mem, 0, PGSIZE); - if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){ + if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){ kfree(mem); uvmdealloc(pagetable, a, oldsz); return 0; @@ -359,17 +344,12 @@ int copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) { uint64 n, va0, pa0; - pte_t *pte; while(len > 0){ va0 = PGROUNDDOWN(dstva); - if(va0 >= MAXVA) - return -1; - pte = walk(pagetable, va0, 0); - if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 || - (*pte & PTE_W) == 0) + pa0 = walkaddr(pagetable, va0); + if(pa0 == 0) return -1; - pa0 = PTE2PA(*pte); n = PGSIZE - (dstva - va0); if(n > len) n = len; diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index f39983d7bf99ccf2bc7022e43af61bad21ed26fa..246a4e24ab4333a38c38003ee24f006a9dbdd4ea 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -20,7 +20,7 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/BPB + 1; +int nbitmap = FSSIZE/(BSIZE*8) + 1; int ninodeblocks = NINODES / IPB + 1; int nlog = LOGSIZE; int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) @@ -40,9 +40,8 @@ void rinode(uint inum, struct dinode *ip); void rsect(uint sec, void *buf); uint ialloc(ushort type); void iappend(uint inum, void *p, int n); -void die(const char *); -// convert to riscv byte order +// convert to intel byte order ushort xshort(ushort x) { @@ -86,8 +85,10 @@ main(int argc, char *argv[]) assert((BSIZE % sizeof(struct dirent)) == 0); fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); - if(fsfd < 0) - die(argv[1]); + if(fsfd < 0){ + perror(argv[1]); + exit(1); + } // 1 fs block = 1 disk sector nmeta = 2 + nlog + ninodeblocks + nbitmap; @@ -137,8 +138,10 @@ main(int argc, char *argv[]) assert(index(shortname, '/') == 0); - if((fd = open(argv[i], 0)) < 0) - die(argv[i]); + if((fd = open(argv[i], 0)) < 0){ + perror(argv[i]); + exit(1); + } // Skip leading _ in name when writing to file system. // The binaries are named _rm, _cat, etc. to keep the @@ -147,8 +150,6 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; - assert(strlen(shortname) <= DIRSIZ); - inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -177,10 +178,14 @@ main(int argc, char *argv[]) void wsect(uint sec, void *buf) { - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) - die("lseek"); - if(write(fsfd, buf, BSIZE) != BSIZE) - die("write"); + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ + perror("lseek"); + exit(1); + } + if(write(fsfd, buf, BSIZE) != BSIZE){ + perror("write"); + exit(1); + } } void @@ -213,10 +218,14 @@ rinode(uint inum, struct dinode *ip) void rsect(uint sec, void *buf) { - if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) - die("lseek"); - if(read(fsfd, buf, BSIZE) != BSIZE) - die("read"); + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){ + perror("lseek"); + exit(1); + } + if(read(fsfd, buf, BSIZE) != BSIZE){ + perror("read"); + exit(1); + } } uint @@ -240,7 +249,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BPB); + assert(used < BSIZE*8); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); @@ -294,10 +303,3 @@ iappend(uint inum, void *xp, int n) din.size = xint(off); winode(inum, &din); } - -void -die(const char *s) -{ - perror(s); - exit(1); -} diff --git a/user/bigfile.c b/user/bigfile.c index f4ebe4caf46829daaa2f5f930860a4b1e8293659..0755700a5faf29f143dd987f4bf336b17980bf7f 100644 --- a/user/bigfile.c +++ b/user/bigfile.c @@ -8,7 +8,7 @@ int main() { char buf[BSIZE]; - int fd, i, blocks, readblocks; + int fd, i, blocks; fd = open("big.file", O_CREATE | O_WRONLY); if(fd < 0){ @@ -35,12 +35,10 @@ main() close(fd); fd = open("big.file", O_RDONLY); - printf("reading bigfile\n"); if(fd < 0){ printf("bigfile: cannot re-open big.file for reading\n"); exit(-1); } - readblocks = 0; for(i = 0; i < blocks; i++){ int cc = read(fd, buf, sizeof(buf)); if(cc <= 0){ @@ -52,12 +50,9 @@ main() *(int*)buf, i); exit(-1); } - readblocks++; - if (readblocks % 100 == 0) - printf("."); } - printf("\nbigfile done; ok\n"); + printf("bigfile done; ok\n"); exit(0); } diff --git a/user/cat.c b/user/cat.c index 6d873a9705a6a8df529b42d4d50dbbe964e459ff..598f0050d21b06da638eea69ddc5e44b072f4c45 100644 --- a/user/cat.c +++ b/user/cat.c @@ -1,5 +1,5 @@ #include "kernel/types.h" -#include "kernel/fcntl.h" +#include "kernel/stat.h" #include "user/user.h" char buf[512]; @@ -32,7 +32,7 @@ main(int argc, char *argv[]) } for(i = 1; i < argc; i++){ - if((fd = open(argv[i], O_RDONLY)) < 0){ + if((fd = open(argv[i], 0)) < 0){ fprintf(2, "cat: cannot open %s\n", argv[i]); exit(1); } diff --git a/user/grep.c b/user/grep.c index 6c33766adc9da5b8e623ff5ec439272c7466faac..19882b9412e29c259dd9ccc2beb4d3d9b50f40a9 100644 --- a/user/grep.c +++ b/user/grep.c @@ -2,7 +2,6 @@ #include "kernel/types.h" #include "kernel/stat.h" -#include "kernel/fcntl.h" #include "user/user.h" char buf[1024]; @@ -52,7 +51,7 @@ main(int argc, char *argv[]) } for(i = 2; i < argc; i++){ - if((fd = open(argv[i], O_RDONLY)) < 0){ + if((fd = open(argv[i], 0)) < 0){ printf("grep: cannot open %s\n", argv[i]); exit(1); } @@ -63,8 +62,7 @@ main(int argc, char *argv[]) } // Regexp matcher from Kernighan & Pike, -// The Practice of Programming, Chapter 9, or -// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html +// The Practice of Programming, Chapter 9. int matchhere(char*, char*); int matchstar(int, char*, char*); diff --git a/user/grind.c b/user/grind.c index 431ed19952ca4852a8367a6e01840adbf3c85d9f..85899f5ca43686711bb9324c5c07745c19c1addd 100644 --- a/user/grind.c +++ b/user/grind.c @@ -277,15 +277,14 @@ go(int which_child) close(aa[0]); close(aa[1]); close(bb[1]); - char buf[4] = { 0, 0, 0, 0 }; + char buf[3] = { 0, 0, 0 }; read(bb[0], buf+0, 1); read(bb[0], buf+1, 1); - read(bb[0], buf+2, 1); close(bb[0]); int st1, st2; wait(&st1); wait(&st2); - if(st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0){ + if(st1 != 0 || st2 != 0 || strcmp(buf, "hi") != 0){ printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); exit(1); } @@ -305,7 +304,7 @@ iter() exit(1); } if(pid1 == 0){ - rand_next ^= 31; + rand_next = 31; go(0); exit(0); } @@ -316,7 +315,7 @@ iter() exit(1); } if(pid2 == 0){ - rand_next ^= 7177; + rand_next = 7177; go(1); exit(0); } @@ -346,6 +345,5 @@ main() wait(0); } sleep(20); - rand_next += 1; } } diff --git a/user/initcode.S b/user/initcode.S index ba44ff57b07528acdb50afcc7317e4d0a2dc5071..e8f7a91f01fa070b13b4bcbe73c9e9389aa65ba4 100644 --- a/user/initcode.S +++ b/user/initcode.S @@ -24,5 +24,5 @@ init: # char *argv[] = { init, 0 }; .p2align 2 argv: - .quad init - .quad 0 + .long init + .long 0 diff --git a/user/ls.c b/user/ls.c index 39ab074779ae36dbb937744e8d44168f317e7a0c..b54d9510b2a0f4d035d40d62bcc2d1557900412c 100644 --- a/user/ls.c +++ b/user/ls.c @@ -2,7 +2,6 @@ #include "kernel/stat.h" #include "user/user.h" #include "kernel/fs.h" -#include "kernel/fcntl.h" char* fmtname(char *path) @@ -31,7 +30,7 @@ ls(char *path) struct dirent de; struct stat st; - if((fd = open(path, O_RDONLY)) < 0){ + if((fd = open(path, 0)) < 0){ fprintf(2, "ls: cannot open %s\n", path); return; } @@ -43,9 +42,8 @@ ls(char *path) } switch(st.type){ - case T_DEVICE: case T_FILE: - printf("%s %d %d %d\n", fmtname(path), st.type, st.ino, (int) st.size); + printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size); break; case T_DIR: @@ -65,7 +63,7 @@ ls(char *path) printf("ls: cannot stat %s\n", buf); continue; } - printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, (int) st.size); + printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); } break; } diff --git a/user/printf.c b/user/printf.c index 81787467fa7910a827f21abe7cae444ca2213cb4..5c5c78295b154d15e7a0bfb57729a8919bff70ef 100644 --- a/user/printf.c +++ b/user/printf.c @@ -52,61 +52,18 @@ void vprintf(int fd, const char *fmt, va_list ap) { char *s; - int c0, c1, c2, i, state; + int c, i, state; state = 0; for(i = 0; fmt[i]; i++){ - c0 = fmt[i] & 0xff; + c = fmt[i] & 0xff; if(state == 0){ - if(c0 == '%'){ + if(c == '%'){ state = '%'; } else { - putc(fd, c0); + putc(fd, c); } } else if(state == '%'){ - c1 = c2 = 0; - if(c0) c1 = fmt[i+1] & 0xff; - if(c1) c2 = fmt[i+2] & 0xff; - if(c0 == 'd'){ - printint(fd, va_arg(ap, int), 10, 1); - } else if(c0 == 'l' && c1 == 'd'){ - printint(fd, va_arg(ap, uint64), 10, 1); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'd'){ - printint(fd, va_arg(ap, uint64), 10, 1); - i += 2; - } else if(c0 == 'u'){ - printint(fd, va_arg(ap, int), 10, 0); - } else if(c0 == 'l' && c1 == 'u'){ - printint(fd, va_arg(ap, uint64), 10, 0); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'u'){ - printint(fd, va_arg(ap, uint64), 10, 0); - i += 2; - } else if(c0 == 'x'){ - printint(fd, va_arg(ap, int), 16, 0); - } else if(c0 == 'l' && c1 == 'x'){ - printint(fd, va_arg(ap, uint64), 16, 0); - i += 1; - } else if(c0 == 'l' && c1 == 'l' && c2 == 'x'){ - printint(fd, va_arg(ap, uint64), 16, 0); - i += 2; - } else if(c0 == 'p'){ - printptr(fd, va_arg(ap, uint64)); - } else if(c0 == 's'){ - if((s = va_arg(ap, char*)) == 0) - s = "(null)"; - for(; *s; s++) - putc(fd, *s); - } else if(c0 == '%'){ - putc(fd, '%'); - } else { - // Unknown % sequence. Print it to draw attention. - putc(fd, '%'); - putc(fd, c0); - } - -#if 0 if(c == 'd'){ printint(fd, va_arg(ap, int), 10, 1); } else if(c == 'l') { @@ -132,7 +89,6 @@ vprintf(int fd, const char *fmt, va_list ap) putc(fd, '%'); putc(fd, c); } -#endif state = 0; } } diff --git a/user/sh.c b/user/sh.c index 836ebcbc7eba2f8133085b96c376a20d9e7ce565..83dd51351b264947b1641c754e64cca1bb36c136 100644 --- a/user/sh.c +++ b/user/sh.c @@ -52,7 +52,6 @@ struct backcmd { int fork1(void); // Fork but panics on failure. void panic(char*); struct cmd *parsecmd(char*); -void runcmd(struct cmd*) __attribute__((noreturn)); // Execute cmd. Never returns. void @@ -134,7 +133,7 @@ runcmd(struct cmd *cmd) int getcmd(char *buf, int nbuf) { - write(2, "$ ", 2); + fprintf(2, "$ "); memset(buf, 0, nbuf); gets(buf, nbuf); if(buf[0] == 0) // EOF diff --git a/user/symlinktest.c b/user/symlinktest.c index 781fc9eb6e96c539707d6c10126ab0948275780d..ac6e31c1400b863279ab73e40a27791559b8d9f4 100644 --- a/user/symlinktest.c +++ b/user/symlinktest.c @@ -37,15 +37,6 @@ cleanup(void) unlink("/testsymlink/4"); unlink("/testsymlink/z"); unlink("/testsymlink/y"); - for(int i = 0; i < NINODE+2; i++){ - char name[32]; - memset(name, 0, sizeof(name)); - const char *base = "/testsymlink/"; - strcpy(name, base); - name[strlen(base)+0] = 'a' + (i / 26); - name[strlen(base)+1] = 'a' + (i % 26); - unlink(name); - } unlink("/testsymlink"); } @@ -120,7 +111,6 @@ testsymlink(void) close(fd1); close(fd2); - fd1 = fd2 = -1; fd1 = open("/testsymlink/4", O_CREATE | O_RDWR); if(fd1<0) fail("Failed to create 4\n"); @@ -135,52 +125,6 @@ testsymlink(void) if(c!=c2) fail("Value read from 4 differed from value written to 1\n"); - close(fd1); - close(fd2); - fd1 = fd2 = -1; - - // - // check that many symlinks can co-exist. - // - for(int i = 0; i < NINODE+2; i++){ - char name[32]; - memset(name, 0, sizeof(name)); - const char *base = "/testsymlink/"; - strcpy(name, base); - name[strlen(base)+0] = 'a' + (i / 26); - name[strlen(base)+1] = 'a' + (i % 26); - r = symlink("/testsymlink/4", name); - if(r) fail("symlink() failed in many test"); - } - for(int i = 0; i < NINODE+2; i++){ - char name[32]; - memset(name, 0, sizeof(name)); - const char *base = "/testsymlink/"; - strcpy(name, base); - name[strlen(base)+0] = 'a' + (i / 26); - name[strlen(base)+1] = 'a' + (i % 26); - fd1 = open(name, O_RDONLY); - if(fd1 < 0) - fail("open() failed in many test"); - char buf[16]; - buf[0] = '\0'; - if(read(fd1, buf, sizeof(buf)) != 1) - fail("read() failed in many test"); - if(buf[0] != '#') - fail("wrong content in many test"); - close(fd1); - fd1 = -1; - } - - unlink("/testsymlink/a"); - if(symlink("/README", "/testsymlink/a") != 0) - fail("could not link to /README"); - fd1 = open("/testsymlink/a", O_RDONLY); - if(fd1 < 0) - fail("could not open symlink pointing to /README"); - close(fd1); - fd1 = -1; - printf("test symlinks: ok\n"); done: close(fd1); @@ -220,7 +164,7 @@ concur(void) if (stat_slink("/testsymlink/y", &st) == 0) { m++; if(st.type != T_SYMLINK) { - printf("FAILED: type %d not a symbolic link\n", st.type); + printf("FAILED: not a symbolic link\n", st.type); exit(1); } } diff --git a/user/ulib.c b/user/ulib.c index 00648466f35d018b609dc4b8c162c8a7a48d32fb..4775939f416a17678fe7ba736708c5a88d676d4b 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -3,17 +3,6 @@ #include "kernel/fcntl.h" #include "user/user.h" -// -// wrapper so that it's OK if main() does not call exit(). -// -void -start() -{ - extern int main(); - main(); - exit(0); -} - char* strcpy(char *s, const char *t) { diff --git a/user/user.h b/user/user.h index f16fe278281e40a79abfa4c734700e9fcf1f5dda..b71ecda2b66d940b2dcc52fcd3673d5d91b3f9f1 100644 --- a/user/user.h +++ b/user/user.h @@ -1,4 +1,5 @@ struct stat; +struct rtcdate; // system calls int fork(void); @@ -9,7 +10,7 @@ int write(int, const void*, int); int read(int, void*, int); int close(int); int kill(int); -int exec(const char*, char**); +int exec(char*, char**); int open(const char*, int); int mknod(const char*, short, short); int unlink(const char*); @@ -29,15 +30,13 @@ char* strcpy(char*, const char*); void *memmove(void*, const void*, int); char* strchr(const char*, char c); int strcmp(const char*, const char*); -void fprintf(int, const char*, ...) __attribute__ ((format (printf, 2, 3))); -void printf(const char*, ...) __attribute__ ((format (printf, 1, 2))); +void fprintf(int, const char*, ...); +void printf(const char*, ...); char* gets(char*, int max); uint strlen(const char*); void* memset(void*, int, uint); +void* malloc(uint); +void free(void*); int atoi(const char*); int memcmp(const void *, const void *, uint); void *memcpy(void *, const void *, uint); - -// umalloc.c -void* malloc(uint); -void free(void*); diff --git a/user/user.ld b/user/user.ld deleted file mode 100644 index 3da93e00d561e260fbe36584494ca8759dff698d..0000000000000000000000000000000000000000 --- a/user/user.ld +++ /dev/null @@ -1,39 +0,0 @@ -OUTPUT_ARCH( "riscv" ) - -SECTIONS -{ - . = 0x0; - - .text : { - *(.text .text.*) - } - - .rodata : { - . = ALIGN(16); - *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ - . = ALIGN(16); - *(.rodata .rodata.*) - } - - .eh_frame : { - *(.eh_frame) - *(.eh_frame.*) - } - - . = ALIGN(0x1000); - .data : { - . = ALIGN(16); - *(.sdata .sdata.*) /* do not need to distinguish this from .data */ - . = ALIGN(16); - *(.data .data.*) - } - - .bss : { - . = ALIGN(16); - *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ - . = ALIGN(16); - *(.bss .bss.*) - } - - PROVIDE(end = .); -} diff --git a/user/usertests.c b/user/usertests.c index 28b53f9a6d4c21ea1bcf489359d4ea94abc472d9..ec6630d98ab7342c5eeda7093c850dab0c2963cf 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -21,21 +21,14 @@ char buf[BUFSZ]; -// -// Section with tests that run fairly quickly. Use -q if you want to -// run just those. With -q usertests also runs the ones that take a -// fair of time. -// - // what if you pass ridiculous pointers to system calls // that read user memory with copyin? void copyin(char *s) { - uint64 addrs[] = { 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000, - 0xffffffffffffffff }; + uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; - for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){ + for(int ai = 0; ai < 2; ai++){ uint64 addr = addrs[ai]; int fd = open("copyin1", O_CREATE|O_WRONLY); @@ -45,7 +38,7 @@ copyin(char *s) } int n = write(fd, (void*)addr, 8192); if(n >= 0){ - printf("write(fd, %p, 8192) returned %d, not -1\n", (void*)addr, n); + printf("write(fd, %p, 8192) returned %d, not -1\n", addr, n); exit(1); } close(fd); @@ -53,7 +46,7 @@ copyin(char *s) n = write(1, (char*)addr, 8192); if(n > 0){ - printf("write(1, %p, 8192) returned %d, not -1 or 0\n", (void*)addr, n); + printf("write(1, %p, 8192) returned %d, not -1 or 0\n", addr, n); exit(1); } @@ -64,7 +57,7 @@ copyin(char *s) } n = write(fds[1], (char*)addr, 8192); if(n > 0){ - printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", (void*)addr, n); + printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); exit(1); } close(fds[0]); @@ -77,10 +70,9 @@ copyin(char *s) void copyout(char *s) { - uint64 addrs[] = { 0LL, 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000, - 0xffffffffffffffff }; + uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; - for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){ + for(int ai = 0; ai < 2; ai++){ uint64 addr = addrs[ai]; int fd = open("README", 0); @@ -90,7 +82,7 @@ copyout(char *s) } int n = read(fd, (void*)addr, 8192); if(n > 0){ - printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", (void*)addr, n); + printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", addr, n); exit(1); } close(fd); @@ -107,7 +99,7 @@ copyout(char *s) } n = read(fds[0], (void*)addr, 8192); if(n > 0){ - printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", (void*)addr, n); + printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); exit(1); } close(fds[0]); @@ -119,15 +111,14 @@ copyout(char *s) void copyinstr1(char *s) { - uint64 addrs[] = { 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000, - 0xffffffffffffffff }; + uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; - for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){ + for(int ai = 0; ai < 2; ai++){ uint64 addr = addrs[ai]; int fd = open((char *)addr, O_CREATE|O_WRONLY); if(fd >= 0){ - printf("open(%p) returned %d, not -1\n", (void*)addr, fd); + printf("open(%p) returned %d, not -1\n", addr, fd); exit(1); } } @@ -267,7 +258,7 @@ rwsbrk() } n = write(fd, (void*)(a+4096), 1024); if(n >= 0){ - printf("write(fd, %p, 1024) returned %d, not -1\n", (void*)a+4096, n); + printf("write(fd, %p, 1024) returned %d, not -1\n", a+4096, n); exit(1); } close(fd); @@ -280,7 +271,7 @@ rwsbrk() } n = read(fd, (void*)(a+4096), 10); if(n >= 0){ - printf("read(fd, %p, 10) returned %d, not -1\n", (void*)a+4096, n); + printf("read(fd, %p, 10) returned %d, not -1\n", a+4096, n); exit(1); } close(fd); @@ -592,7 +583,7 @@ writebig(char *s) for(i = 0; i < MAXFILE; i++){ ((int*)buf)[0] = i; if(write(fd, buf, BSIZE) != BSIZE){ - printf("%s: error: write big file failed i=%d\n", s, i); + printf("%s: error: write big file failed\n", s, i); exit(1); } } @@ -609,7 +600,7 @@ writebig(char *s) for(;;){ i = read(fd, buf, BSIZE); if(i == 0){ - if(n != MAXFILE){ + if(n == MAXFILE - 1){ printf("%s: read only %d blocks from big", s, n); exit(1); } @@ -776,7 +767,7 @@ pipe1(char *s) cc = sizeof(buf); } if(total != N * SZ){ - printf("%s: pipe1 oops 3 total %d\n", s, total); + printf("%s: pipe1 oops 3 total %d\n", total); exit(1); } close(fds[0]); @@ -788,36 +779,6 @@ pipe1(char *s) } } - -// test if child is killed (status = -1) -void -killstatus(char *s) -{ - int xst; - - for(int i = 0; i < 100; i++){ - int pid1 = fork(); - if(pid1 < 0){ - printf("%s: fork failed\n", s); - exit(1); - } - if(pid1 == 0){ - while(1) { - getpid(); - } - exit(0); - } - sleep(1); - kill(pid1); - wait(&xst); - if(xst != -1) { - printf("%s: status should be -1\n", s); - exit(1); - } - } - exit(0); -} - // meant to be run w/ at most two CPUs void preempt(char *s) @@ -1072,7 +1033,7 @@ mem(char *s) } m1 = malloc(1024*20); if(m1 == 0){ - printf("%s: couldn't allocate mem?!!\n", s); + printf("couldn't allocate mem?!!\n", s); exit(1); } free(m1); @@ -1164,14 +1125,14 @@ fourfiles(char *s) pid = fork(); if(pid < 0){ - printf("%s: fork failed\n", s); + printf("fork failed\n", s); exit(1); } if(pid == 0){ fd = open(fname, O_CREATE | O_RDWR); if(fd < 0){ - printf("%s: create failed\n", s); + printf("create failed\n", s); exit(1); } @@ -1200,7 +1161,7 @@ fourfiles(char *s) while((n = read(fd, buf, sizeof(buf))) > 0){ for(j = 0; j < n; j++){ if(buf[j] != '0'+i){ - printf("%s: wrong char\n", s); + printf("wrong char\n", s); exit(1); } } @@ -1226,7 +1187,7 @@ createdelete(char *s) for(pi = 0; pi < NCHILD; pi++){ pid = fork(); if(pid < 0){ - printf("%s: fork failed\n", s); + printf("fork failed\n", s); exit(1); } @@ -1280,7 +1241,7 @@ createdelete(char *s) for(i = 0; i < N; i++){ for(pi = 0; pi < NCHILD; pi++){ - name[0] = 'p' + pi; + name[0] = 'p' + i; name[1] = '0' + i; unlink(name); } @@ -1381,7 +1342,7 @@ linktest(char *s) unlink("lf2"); if(link("lf2", "lf1") >= 0){ - printf("%s: link non-existent succeeded! oops\n", s); + printf("%s: link non-existant succeeded! oops\n", s); exit(1); } @@ -1521,6 +1482,46 @@ linkunlink(char *s) exit(0); } +// directory that uses indirect blocks +void +bigdir(char *s) +{ + enum { N = 500 }; + int i, fd; + char name[10]; + + unlink("bd"); + + fd = open("bd", O_CREATE); + if(fd < 0){ + printf("%s: bigdir create failed\n", s); + exit(1); + } + close(fd); + + for(i = 0; i < N; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(link("bd", name) != 0){ + printf("%s: bigdir link(bd, %s) failed\n", s, name); + exit(1); + } + } + + unlink("bd"); + for(i = 0; i < N; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(unlink(name) != 0){ + printf("%s: bigdir unlink failed", s); + exit(1); + } + } +} void subdir(char *s) @@ -1547,7 +1548,7 @@ subdir(char *s) } if(mkdir("/dd/dd") != 0){ - printf("%s: subdir mkdir dd/dd failed\n", s); + printf("subdir mkdir dd/dd failed\n", s); exit(1); } @@ -1572,7 +1573,7 @@ subdir(char *s) close(fd); if(link("dd/dd/ff", "dd/dd/ffff") != 0){ - printf("%s: link dd/dd/ff dd/dd/ffff failed\n", s); + printf("link dd/dd/ff dd/dd/ffff failed\n", s); exit(1); } @@ -1594,7 +1595,7 @@ subdir(char *s) exit(1); } if(chdir("dd/../../../dd") != 0){ - printf("%s: chdir dd/../../../dd failed\n", s); + printf("chdir dd/../../dd failed\n", s); exit(1); } if(chdir("./..") != 0){ @@ -1727,6 +1728,59 @@ bigwrite(char *s) } } +// concurrent writes to try to provoke deadlock in the virtio disk +// driver. +void +manywrites(char *s) +{ + int nchildren = 4; + int howmany = 30; // increase to look for deadlock + + for(int ci = 0; ci < nchildren; ci++){ + int pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(1); + } + + if(pid == 0){ + char name[3]; + name[0] = 'b'; + name[1] = 'a' + ci; + name[2] = '\0'; + unlink(name); + + for(int iters = 0; iters < howmany; iters++){ + for(int i = 0; i < ci+1; i++){ + int fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf("%s: cannot create %s\n", s, name); + exit(1); + } + int sz = sizeof(buf); + int cc = write(fd, buf, sz); + if(cc != sz){ + printf("%s: write(%d) ret %d\n", s, sz, cc); + exit(1); + } + close(fd); + } + unlink(name); + } + + unlink(name); + exit(0); + } + } + + for(int ci = 0; ci < nchildren; ci++){ + int st = 0; + wait(&st); + if(st != 0) + exit(st); + } + exit(0); +} void bigfile(char *s) @@ -2037,7 +2091,7 @@ sbrkbasic(char *s) for(i = 0; i < 5000; i++){ b = sbrk(1); if(b != a){ - printf("%s: sbrk test failed %d %p %p\n", s, i, a, b); + printf("%s: sbrk test failed %d %x %x\n", i, a, b); exit(1); } *b = 1; @@ -2095,7 +2149,7 @@ sbrkmuch(char *s) } c = sbrk(0); if(c != a - PGSIZE){ - printf("%s: sbrk deallocation produced wrong address, a %p c %p\n", s, a, c); + printf("%s: sbrk deallocation produced wrong address, a %x c %x\n", s, a, c); exit(1); } @@ -2103,7 +2157,7 @@ sbrkmuch(char *s) a = sbrk(0); c = sbrk(PGSIZE); if(c != a || sbrk(0) != a + PGSIZE){ - printf("%s: sbrk re-allocation failed, a %p c %p\n", s, a, c); + printf("%s: sbrk re-allocation failed, a %x c %x\n", s, a, c); exit(1); } if(*lastaddr == 99){ @@ -2115,7 +2169,7 @@ sbrkmuch(char *s) a = sbrk(0); c = sbrk(-(sbrk(0) - oldbrk)); if(c != a){ - printf("%s: sbrk downsize failed, a %p c %p\n", s, a, c); + printf("%s: sbrk downsize failed, a %x c %x\n", s, a, c); exit(1); } } @@ -2134,31 +2188,7 @@ kernmem(char *s) exit(1); } if(pid == 0){ - printf("%s: oops could read %p = %x\n", s, a, *a); - exit(1); - } - int xstatus; - wait(&xstatus); - if(xstatus != -1) // did kernel kill child? - exit(1); - } -} - -// user code should not be able to write to addresses above MAXVA. -void -MAXVAplus(char *s) -{ - volatile uint64 a = MAXVA; - for( ; a != 0; a <<= 1){ - int pid; - pid = fork(); - if(pid < 0){ - printf("%s: fork failed\n", s); - exit(1); - } - if(pid == 0){ - *(char*)a = 99; - printf("%s: oops wrote %p\n", s, (void*)a); + printf("%s: oops could read %x = %x\n", s, a, *a); exit(1); } int xstatus; @@ -2282,7 +2312,7 @@ validatetest(char *s) } } -// does uninitialized data start out zero? +// does unintialized data start out zero? char uninit[10000]; void bsstest(char *s) @@ -2310,14 +2340,9 @@ bigargtest(char *s) if(pid == 0){ static char *args[MAXARG]; int i; - char big[400]; - memset(big, ' ', sizeof(big)); - big[sizeof(big)-1] = '\0'; for(i = 0; i < MAXARG-1; i++) - args[i] = big; + args[i] = "bigargs test: failed\n "; args[MAXARG-1] = 0; - // this exec() should fail (and return) because the - // arguments are too large. exec("echo", args); fd = open("bigarg-ok", O_CREATE); close(fd); @@ -2403,6 +2428,14 @@ void argptest(char *s) close(fd); } +unsigned long randstate = 1; +unsigned int +rand() +{ + randstate = randstate * 1664525 + 1013904223; + return randstate; +} + // check that there's an invalid page beneath // the user stack, to catch stack overflow. void @@ -2414,9 +2447,9 @@ stacktest(char *s) pid = fork(); if(pid == 0) { char *sp = (char *) r_sp(); - sp -= USERSTACK*PGSIZE; + sp -= PGSIZE; // the *sp should cause a trap. - printf("%s: stacktest: read below stack %d\n", s, *sp); + printf("%s: stacktest: read below stack %p\n", s, *sp); exit(1); } else if(pid < 0){ printf("%s: fork failed\n", s); @@ -2429,47 +2462,17 @@ stacktest(char *s) exit(xstatus); } -// check that writes to a few forbidden addresses -// cause a fault, e.g. process's text and TRAMPOLINE. -void -nowrite(char *s) -{ - int pid; - int xstatus; - uint64 addrs[] = { 0, 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000, - 0xffffffffffffffff }; - - for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){ - pid = fork(); - if(pid == 0) { - volatile int *addr = (int *) addrs[ai]; - *addr = 10; - printf("%s: write to %p did not fail!\n", s, addr); - exit(0); - } else if(pid < 0){ - printf("%s: fork failed\n", s); - exit(1); - } - wait(&xstatus); - if(xstatus == 0){ - // kernel did not kill child! - exit(1); - } - } - exit(0); -} - // regression test. copyin(), copyout(), and copyinstr() used to cast // the virtual page address to uint, which (with certain wild system // call arguments) resulted in a kernel page faults. -void *big = (void*) 0xeaeb0b5b00002f5e; void pgbug(char *s) { char *argv[1]; argv[0] = 0; - exec(big, argv); - pipe(big); + exec((char*)0xeaeb0b5b00002f5e, argv); + + pipe((int*)0xeaeb0b5b00002f5e); exit(0); } @@ -2532,227 +2535,6 @@ sbrkbugs(char *s) exit(0); } -// if process size was somewhat more than a page boundary, and then -// shrunk to be somewhat less than that page boundary, can the kernel -// still copyin() from addresses in the last page? -void -sbrklast(char *s) -{ - uint64 top = (uint64) sbrk(0); - if((top % 4096) != 0) - sbrk(4096 - (top % 4096)); - sbrk(4096); - sbrk(10); - sbrk(-20); - top = (uint64) sbrk(0); - char *p = (char *) (top - 64); - p[0] = 'x'; - p[1] = '\0'; - int fd = open(p, O_RDWR|O_CREATE); - write(fd, p, 1); - close(fd); - fd = open(p, O_RDWR); - p[0] = '\0'; - read(fd, p, 1); - if(p[0] != 'x') - exit(1); -} - - -// does sbrk handle signed int32 wrap-around with -// negative arguments? -void -sbrk8000(char *s) -{ - sbrk(0x80000004); - volatile char *top = sbrk(0); - *(top-1) = *(top-1) + 1; -} - - - -// regression test. test whether exec() leaks memory if one of the -// arguments is invalid. the test passes if the kernel doesn't panic. -void -badarg(char *s) -{ - for(int i = 0; i < 50000; i++){ - char *argv[2]; - argv[0] = (char*)0xffffffff; - argv[1] = 0; - exec("echo", argv); - } - - exit(0); -} - -struct test { - void (*f)(char *); - char *s; -} quicktests[] = { - {copyin, "copyin"}, - {copyout, "copyout"}, - {copyinstr1, "copyinstr1"}, - {copyinstr2, "copyinstr2"}, - {copyinstr3, "copyinstr3"}, - {rwsbrk, "rwsbrk" }, - {truncate1, "truncate1"}, - {truncate2, "truncate2"}, - {truncate3, "truncate3"}, - {openiputtest, "openiput"}, - {exitiputtest, "exitiput"}, - {iputtest, "iput"}, - {opentest, "opentest"}, - {writetest, "writetest"}, - {writebig, "writebig"}, - {createtest, "createtest"}, - {dirtest, "dirtest"}, - {exectest, "exectest"}, - {pipe1, "pipe1"}, - {killstatus, "killstatus"}, - {preempt, "preempt"}, - {exitwait, "exitwait"}, - {reparent, "reparent" }, - {twochildren, "twochildren"}, - {forkfork, "forkfork"}, - {forkforkfork, "forkforkfork"}, - {reparent2, "reparent2"}, - {mem, "mem"}, - {sharedfd, "sharedfd"}, - {fourfiles, "fourfiles"}, - {createdelete, "createdelete"}, - {unlinkread, "unlinkread"}, - {linktest, "linktest"}, - {concreate, "concreate"}, - {linkunlink, "linkunlink"}, - {subdir, "subdir"}, - {bigwrite, "bigwrite"}, - {bigfile, "bigfile"}, - {fourteen, "fourteen"}, - {rmdot, "rmdot"}, - {dirfile, "dirfile"}, - {iref, "iref"}, - {forktest, "forktest"}, - {sbrkbasic, "sbrkbasic"}, - {sbrkmuch, "sbrkmuch"}, - {kernmem, "kernmem"}, - {MAXVAplus, "MAXVAplus"}, - {sbrkfail, "sbrkfail"}, - {sbrkarg, "sbrkarg"}, - {validatetest, "validatetest"}, - {bsstest, "bsstest"}, - {bigargtest, "bigargtest"}, - {argptest, "argptest"}, - {stacktest, "stacktest"}, - {nowrite, "nowrite"}, - {pgbug, "pgbug" }, - {sbrkbugs, "sbrkbugs" }, - {sbrklast, "sbrklast"}, - {sbrk8000, "sbrk8000"}, - {badarg, "badarg" }, - - { 0, 0}, -}; - -// -// Section with tests that take a fair bit of time -// - -// directory that uses indirect blocks -void -bigdir(char *s) -{ - enum { N = 500 }; - int i, fd; - char name[10]; - - unlink("bd"); - - fd = open("bd", O_CREATE); - if(fd < 0){ - printf("%s: bigdir create failed\n", s); - exit(1); - } - close(fd); - - for(i = 0; i < N; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(link("bd", name) != 0){ - printf("%s: bigdir i=%d link(bd, %s) failed\n", s, i, name); - exit(1); - } - } - - unlink("bd"); - for(i = 0; i < N; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(unlink(name) != 0){ - printf("%s: bigdir unlink failed", s); - exit(1); - } - } -} - -// concurrent writes to try to provoke deadlock in the virtio disk -// driver. -void -manywrites(char *s) -{ - int nchildren = 4; - int howmany = 30; // increase to look for deadlock - - for(int ci = 0; ci < nchildren; ci++){ - int pid = fork(); - if(pid < 0){ - printf("fork failed\n"); - exit(1); - } - - if(pid == 0){ - char name[3]; - name[0] = 'b'; - name[1] = 'a' + ci; - name[2] = '\0'; - unlink(name); - - for(int iters = 0; iters < howmany; iters++){ - for(int i = 0; i < ci+1; i++){ - int fd = open(name, O_CREATE | O_RDWR); - if(fd < 0){ - printf("%s: cannot create %s\n", s, name); - exit(1); - } - int sz = sizeof(buf); - int cc = write(fd, buf, sz); - if(cc != sz){ - printf("%s: write(%d) ret %d\n", s, sz, cc); - exit(1); - } - close(fd); - } - unlink(name); - } - - unlink(name); - exit(0); - } - } - - for(int ci = 0; ci < nchildren; ci++){ - int st = 0; - wait(&st); - if(st != 0) - exit(st); - } - exit(0); -} - // regression test. does write() with an invalid buffer pointer cause // a block to be allocated for a file that is then not freed when the // file is deleted? if the kernel has this bug, it will panic: balloc: @@ -2790,6 +2572,21 @@ badwrite(char *s) exit(0); } +// regression test. test whether exec() leaks memory if one of the +// arguments is invalid. the test passes if the kernel doesn't panic. +void +badarg(char *s) +{ + for(int i = 0; i < 50000; i++){ + char *argv[2]; + argv[0] = (char*)0xffffffff; + argv[1] = 0; + exec("echo", argv); + } + + exit(0); +} + // test the exec() code that cleans up if it runs out // of memory. it's really a test that such a condition // doesn't cause a panic. @@ -2827,174 +2624,6 @@ execout(char *s) exit(0); } -// can the kernel tolerate running out of disk space? -void -diskfull(char *s) -{ - int fi; - int done = 0; - - unlink("diskfulldir"); - - for(fi = 0; done == 0 && '0' + fi < 0177; fi++){ - char name[32]; - name[0] = 'b'; - name[1] = 'i'; - name[2] = 'g'; - name[3] = '0' + fi; - name[4] = '\0'; - unlink(name); - int fd = open(name, O_CREATE|O_RDWR|O_TRUNC); - if(fd < 0){ - // oops, ran out of inodes before running out of blocks. - printf("%s: could not create file %s\n", s, name); - done = 1; - break; - } - for(int i = 0; i < MAXFILE; i++){ - char buf[BSIZE]; - if(write(fd, buf, BSIZE) != BSIZE){ - done = 1; - close(fd); - break; - } - } - close(fd); - } - - // now that there are no free blocks, test that dirlink() - // merely fails (doesn't panic) if it can't extend - // directory content. one of these file creations - // is expected to fail. - int nzz = 128; - for(int i = 0; i < nzz; i++){ - char name[32]; - name[0] = 'z'; - name[1] = 'z'; - name[2] = '0' + (i / 32); - name[3] = '0' + (i % 32); - name[4] = '\0'; - unlink(name); - int fd = open(name, O_CREATE|O_RDWR|O_TRUNC); - if(fd < 0) - break; - close(fd); - } - - // this mkdir() is expected to fail. - if(mkdir("diskfulldir") == 0) - printf("%s: mkdir(diskfulldir) unexpectedly succeeded!\n", s); - - unlink("diskfulldir"); - - for(int i = 0; i < nzz; i++){ - char name[32]; - name[0] = 'z'; - name[1] = 'z'; - name[2] = '0' + (i / 32); - name[3] = '0' + (i % 32); - name[4] = '\0'; - unlink(name); - } - - for(int i = 0; '0' + i < 0177; i++){ - char name[32]; - name[0] = 'b'; - name[1] = 'i'; - name[2] = 'g'; - name[3] = '0' + i; - name[4] = '\0'; - unlink(name); - } -} - -void -outofinodes(char *s) -{ - int nzz = 32*32; - for(int i = 0; i < nzz; i++){ - char name[32]; - name[0] = 'z'; - name[1] = 'z'; - name[2] = '0' + (i / 32); - name[3] = '0' + (i % 32); - name[4] = '\0'; - unlink(name); - int fd = open(name, O_CREATE|O_RDWR|O_TRUNC); - if(fd < 0){ - // failure is eventually expected. - break; - } - close(fd); - } - - for(int i = 0; i < nzz; i++){ - char name[32]; - name[0] = 'z'; - name[1] = 'z'; - name[2] = '0' + (i / 32); - name[3] = '0' + (i % 32); - name[4] = '\0'; - unlink(name); - } -} - -struct test slowtests[] = { - {bigdir, "bigdir"}, - {manywrites, "manywrites"}, - {badwrite, "badwrite" }, - {execout, "execout"}, - {diskfull, "diskfull"}, - {outofinodes, "outofinodes"}, - - { 0, 0}, -}; - -// -// drive tests -// - -// run each test in its own process. run returns 1 if child's exit() -// indicates success. -int -run(void f(char *), char *s) { - int pid; - int xstatus; - - printf("test %s: ", s); - if((pid = fork()) < 0) { - printf("runtest: fork error\n"); - exit(1); - } - if(pid == 0) { - f(s); - exit(0); - } else { - wait(&xstatus); - if(xstatus != 0) - printf("FAILED\n"); - else - printf("OK\n"); - return xstatus == 0; - } -} - -int -runtests(struct test *tests, char *justone, int continuous) { - for (struct test *t = tests; t->s != 0; t++) { - if((justone == 0) || strcmp(t->s, justone) == 0) { - if(!run(t->f, t->s)){ - if(continuous != 2){ - printf("SOME TESTS FAILED\n"); - return 1; - } - } - } - } - return 0; -} - - // // use sbrk() to count how many free physical memory pages there are. // touches the pages to force allocation. @@ -3061,58 +2690,158 @@ countfree() return n; } +// run each test in its own process. run returns 1 if child's exit() +// indicates success. int -drivetests(int quick, int continuous, char *justone) { - do { - printf("usertests starting\n"); - int free0 = countfree(); - int free1 = 0; - if (runtests(quicktests, justone, continuous)) { - if(continuous != 2) { - return 1; - } - } - if(!quick) { - if (justone == 0) - printf("usertests slow tests starting\n"); - if (runtests(slowtests, justone, continuous)) { - if(continuous != 2) { - return 1; - } - } - } - if((free1 = countfree()) < free0) { - printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); - if(continuous != 2) { - return 1; - } - } - } while(continuous); - return 0; +run(void f(char *), char *s) { + int pid; + int xstatus; + + printf("test %s: ", s); + if((pid = fork()) < 0) { + printf("runtest: fork error\n"); + exit(1); + } + if(pid == 0) { + f(s); + exit(0); + } else { + wait(&xstatus); + if(xstatus != 0) + printf("FAILED\n"); + else + printf("OK\n"); + return xstatus == 0; + } } int main(int argc, char *argv[]) { int continuous = 0; - int quick = 0; char *justone = 0; - if(argc == 2 && strcmp(argv[1], "-q") == 0){ - quick = 1; - } else if(argc == 2 && strcmp(argv[1], "-c") == 0){ + if(argc == 2 && strcmp(argv[1], "-c") == 0){ continuous = 1; } else if(argc == 2 && strcmp(argv[1], "-C") == 0){ continuous = 2; } else if(argc == 2 && argv[1][0] != '-'){ justone = argv[1]; } else if(argc > 1){ - printf("Usage: usertests [-c] [-C] [-q] [testname]\n"); + printf("Usage: usertests [-c] [testname]\n"); exit(1); } - if (drivetests(quick, continuous, justone)) { + + struct test { + void (*f)(char *); + char *s; + } tests[] = { + {manywrites, "manywrites"}, + {execout, "execout"}, + {copyin, "copyin"}, + {copyout, "copyout"}, + {copyinstr1, "copyinstr1"}, + {copyinstr2, "copyinstr2"}, + {copyinstr3, "copyinstr3"}, + {rwsbrk, "rwsbrk" }, + {truncate1, "truncate1"}, + {truncate2, "truncate2"}, + {truncate3, "truncate3"}, + {reparent2, "reparent2"}, + {pgbug, "pgbug" }, + {sbrkbugs, "sbrkbugs" }, + // {badwrite, "badwrite" }, + {badarg, "badarg" }, + {reparent, "reparent" }, + {twochildren, "twochildren"}, + {forkfork, "forkfork"}, + {forkforkfork, "forkforkfork"}, + {argptest, "argptest"}, + {createdelete, "createdelete"}, + {linkunlink, "linkunlink"}, + {linktest, "linktest"}, + {unlinkread, "unlinkread"}, + {concreate, "concreate"}, + {subdir, "subdir"}, + {fourfiles, "fourfiles"}, + {sharedfd, "sharedfd"}, + {dirtest, "dirtest"}, + {exectest, "exectest"}, + {bigargtest, "bigargtest"}, + {bigwrite, "bigwrite"}, + {bsstest, "bsstest"}, + {sbrkbasic, "sbrkbasic"}, + {sbrkmuch, "sbrkmuch"}, + {kernmem, "kernmem"}, + {sbrkfail, "sbrkfail"}, + {sbrkarg, "sbrkarg"}, + {validatetest, "validatetest"}, + {stacktest, "stacktest"}, + {opentest, "opentest"}, + {writetest, "writetest"}, + {writebig, "writebig"}, + {createtest, "createtest"}, + {openiputtest, "openiput"}, + {exitiputtest, "exitiput"}, + {iputtest, "iput"}, + {mem, "mem"}, + {pipe1, "pipe1"}, + {preempt, "preempt"}, + {exitwait, "exitwait"}, + {rmdot, "rmdot"}, + {fourteen, "fourteen"}, + {bigfile, "bigfile"}, + {dirfile, "dirfile"}, + {iref, "iref"}, + {forktest, "forktest"}, + {bigdir, "bigdir"}, // slow + { 0, 0}, + }; + + if(continuous){ + printf("continuous usertests starting\n"); + while(1){ + int fail = 0; + int free0 = countfree(); + for (struct test *t = tests; t->s != 0; t++) { + if(!run(t->f, t->s)){ + fail = 1; + break; + } + } + if(fail){ + printf("SOME TESTS FAILED\n"); + if(continuous != 2) + exit(1); + } + int free1 = countfree(); + if(free1 < free0){ + printf("FAILED -- lost %d free pages\n", free0 - free1); + if(continuous != 2) + exit(1); + } + } + } + + printf("usertests starting\n"); + int free0 = countfree(); + int free1 = 0; + int fail = 0; + for (struct test *t = tests; t->s != 0; t++) { + if((justone == 0) || strcmp(t->s, justone) == 0) { + if(!run(t->f, t->s)) + fail = 1; + } + } + + if(fail){ + printf("SOME TESTS FAILED\n"); + exit(1); + } else if((free1 = countfree()) < free0){ + printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); exit(1); + } else { + printf("ALL TESTS PASSED\n"); + exit(0); } - printf("ALL TESTS PASSED\n"); - exit(0); } diff --git a/user/wc.c b/user/wc.c index d8f3b2ad010f2c0fc0227702b6c10dfe8c5ee2d2..6a851ca4ac5a4deb7c2f2cb2b5300affab3d0c01 100644 --- a/user/wc.c +++ b/user/wc.c @@ -1,6 +1,5 @@ #include "kernel/types.h" #include "kernel/stat.h" -#include "kernel/fcntl.h" #include "user/user.h" char buf[512]; @@ -44,7 +43,7 @@ main(int argc, char *argv[]) } for(i = 1; i < argc; i++){ - if((fd = open(argv[i], O_RDONLY)) < 0){ + if((fd = open(argv[i], 0)) < 0){ printf("wc: cannot open %s\n", argv[i]); exit(1); }