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);
     }