[
  {
    "path": ".dir-locals.el",
    "content": "((c-mode\n  (indent-tabs-mode . nil)\n  (c-file-style . \"bsd\")\n  (c-basic-offset . 2)))\n"
  },
  {
    "path": ".editorconfig",
    "content": "; https://editorconfig.org\n\nroot = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\n\n[*.{c,h}]\nindent_size = 2\n\n[*.S]\nindent_size = 8\n\n[*.ld]\nindent_size = 2\n\n[Makefile]\nindent_style = tab\nindent_size = 8\n"
  },
  {
    "path": ".gdbinit.tmpl-riscv",
    "content": "set confirm off\nset architecture riscv:rv64\ntarget remote 127.0.0.1:1234\nsymbol-file kernel/kernel\nset disassemble-next-line auto\nset riscv use-compressed-breakpoints yes\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n_*\n*.o\n*.d\n*.asm\n*.sym\n*.img\nvectors.S\nbootblock\nentryother\ninitcode\ninitcode.out\nkernelmemfs\nmkfs\nkernel/kernel\nuser/usys.S\n.gdbinit\nTAGS\n"
  },
  {
    "path": "LICENSE",
    "content": "The xv6 software is:\n\nCopyright (c) 2006-2024 Frans Kaashoek, Robert Morris, Russ Cox,\n                        Massachusetts Institute of Technology\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "K=kernel\nU=user\n\nOBJS = \\\n  $K/entry.o \\\n  $K/start.o \\\n  $K/console.o \\\n  $K/printf.o \\\n  $K/uart.o \\\n  $K/kalloc.o \\\n  $K/spinlock.o \\\n  $K/string.o \\\n  $K/main.o \\\n  $K/vm.o \\\n  $K/proc.o \\\n  $K/swtch.o \\\n  $K/trampoline.o \\\n  $K/trap.o \\\n  $K/syscall.o \\\n  $K/sysproc.o \\\n  $K/bio.o \\\n  $K/fs.o \\\n  $K/log.o \\\n  $K/sleeplock.o \\\n  $K/file.o \\\n  $K/pipe.o \\\n  $K/exec.o \\\n  $K/sysfile.o \\\n  $K/kernelvec.o \\\n  $K/plic.o \\\n  $K/virtio_disk.o\n\n# riscv64-unknown-elf- or riscv64-linux-gnu-\n# perhaps in /opt/riscv/bin\n#TOOLPREFIX = \n\n# Try to infer the correct TOOLPREFIX if not set\nifndef TOOLPREFIX\nTOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \\\n\tthen echo 'riscv64-unknown-elf-'; \\\n\telif riscv64-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \\\n\tthen echo 'riscv64-elf-'; \\\n\telif riscv64-none-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \\\n\tthen echo 'riscv64-none-elf-'; \\\n\telif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \\\n\tthen echo 'riscv64-linux-gnu-'; \\\n\telif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \\\n\tthen echo 'riscv64-unknown-linux-gnu-'; \\\n\telse echo \"***\" 1>&2; \\\n\techo \"*** Error: Couldn't find a riscv64 version of GCC/binutils.\" 1>&2; \\\n\techo \"*** To turn off this error, run 'gmake TOOLPREFIX= ...'.\" 1>&2; \\\n\techo \"***\" 1>&2; exit 1; fi)\nendif\n\nQEMU = qemu-system-riscv64\nMIN_QEMU_VERSION = 7.2\n\nCC = $(TOOLPREFIX)gcc\nAS = $(TOOLPREFIX)gas\nLD = $(TOOLPREFIX)ld\nOBJCOPY = $(TOOLPREFIX)objcopy\nOBJDUMP = $(TOOLPREFIX)objdump\n\nCFLAGS = -Wall -Werror -Wno-unknown-attributes -O -fno-omit-frame-pointer -ggdb -gdwarf-2\nCFLAGS += -march=rv64gc\nCFLAGS += -MD\nCFLAGS += -mcmodel=medany\nCFLAGS += -ffreestanding\nCFLAGS += -fno-common -nostdlib\nCFLAGS += -fno-builtin-strncpy -fno-builtin-strncmp -fno-builtin-strlen -fno-builtin-memset\nCFLAGS += -fno-builtin-memmove -fno-builtin-memcmp -fno-builtin-log -fno-builtin-bzero\nCFLAGS += -fno-builtin-strchr -fno-builtin-exit -fno-builtin-malloc -fno-builtin-putc\nCFLAGS += -fno-builtin-free\nCFLAGS += -fno-builtin-memcpy -Wno-main\nCFLAGS += -fno-builtin-printf -fno-builtin-fprintf -fno-builtin-vprintf\nCFLAGS += -I.\nCFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)\n\n# Disable PIE when possible (for Ubuntu 16.10 toolchain)\nifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)\nCFLAGS += -fno-pie -no-pie\nendif\nifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),)\nCFLAGS += -fno-pie -nopie\nendif\n\nLDFLAGS = -z max-page-size=4096\n\n$K/kernel: $(OBJS) $K/kernel.ld\n\t$(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) \n\t$(OBJDUMP) -S $K/kernel > $K/kernel.asm\n\t$(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym\n\n$K/%.o: $K/%.S\n\t$(CC) -march=rv64gc -g -c -o $@ $<\n\ntags: $(OBJS)\n\tetags kernel/*.S kernel/*.c\n\nULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o\n\n_%: %.o $(ULIB) $U/user.ld\n\t$(LD) $(LDFLAGS) -T $U/user.ld -o $@ $< $(ULIB)\n\t$(OBJDUMP) -S $@ > $*.asm\n\t$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym\n\n$U/usys.S : $U/usys.pl\n\tperl $U/usys.pl > $U/usys.S\n\n$U/usys.o : $U/usys.S\n\t$(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S\n\n$U/_forktest: $U/forktest.o $(ULIB)\n\t# forktest has less library code linked in - needs to be small\n\t# in order to be able to max out the proc table.\n\t$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o\n\t$(OBJDUMP) -S $U/_forktest > $U/forktest.asm\n\nmkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h\n\tgcc -Wno-unknown-attributes -I. -o mkfs/mkfs mkfs/mkfs.c\n\n# Prevent deletion of intermediate files, e.g. cat.o, after first build, so\n# that disk image changes after first build are persistent until clean.  More\n# details:\n# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html\n.PRECIOUS: %.o\n\nUPROGS=\\\n\t$U/_cat\\\n\t$U/_echo\\\n\t$U/_forktest\\\n\t$U/_grep\\\n\t$U/_init\\\n\t$U/_kill\\\n\t$U/_ln\\\n\t$U/_ls\\\n\t$U/_mkdir\\\n\t$U/_rm\\\n\t$U/_sh\\\n\t$U/_stressfs\\\n\t$U/_usertests\\\n\t$U/_grind\\\n\t$U/_wc\\\n\t$U/_zombie\\\n\t$U/_logstress\\\n\t$U/_forphan\\\n\t$U/_dorphan\\\n\nfs.img: mkfs/mkfs README $(UPROGS)\n\tmkfs/mkfs fs.img README $(UPROGS)\n\n-include kernel/*.d user/*.d\n\nclean: \n\trm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \\\n\t*/*.o */*.d */*.asm */*.sym \\\n\t$K/kernel fs.img \\\n\tmkfs/mkfs .gdbinit \\\n        $U/usys.S \\\n\t$(UPROGS)\n\n# try to generate a unique GDB port\nGDBPORT = $(shell expr `id -u` % 5000 + 25000)\n# QEMU's gdb stub command line changed in 0.11\nQEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \\\n\tthen echo \"-gdb tcp::$(GDBPORT)\"; \\\n\telse echo \"-s -p $(GDBPORT)\"; fi)\nifndef CPUS\nCPUS := 3\nendif\n\nQEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic\nQEMUOPTS += -global virtio-mmio.force-legacy=false\nQEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0\nQEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0\n\nqemu: check-qemu-version $K/kernel fs.img\n\t$(QEMU) $(QEMUOPTS)\n\n.gdbinit: .gdbinit.tmpl-riscv\n\tsed \"s/:1234/:$(GDBPORT)/\" < $^ > $@\n\nqemu-gdb: $K/kernel .gdbinit fs.img\n\t@echo \"*** Now run 'gdb' in another window.\" 1>&2\n\t$(QEMU) $(QEMUOPTS) -S $(QEMUGDB)\n\nprint-gdbport:\n\t@echo $(GDBPORT)\n\nQEMU_VERSION := $(shell $(QEMU) --version | head -n 1 | sed -E 's/^QEMU emulator version ([0-9]+\\.[0-9]+)\\..*/\\1/')\ncheck-qemu-version:\n\t@if [ \"$(shell echo \"$(QEMU_VERSION) >= $(MIN_QEMU_VERSION)\" | bc)\" -eq 0 ]; then \\\n\t\techo \"ERROR: Need qemu version >= $(MIN_QEMU_VERSION)\"; \\\n\t\texit 1; \\\n\tfi\n"
  },
  {
    "path": "README",
    "content": "xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix\nVersion 6 (v6).  xv6 loosely follows the structure and style of v6,\nbut is implemented for a modern RISC-V multiprocessor using ANSI C.\n\nACKNOWLEDGMENTS\n\nxv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer\nto Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,\n2000)).  See also https://pdos.csail.mit.edu/6.1810/, which provides\npointers to on-line resources for v6.\n\nThe following people have made contributions: Russ Cox (context switching,\nlocking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin\nClements.\n\nWe are also grateful for the bug reports and patches contributed by\nAbhinavpatel00, Takahiro Aoyagi, Marcelo Arroyo, Hirbod Behnam, Silas\nBoyd-Wickizer, Anton Burtsev, carlclone, Ian Chen, clivezeng, Dan\nCross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,Wenyang Duan,\nechtwerner, eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi,\nNathaniel Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam\nHanda, Matt Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš\nJókay, John Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95,\nWolfgang Keller, Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim\nKolontsov, Austin Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu,\nYandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi Merimovich,\nmes900903, Mark Morrissey, mtasm, Joel Nider, Hayato Ohhashi,\nOptimisticSide, papparapa, phosphagos, Harry Porter, Greg Price, Zheng\nqhuo, Quancheng, RayAndrew, Jude Rich, segfault, Ayan Shafqat, Eldar\nSehayek, Yongming Shen, Fumiya Shigemitsu, snoire, Taojie, Cam Tenny,\ntyfkda, Warren Toomey, Stephen Tu, Alissa Tung, Rafael Ubal, unicornx,\nAmane Uehara, Pablo Ventura, Luc Videau, Xi Wang, WaheedHafez, Keiichi\nWatanabe, Lucas Wolf, Nicolas Wolovick, wxdao, Grant Wu, x653, Andy\nZhang, Jindong Zhang, Icenowy Zheng, ZhUyU1997, and Zou Chang Wei.\n\nERROR REPORTS\n\nPlease send errors and suggestions to Frans Kaashoek and Robert Morris\n(kaashoek,rtm@mit.edu).  The main purpose of xv6 is as a teaching\noperating system for MIT's 6.1810, so we are more interested in\nsimplifications and clarifications than new features.\n\nBUILDING AND RUNNING XV6\n\nYou will need a RISC-V \"newlib\" tool chain from\nhttps://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for\nriscv64-softmmu.  Once they are installed, and in your shell\nsearch path, you can run \"make qemu\".\n"
  },
  {
    "path": "kernel/bio.c",
    "content": "// Buffer cache.\n//\n// The buffer cache is a linked list of buf structures holding\n// cached copies of disk block contents.  Caching disk blocks\n// in memory reduces the number of disk reads and also provides\n// a synchronization point for disk blocks used by multiple processes.\n//\n// Interface:\n// * To get a buffer for a particular disk block, call bread.\n// * After changing buffer data, call bwrite to write it to disk.\n// * When done with the buffer, call brelse.\n// * Do not use the buffer after calling brelse.\n// * Only one process at a time can use a buffer,\n//     so do not keep them longer than necessary.\n\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"fs.h\"\n#include \"buf.h\"\n\nstruct {\n  struct spinlock lock;\n  struct buf buf[NBUF];\n\n  // Linked list of all buffers, through prev/next.\n  // Sorted by how recently the buffer was used.\n  // head.next is most recent, head.prev is least.\n  struct buf head;\n} bcache;\n\nvoid\nbinit(void)\n{\n  struct buf *b;\n\n  initlock(&bcache.lock, \"bcache\");\n\n  // Create linked list of buffers\n  bcache.head.prev = &bcache.head;\n  bcache.head.next = &bcache.head;\n  for(b = bcache.buf; b < bcache.buf+NBUF; b++){\n    b->next = bcache.head.next;\n    b->prev = &bcache.head;\n    initsleeplock(&b->lock, \"buffer\");\n    bcache.head.next->prev = b;\n    bcache.head.next = b;\n  }\n}\n\n// Look through buffer cache for block on device dev.\n// If not found, allocate a buffer.\n// In either case, return locked buffer.\nstatic struct buf*\nbget(uint dev, uint blockno)\n{\n  struct buf *b;\n\n  acquire(&bcache.lock);\n\n  // Is the block already cached?\n  for(b = bcache.head.next; b != &bcache.head; b = b->next){\n    if(b->dev == dev && b->blockno == blockno){\n      b->refcnt++;\n      release(&bcache.lock);\n      acquiresleep(&b->lock);\n      return b;\n    }\n  }\n\n  // Not cached.\n  // Recycle the least recently used (LRU) unused buffer.\n  for(b = bcache.head.prev; b != &bcache.head; b = b->prev){\n    if(b->refcnt == 0) {\n      b->dev = dev;\n      b->blockno = blockno;\n      b->valid = 0;\n      b->refcnt = 1;\n      release(&bcache.lock);\n      acquiresleep(&b->lock);\n      return b;\n    }\n  }\n  panic(\"bget: no buffers\");\n}\n\n// Return a locked buf with the contents of the indicated block.\nstruct buf*\nbread(uint dev, uint blockno)\n{\n  struct buf *b;\n\n  b = bget(dev, blockno);\n  if(!b->valid) {\n    virtio_disk_rw(b, 0);\n    b->valid = 1;\n  }\n  return b;\n}\n\n// Write b's contents to disk.  Must be locked.\nvoid\nbwrite(struct buf *b)\n{\n  if(!holdingsleep(&b->lock))\n    panic(\"bwrite\");\n  virtio_disk_rw(b, 1);\n}\n\n// Release a locked buffer.\n// Move to the head of the most-recently-used list.\nvoid\nbrelse(struct buf *b)\n{\n  if(!holdingsleep(&b->lock))\n    panic(\"brelse\");\n\n  releasesleep(&b->lock);\n\n  acquire(&bcache.lock);\n  b->refcnt--;\n  if (b->refcnt == 0) {\n    // no one is waiting for it.\n    b->next->prev = b->prev;\n    b->prev->next = b->next;\n    b->next = bcache.head.next;\n    b->prev = &bcache.head;\n    bcache.head.next->prev = b;\n    bcache.head.next = b;\n  }\n  \n  release(&bcache.lock);\n}\n\nvoid\nbpin(struct buf *b) {\n  acquire(&bcache.lock);\n  b->refcnt++;\n  release(&bcache.lock);\n}\n\nvoid\nbunpin(struct buf *b) {\n  acquire(&bcache.lock);\n  b->refcnt--;\n  release(&bcache.lock);\n}\n\n\n"
  },
  {
    "path": "kernel/buf.h",
    "content": "struct buf {\n  int valid;   // has data been read from disk?\n  int disk;    // does disk \"own\" buf?\n  uint dev;\n  uint blockno;\n  struct sleeplock lock;\n  uint refcnt;\n  struct buf *prev; // LRU cache list\n  struct buf *next;\n  uchar data[BSIZE];\n};\n\n"
  },
  {
    "path": "kernel/console.c",
    "content": "//\n// Console input and output, to the uart.\n// Reads are line at a time.\n// Implements special input characters:\n//   newline -- end of line\n//   control-h -- backspace\n//   control-u -- kill line\n//   control-d -- end of file\n//   control-p -- print process list\n//\n\n#include <stdarg.h>\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"fs.h\"\n#include \"file.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"proc.h\"\n\n#define BACKSPACE 0x100  // erase the last output character\n#define C(x)  ((x)-'@')  // Control-x\n\n//\n// send one character to the uart, but don't use\n// interrupts or sleep(). safe to be called from\n// interrupts, e.g. by printf and to echo input\n// characters.\n//\nvoid\nconsputc(int c)\n{\n  if(c == BACKSPACE){\n    // if the user typed backspace, overwrite with a space.\n    uartputc_sync('\\b'); uartputc_sync(' '); uartputc_sync('\\b');\n  } else {\n    uartputc_sync(c);\n  }\n}\n\nstruct {\n  struct spinlock lock;\n  \n  // input circular buffer\n#define INPUT_BUF_SIZE 128\n  char buf[INPUT_BUF_SIZE];\n  uint r;  // Read index\n  uint w;  // Write index\n  uint e;  // Edit index\n} cons;\n\n//\n// user write() system calls to the console go here.\n// uses sleep() and UART interrupts.\n//\nint\nconsolewrite(int user_src, uint64 src, int n)\n{\n  char buf[32]; // move batches from user space to uart.\n  int i = 0;\n\n  while(i < n){\n    int nn = sizeof(buf);\n    if(nn > n - i)\n      nn = n - i;\n    if(either_copyin(buf, user_src, src+i, nn) == -1)\n      break;\n    uartwrite(buf, nn);\n    i += nn;\n  }\n\n  return i;\n}\n\n//\n// user read()s from the console go here.\n// copy (up to) a whole input line to dst.\n// user_dst indicates whether dst is a user\n// or kernel address.\n//\nint\nconsoleread(int user_dst, uint64 dst, int n)\n{\n  uint target;\n  int c;\n  char cbuf;\n\n  target = n;\n  acquire(&cons.lock);\n  while(n > 0){\n    // wait until interrupt handler has put some\n    // input into cons.buffer.\n    while(cons.r == cons.w){\n      if(killed(myproc())){\n        release(&cons.lock);\n        return -1;\n      }\n      sleep(&cons.r, &cons.lock);\n    }\n\n    c = cons.buf[cons.r++ % INPUT_BUF_SIZE];\n\n    if(c == C('D')){  // end-of-file\n      if(n < target){\n        // Save ^D for next time, to make sure\n        // caller gets a 0-byte result.\n        cons.r--;\n      }\n      break;\n    }\n\n    // copy the input byte to the user-space buffer.\n    cbuf = c;\n    if(either_copyout(user_dst, dst, &cbuf, 1) == -1)\n      break;\n\n    dst++;\n    --n;\n\n    if(c == '\\n'){\n      // a whole line has arrived, return to\n      // the user-level read().\n      break;\n    }\n  }\n  release(&cons.lock);\n\n  return target - n;\n}\n\n//\n// the console input interrupt handler.\n// uartintr() calls this for each input character.\n// do erase/kill processing, append to cons.buf,\n// wake up consoleread() if a whole line has arrived.\n//\nvoid\nconsoleintr(int c)\n{\n  acquire(&cons.lock);\n\n  switch(c){\n  case C('P'):  // Print process list.\n    procdump();\n    break;\n  case C('U'):  // Kill line.\n    while(cons.e != cons.w &&\n          cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\\n'){\n      cons.e--;\n      consputc(BACKSPACE);\n    }\n    break;\n  case C('H'): // Backspace\n  case '\\x7f': // Delete key\n    if(cons.e != cons.w){\n      cons.e--;\n      consputc(BACKSPACE);\n    }\n    break;\n  default:\n    if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){\n      c = (c == '\\r') ? '\\n' : c;\n\n      // echo back to the user.\n      consputc(c);\n\n      // store for consumption by consoleread().\n      cons.buf[cons.e++ % INPUT_BUF_SIZE] = c;\n\n      if(c == '\\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){\n        // wake up consoleread() if a whole line (or end-of-file)\n        // has arrived.\n        cons.w = cons.e;\n        wakeup(&cons.r);\n      }\n    }\n    break;\n  }\n  \n  release(&cons.lock);\n}\n\nvoid\nconsoleinit(void)\n{\n  initlock(&cons.lock, \"cons\");\n\n  uartinit();\n\n  // connect read and write system calls\n  // to consoleread and consolewrite.\n  devsw[CONSOLE].read = consoleread;\n  devsw[CONSOLE].write = consolewrite;\n}\n"
  },
  {
    "path": "kernel/defs.h",
    "content": "struct buf;\nstruct context;\nstruct file;\nstruct inode;\nstruct pipe;\nstruct proc;\nstruct spinlock;\nstruct sleeplock;\nstruct stat;\nstruct superblock;\n\n// bio.c\nvoid            binit(void);\nstruct buf*     bread(uint, uint);\nvoid            brelse(struct buf*);\nvoid            bwrite(struct buf*);\nvoid            bpin(struct buf*);\nvoid            bunpin(struct buf*);\n\n// console.c\nvoid            consoleinit(void);\nvoid            consoleintr(int);\nvoid            consputc(int);\n\n// exec.c\nint             kexec(char*, char**);\n\n// file.c\nstruct file*    filealloc(void);\nvoid            fileclose(struct file*);\nstruct file*    filedup(struct file*);\nvoid            fileinit(void);\nint             fileread(struct file*, uint64, int n);\nint             filestat(struct file*, uint64 addr);\nint             filewrite(struct file*, uint64, int n);\n\n// fs.c\nvoid            fsinit(int);\nint             dirlink(struct inode*, char*, uint);\nstruct inode*   dirlookup(struct inode*, char*, uint*);\nstruct inode*   ialloc(uint, short);\nstruct inode*   idup(struct inode*);\nvoid            iinit();\nvoid            ilock(struct inode*);\nvoid            iput(struct inode*);\nvoid            iunlock(struct inode*);\nvoid            iunlockput(struct inode*);\nvoid            iupdate(struct inode*);\nint             namecmp(const char*, const char*);\nstruct inode*   namei(char*);\nstruct inode*   nameiparent(char*, char*);\nint             readi(struct inode*, int, uint64, uint, uint);\nvoid            stati(struct inode*, struct stat*);\nint             writei(struct inode*, int, uint64, uint, uint);\nvoid            itrunc(struct inode*);\nvoid            ireclaim(int);\n\n// kalloc.c\nvoid*           kalloc(void);\nvoid            kfree(void *);\nvoid            kinit(void);\n\n// log.c\nvoid            initlog(int, struct superblock*);\nvoid            log_write(struct buf*);\nvoid            begin_op(void);\nvoid            end_op(void);\n\n// pipe.c\nint             pipealloc(struct file**, struct file**);\nvoid            pipeclose(struct pipe*, int);\nint             piperead(struct pipe*, uint64, int);\nint             pipewrite(struct pipe*, uint64, int);\n\n// printf.c\nint             printf(char*, ...) __attribute__ ((format (printf, 1, 2)));\nvoid            panic(char*) __attribute__((noreturn));\nvoid            printfinit(void);\n\n// proc.c\nint             cpuid(void);\nvoid            kexit(int);\nint             kfork(void);\nint             growproc(int);\nvoid            proc_mapstacks(pagetable_t);\npagetable_t     proc_pagetable(struct proc *);\nvoid            proc_freepagetable(pagetable_t, uint64);\nint             kkill(int);\nint             killed(struct proc*);\nvoid            setkilled(struct proc*);\nstruct cpu*     mycpu(void);\nstruct proc*    myproc();\nvoid            procinit(void);\nvoid            scheduler(void) __attribute__((noreturn));\nvoid            sched(void);\nvoid            sleep(void*, struct spinlock*);\nvoid            userinit(void);\nint             kwait(uint64);\nvoid            wakeup(void*);\nvoid            yield(void);\nint             either_copyout(int user_dst, uint64 dst, void *src, uint64 len);\nint             either_copyin(void *dst, int user_src, uint64 src, uint64 len);\nvoid            procdump(void);\n\n// swtch.S\nvoid            swtch(struct context*, struct context*);\n\n// spinlock.c\nvoid            acquire(struct spinlock*);\nint             holding(struct spinlock*);\nvoid            initlock(struct spinlock*, char*);\nvoid            release(struct spinlock*);\nvoid            push_off(void);\nvoid            pop_off(void);\n\n// sleeplock.c\nvoid            acquiresleep(struct sleeplock*);\nvoid            releasesleep(struct sleeplock*);\nint             holdingsleep(struct sleeplock*);\nvoid            initsleeplock(struct sleeplock*, char*);\n\n// string.c\nint             memcmp(const void*, const void*, uint);\nvoid*           memmove(void*, const void*, uint);\nvoid*           memset(void*, int, uint);\nchar*           safestrcpy(char*, const char*, int);\nint             strlen(const char*);\nint             strncmp(const char*, const char*, uint);\nchar*           strncpy(char*, const char*, int);\n\n// syscall.c\nvoid            argint(int, int*);\nint             argstr(int, char*, int);\nvoid            argaddr(int, uint64 *);\nint             fetchstr(uint64, char*, int);\nint             fetchaddr(uint64, uint64*);\nvoid            syscall();\n\n// trap.c\nextern uint     ticks;\nvoid            trapinit(void);\nvoid            trapinithart(void);\nextern struct spinlock tickslock;\nvoid            prepare_return(void);\n\n// uart.c\nvoid            uartinit(void);\nvoid            uartintr(void);\nvoid            uartwrite(char [], int);\nvoid            uartputc_sync(int);\nint             uartgetc(void);\n\n// vm.c\nvoid            kvminit(void);\nvoid            kvminithart(void);\nvoid            kvmmap(pagetable_t, uint64, uint64, uint64, int);\nint             mappages(pagetable_t, uint64, uint64, uint64, int);\npagetable_t     uvmcreate(void);\nuint64          uvmalloc(pagetable_t, uint64, uint64, int);\nuint64          uvmdealloc(pagetable_t, uint64, uint64);\nint             uvmcopy(pagetable_t, pagetable_t, uint64);\nvoid            uvmfree(pagetable_t, uint64);\nvoid            uvmunmap(pagetable_t, uint64, uint64, int);\nvoid            uvmclear(pagetable_t, uint64);\npte_t *         walk(pagetable_t, uint64, int);\nuint64          walkaddr(pagetable_t, uint64);\nint             copyout(pagetable_t, uint64, char *, uint64);\nint             copyin(pagetable_t, char *, uint64, uint64);\nint             copyinstr(pagetable_t, char *, uint64, uint64);\nint             ismapped(pagetable_t, uint64);\nuint64          vmfault(pagetable_t, uint64, int);\n\n// plic.c\nvoid            plicinit(void);\nvoid            plicinithart(void);\nint             plic_claim(void);\nvoid            plic_complete(int);\n\n// virtio_disk.c\nvoid            virtio_disk_init(void);\nvoid            virtio_disk_rw(struct buf *, int);\nvoid            virtio_disk_intr(void);\n\n// number of elements in fixed-size array\n#define NELEM(x) (sizeof(x)/sizeof((x)[0]))\n"
  },
  {
    "path": "kernel/elf.h",
    "content": "// Format of an ELF executable file\n\n#define ELF_MAGIC 0x464C457FU  // \"\\x7FELF\" in little endian\n\n// File header\nstruct elfhdr {\n  uint magic;  // must equal ELF_MAGIC\n  uchar elf[12];\n  ushort type;\n  ushort machine;\n  uint version;\n  uint64 entry;\n  uint64 phoff;\n  uint64 shoff;\n  uint flags;\n  ushort ehsize;\n  ushort phentsize;\n  ushort phnum;\n  ushort shentsize;\n  ushort shnum;\n  ushort shstrndx;\n};\n\n// Program section header\nstruct proghdr {\n  uint32 type;\n  uint32 flags;\n  uint64 off;\n  uint64 vaddr;\n  uint64 paddr;\n  uint64 filesz;\n  uint64 memsz;\n  uint64 align;\n};\n\n// Values for Proghdr type\n#define ELF_PROG_LOAD           1\n\n// Flag bits for Proghdr flags\n#define ELF_PROG_FLAG_EXEC      1\n#define ELF_PROG_FLAG_WRITE     2\n#define ELF_PROG_FLAG_READ      4\n"
  },
  {
    "path": "kernel/entry.S",
    "content": "        # qemu -kernel loads the kernel at 0x80000000\n        # and causes each hart (i.e. CPU) to jump there.\n        # kernel.ld causes the following code to\n        # be placed at 0x80000000.\n.section .text\n.global _entry\n_entry:\n        # set up a stack for C.\n        # stack0 is declared in start.c,\n        # with a 4096-byte stack per CPU.\n        # sp = stack0 + ((hartid + 1) * 4096)\n        la sp, stack0\n        li a0, 1024*4\n        csrr a1, mhartid\n        addi a1, a1, 1\n        mul a0, a0, a1\n        add sp, sp, a0\n        # jump to start() in start.c\n        call start\nspin:\n        j spin\n"
  },
  {
    "path": "kernel/exec.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"defs.h\"\n#include \"elf.h\"\n\nstatic int loadseg(pde_t *, uint64, struct inode *, uint, uint);\n\n// map ELF permissions to PTE permission bits.\nint flags2perm(int flags)\n{\n    int perm = 0;\n    if(flags & 0x1)\n      perm = PTE_X;\n    if(flags & 0x2)\n      perm |= PTE_W;\n    return perm;\n}\n\n//\n// the implementation of the exec() system call\n//\nint\nkexec(char *path, char **argv)\n{\n  char *s, *last;\n  int i, off;\n  uint64 argc, sz = 0, sp, ustack[MAXARG], stackbase;\n  struct elfhdr elf;\n  struct inode *ip;\n  struct proghdr ph;\n  pagetable_t pagetable = 0, oldpagetable;\n  struct proc *p = myproc();\n\n  begin_op();\n\n  // Open the executable file.\n  if((ip = namei(path)) == 0){\n    end_op();\n    return -1;\n  }\n  ilock(ip);\n\n  // Read the ELF header.\n  if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))\n    goto bad;\n\n  // Is this really an ELF file?\n  if(elf.magic != ELF_MAGIC)\n    goto bad;\n\n  if((pagetable = proc_pagetable(p)) == 0)\n    goto bad;\n\n  // Load program into memory.\n  for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){\n    if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))\n      goto bad;\n    if(ph.type != ELF_PROG_LOAD)\n      continue;\n    if(ph.memsz < ph.filesz)\n      goto bad;\n    if(ph.vaddr + ph.memsz < ph.vaddr)\n      goto bad;\n    if(ph.vaddr % PGSIZE != 0)\n      goto bad;\n    uint64 sz1;\n    if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, flags2perm(ph.flags))) == 0)\n      goto bad;\n    sz = sz1;\n    if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)\n      goto bad;\n  }\n  iunlockput(ip);\n  end_op();\n  ip = 0;\n\n  p = myproc();\n  uint64 oldsz = p->sz;\n\n  // Allocate some pages at the next page boundary.\n  // Make the first inaccessible as a stack guard.\n  // Use the rest as the user stack.\n  sz = PGROUNDUP(sz);\n  uint64 sz1;\n  if((sz1 = uvmalloc(pagetable, sz, sz + (USERSTACK+1)*PGSIZE, PTE_W)) == 0)\n    goto bad;\n  sz = sz1;\n  uvmclear(pagetable, sz-(USERSTACK+1)*PGSIZE);\n  sp = sz;\n  stackbase = sp - USERSTACK*PGSIZE;\n\n  // Copy argument strings into new stack, remember their\n  // addresses in ustack[].\n  for(argc = 0; argv[argc]; argc++) {\n    if(argc >= MAXARG)\n      goto bad;\n    sp -= strlen(argv[argc]) + 1;\n    sp -= sp % 16; // riscv sp must be 16-byte aligned\n    if(sp < stackbase)\n      goto bad;\n    if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)\n      goto bad;\n    ustack[argc] = sp;\n  }\n  ustack[argc] = 0;\n\n  // push a copy of ustack[], the array of argv[] pointers.\n  sp -= (argc+1) * sizeof(uint64);\n  sp -= sp % 16;\n  if(sp < stackbase)\n    goto bad;\n  if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)\n    goto bad;\n\n  // a0 and a1 contain arguments to user main(argc, argv)\n  // argc is returned via the system call return\n  // value, which goes in a0.\n  p->trapframe->a1 = sp;\n\n  // Save program name for debugging.\n  for(last=s=path; *s; s++)\n    if(*s == '/')\n      last = s+1;\n  safestrcpy(p->name, last, sizeof(p->name));\n    \n  // Commit to the user image.\n  oldpagetable = p->pagetable;\n  p->pagetable = pagetable;\n  p->sz = sz;\n  p->trapframe->epc = elf.entry;  // initial program counter = ulib.c:start()\n  p->trapframe->sp = sp; // initial stack pointer\n  proc_freepagetable(oldpagetable, oldsz);\n\n  return argc; // this ends up in a0, the first argument to main(argc, argv)\n\n bad:\n  if(pagetable)\n    proc_freepagetable(pagetable, sz);\n  if(ip){\n    iunlockput(ip);\n    end_op();\n  }\n  return -1;\n}\n\n// Load an ELF program segment into pagetable at virtual address va.\n// va must be page-aligned\n// and the pages from va to va+sz must already be mapped.\n// Returns 0 on success, -1 on failure.\nstatic int\nloadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz)\n{\n  uint i, n;\n  uint64 pa;\n\n  for(i = 0; i < sz; i += PGSIZE){\n    pa = walkaddr(pagetable, va + i);\n    if(pa == 0)\n      panic(\"loadseg: address should exist\");\n    if(sz - i < PGSIZE)\n      n = sz - i;\n    else\n      n = PGSIZE;\n    if(readi(ip, 0, (uint64)pa, offset+i, n) != n)\n      return -1;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "kernel/fcntl.h",
    "content": "#define O_RDONLY  0x000\n#define O_WRONLY  0x001\n#define O_RDWR    0x002\n#define O_CREATE  0x200\n#define O_TRUNC   0x400\n"
  },
  {
    "path": "kernel/file.c",
    "content": "//\n// Support functions for system calls that involve file descriptors.\n//\n\n#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"fs.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"file.h\"\n#include \"stat.h\"\n#include \"proc.h\"\n\nstruct devsw devsw[NDEV];\nstruct {\n  struct spinlock lock;\n  struct file file[NFILE];\n} ftable;\n\nvoid\nfileinit(void)\n{\n  initlock(&ftable.lock, \"ftable\");\n}\n\n// Allocate a file structure.\nstruct file*\nfilealloc(void)\n{\n  struct file *f;\n\n  acquire(&ftable.lock);\n  for(f = ftable.file; f < ftable.file + NFILE; f++){\n    if(f->ref == 0){\n      f->ref = 1;\n      release(&ftable.lock);\n      return f;\n    }\n  }\n  release(&ftable.lock);\n  return 0;\n}\n\n// Increment ref count for file f.\nstruct file*\nfiledup(struct file *f)\n{\n  acquire(&ftable.lock);\n  if(f->ref < 1)\n    panic(\"filedup\");\n  f->ref++;\n  release(&ftable.lock);\n  return f;\n}\n\n// Close file f.  (Decrement ref count, close when reaches 0.)\nvoid\nfileclose(struct file *f)\n{\n  struct file ff;\n\n  acquire(&ftable.lock);\n  if(f->ref < 1)\n    panic(\"fileclose\");\n  if(--f->ref > 0){\n    release(&ftable.lock);\n    return;\n  }\n  ff = *f;\n  f->ref = 0;\n  f->type = FD_NONE;\n  release(&ftable.lock);\n\n  if(ff.type == FD_PIPE){\n    pipeclose(ff.pipe, ff.writable);\n  } else if(ff.type == FD_INODE || ff.type == FD_DEVICE){\n    begin_op();\n    iput(ff.ip);\n    end_op();\n  }\n}\n\n// Get metadata about file f.\n// addr is a user virtual address, pointing to a struct stat.\nint\nfilestat(struct file *f, uint64 addr)\n{\n  struct proc *p = myproc();\n  struct stat st;\n  \n  if(f->type == FD_INODE || f->type == FD_DEVICE){\n    ilock(f->ip);\n    stati(f->ip, &st);\n    iunlock(f->ip);\n    if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)\n      return -1;\n    return 0;\n  }\n  return -1;\n}\n\n// Read from file f.\n// addr is a user virtual address.\nint\nfileread(struct file *f, uint64 addr, int n)\n{\n  int r = 0;\n\n  if(f->readable == 0)\n    return -1;\n\n  if(f->type == FD_PIPE){\n    r = piperead(f->pipe, addr, n);\n  } else if(f->type == FD_DEVICE){\n    if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read)\n      return -1;\n    r = devsw[f->major].read(1, addr, n);\n  } else if(f->type == FD_INODE){\n    ilock(f->ip);\n    if((r = readi(f->ip, 1, addr, f->off, n)) > 0)\n      f->off += r;\n    iunlock(f->ip);\n  } else {\n    panic(\"fileread\");\n  }\n\n  return r;\n}\n\n// Write to file f.\n// addr is a user virtual address.\nint\nfilewrite(struct file *f, uint64 addr, int n)\n{\n  int r, ret = 0;\n\n  if(f->writable == 0)\n    return -1;\n\n  if(f->type == FD_PIPE){\n    ret = pipewrite(f->pipe, addr, n);\n  } else if(f->type == FD_DEVICE){\n    if(f->major < 0 || f->major >= NDEV || !devsw[f->major].write)\n      return -1;\n    ret = devsw[f->major].write(1, addr, n);\n  } else if(f->type == FD_INODE){\n    // write a few blocks at a time to avoid exceeding\n    // the maximum log transaction size, including\n    // i-node, indirect block, allocation blocks,\n    // and 2 blocks of slop for non-aligned writes.\n    int max = ((MAXOPBLOCKS-1-1-2) / 2) * BSIZE;\n    int i = 0;\n    while(i < n){\n      int n1 = n - i;\n      if(n1 > max)\n        n1 = max;\n\n      begin_op();\n      ilock(f->ip);\n      if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0)\n        f->off += r;\n      iunlock(f->ip);\n      end_op();\n\n      if(r != n1){\n        // error from writei\n        break;\n      }\n      i += r;\n    }\n    ret = (i == n ? n : -1);\n  } else {\n    panic(\"filewrite\");\n  }\n\n  return ret;\n}\n\n"
  },
  {
    "path": "kernel/file.h",
    "content": "struct file {\n  enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type;\n  int ref; // reference count\n  char readable;\n  char writable;\n  struct pipe *pipe; // FD_PIPE\n  struct inode *ip;  // FD_INODE and FD_DEVICE\n  uint off;          // FD_INODE\n  short major;       // FD_DEVICE\n};\n\n#define major(dev)  ((dev) >> 16 & 0xFFFF)\n#define minor(dev)  ((dev) & 0xFFFF)\n#define\tmkdev(m,n)  ((uint)((m)<<16| (n)))\n\n// in-memory copy of an inode\nstruct inode {\n  uint dev;           // Device number\n  uint inum;          // Inode number\n  int ref;            // Reference count\n  struct sleeplock lock; // protects everything below here\n  int valid;          // inode has been read from disk?\n\n  short type;         // copy of disk inode\n  short major;\n  short minor;\n  short nlink;\n  uint size;\n  uint addrs[NDIRECT+1];\n};\n\n// map major device number to device functions.\nstruct devsw {\n  int (*read)(int, uint64, int);\n  int (*write)(int, uint64, int);\n};\n\nextern struct devsw devsw[];\n\n#define CONSOLE 1\n"
  },
  {
    "path": "kernel/fs.c",
    "content": "// File system implementation.  Five layers:\n//   + Blocks: allocator for raw disk blocks.\n//   + Log: crash recovery for multi-step updates.\n//   + Files: inode allocator, reading, writing, metadata.\n//   + Directories: inode with special contents (list of other inodes!)\n//   + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.\n//\n// This file contains the low-level file system manipulation\n// routines.  The (higher-level) system call implementations\n// are in sysfile.c.\n\n#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"stat.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"sleeplock.h\"\n#include \"fs.h\"\n#include \"buf.h\"\n#include \"file.h\"\n\n#define min(a, b) ((a) < (b) ? (a) : (b))\n// there should be one superblock per disk device, but we run with\n// only one device\nstruct superblock sb; \n\n// Read the super block.\nstatic void\nreadsb(int dev, struct superblock *sb)\n{\n  struct buf *bp;\n\n  bp = bread(dev, 1);\n  memmove(sb, bp->data, sizeof(*sb));\n  brelse(bp);\n}\n\n// Init fs\nvoid\nfsinit(int dev) {\n  readsb(dev, &sb);\n  if(sb.magic != FSMAGIC)\n    panic(\"invalid file system\");\n  initlog(dev, &sb);\n  ireclaim(dev);\n}\n\n// Zero a block.\nstatic void\nbzero(int dev, int bno)\n{\n  struct buf *bp;\n\n  bp = bread(dev, bno);\n  memset(bp->data, 0, BSIZE);\n  log_write(bp);\n  brelse(bp);\n}\n\n// Blocks.\n\n// Allocate a zeroed disk block.\n// returns 0 if out of disk space.\nstatic uint\nballoc(uint dev)\n{\n  int b, bi, m;\n  struct buf *bp;\n\n  bp = 0;\n  for(b = 0; b < sb.size; b += BPB){\n    bp = bread(dev, BBLOCK(b, sb));\n    for(bi = 0; bi < BPB && b + bi < sb.size; bi++){\n      m = 1 << (bi % 8);\n      if((bp->data[bi/8] & m) == 0){  // Is block free?\n        bp->data[bi/8] |= m;  // Mark block in use.\n        log_write(bp);\n        brelse(bp);\n        bzero(dev, b + bi);\n        return b + bi;\n      }\n    }\n    brelse(bp);\n  }\n  printf(\"balloc: out of blocks\\n\");\n  return 0;\n}\n\n// Free a disk block.\nstatic void\nbfree(int dev, uint b)\n{\n  struct buf *bp;\n  int bi, m;\n\n  bp = bread(dev, BBLOCK(b, sb));\n  bi = b % BPB;\n  m = 1 << (bi % 8);\n  if((bp->data[bi/8] & m) == 0)\n    panic(\"freeing free block\");\n  bp->data[bi/8] &= ~m;\n  log_write(bp);\n  brelse(bp);\n}\n\n// Inodes.\n//\n// An inode describes a single unnamed file.\n// The inode disk structure holds metadata: the file's type,\n// its size, the number of links referring to it, and the\n// list of blocks holding the file's content.\n//\n// The inodes are laid out sequentially on disk at block\n// sb.inodestart. Each inode has a number, indicating its\n// position on the disk.\n//\n// The kernel keeps a table of in-use inodes in memory\n// to provide a place for synchronizing access\n// to inodes used by multiple processes. The in-memory\n// inodes include book-keeping information that is\n// not stored on disk: ip->ref and ip->valid.\n//\n// An inode and its in-memory representation go through a\n// sequence of states before they can be used by the\n// rest of the file system code.\n//\n// * Allocation: an inode is allocated if its type (on disk)\n//   is non-zero. ialloc() allocates, and iput() frees if\n//   the reference and link counts have fallen to zero.\n//\n// * Referencing in table: an entry in the inode table\n//   is free if ip->ref is zero. Otherwise ip->ref tracks\n//   the number of in-memory pointers to the entry (open\n//   files and current directories). iget() finds or\n//   creates a table entry and increments its ref; iput()\n//   decrements ref.\n//\n// * Valid: the information (type, size, &c) in an inode\n//   table entry is only correct when ip->valid is 1.\n//   ilock() reads the inode from\n//   the disk and sets ip->valid, while iput() clears\n//   ip->valid if ip->ref has fallen to zero.\n//\n// * Locked: file system code may only examine and modify\n//   the information in an inode and its content if it\n//   has first locked the inode.\n//\n// Thus a typical sequence is:\n//   ip = iget(dev, inum)\n//   ilock(ip)\n//   ... examine and modify ip->xxx ...\n//   iunlock(ip)\n//   iput(ip)\n//\n// ilock() is separate from iget() so that system calls can\n// get a long-term reference to an inode (as for an open file)\n// and only lock it for short periods (e.g., in read()).\n// The separation also helps avoid deadlock and races during\n// pathname lookup. iget() increments ip->ref so that the inode\n// stays in the table and pointers to it remain valid.\n//\n// Many internal file system functions expect the caller to\n// have locked the inodes involved; this lets callers create\n// multi-step atomic operations.\n//\n// The itable.lock spin-lock protects the allocation of itable\n// entries. Since ip->ref indicates whether an entry is free,\n// and ip->dev and ip->inum indicate which i-node an entry\n// holds, one must hold itable.lock while using any of those fields.\n//\n// An ip->lock sleep-lock protects all ip-> fields other than ref,\n// dev, and inum.  One must hold ip->lock in order to\n// read or write that inode's ip->valid, ip->size, ip->type, &c.\n\nstruct {\n  struct spinlock lock;\n  struct inode inode[NINODE];\n} itable;\n\nvoid\niinit()\n{\n  int i = 0;\n  \n  initlock(&itable.lock, \"itable\");\n  for(i = 0; i < NINODE; i++) {\n    initsleeplock(&itable.inode[i].lock, \"inode\");\n  }\n}\n\nstatic struct inode* iget(uint dev, uint inum);\n\n// Allocate an inode on device dev.\n// Mark it as allocated by  giving it type type.\n// Returns an unlocked but allocated and referenced inode,\n// or NULL if there is no free inode.\nstruct inode*\nialloc(uint dev, short type)\n{\n  int inum;\n  struct buf *bp;\n  struct dinode *dip;\n\n  for(inum = 1; inum < sb.ninodes; inum++){\n    bp = bread(dev, IBLOCK(inum, sb));\n    dip = (struct dinode*)bp->data + inum%IPB;\n    if(dip->type == 0){  // a free inode\n      memset(dip, 0, sizeof(*dip));\n      dip->type = type;\n      log_write(bp);   // mark it allocated on the disk\n      brelse(bp);\n      return iget(dev, inum);\n    }\n    brelse(bp);\n  }\n  printf(\"ialloc: no inodes\\n\");\n  return 0;\n}\n\n// Copy a modified in-memory inode to disk.\n// Must be called after every change to an ip->xxx field\n// that lives on disk.\n// Caller must hold ip->lock.\nvoid\niupdate(struct inode *ip)\n{\n  struct buf *bp;\n  struct dinode *dip;\n\n  bp = bread(ip->dev, IBLOCK(ip->inum, sb));\n  dip = (struct dinode*)bp->data + ip->inum%IPB;\n  dip->type = ip->type;\n  dip->major = ip->major;\n  dip->minor = ip->minor;\n  dip->nlink = ip->nlink;\n  dip->size = ip->size;\n  memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));\n  log_write(bp);\n  brelse(bp);\n}\n\n// Find the inode with number inum on device dev\n// and return the in-memory copy. Does not lock\n// the inode and does not read it from disk.\nstatic struct inode*\niget(uint dev, uint inum)\n{\n  struct inode *ip, *empty;\n\n  acquire(&itable.lock);\n\n  // Is the inode already in the table?\n  empty = 0;\n  for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){\n    if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){\n      ip->ref++;\n      release(&itable.lock);\n      return ip;\n    }\n    if(empty == 0 && ip->ref == 0)    // Remember empty slot.\n      empty = ip;\n  }\n\n  // Recycle an inode entry.\n  if(empty == 0)\n    panic(\"iget: no inodes\");\n\n  ip = empty;\n  ip->dev = dev;\n  ip->inum = inum;\n  ip->ref = 1;\n  ip->valid = 0;\n  release(&itable.lock);\n\n  return ip;\n}\n\n// Increment reference count for ip.\n// Returns ip to enable ip = idup(ip1) idiom.\nstruct inode*\nidup(struct inode *ip)\n{\n  acquire(&itable.lock);\n  ip->ref++;\n  release(&itable.lock);\n  return ip;\n}\n\n// Lock the given inode.\n// Reads the inode from disk if necessary.\nvoid\nilock(struct inode *ip)\n{\n  struct buf *bp;\n  struct dinode *dip;\n\n  if(ip == 0 || ip->ref < 1)\n    panic(\"ilock\");\n\n  acquiresleep(&ip->lock);\n\n  if(ip->valid == 0){\n    bp = bread(ip->dev, IBLOCK(ip->inum, sb));\n    dip = (struct dinode*)bp->data + ip->inum%IPB;\n    ip->type = dip->type;\n    ip->major = dip->major;\n    ip->minor = dip->minor;\n    ip->nlink = dip->nlink;\n    ip->size = dip->size;\n    memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));\n    brelse(bp);\n    ip->valid = 1;\n    if(ip->type == 0)\n      panic(\"ilock: no type\");\n  }\n}\n\n// Unlock the given inode.\nvoid\niunlock(struct inode *ip)\n{\n  if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)\n    panic(\"iunlock\");\n\n  releasesleep(&ip->lock);\n}\n\n// Drop a reference to an in-memory inode.\n// If that was the last reference, the inode table entry can\n// be recycled.\n// If that was the last reference and the inode has no links\n// to it, free the inode (and its content) on disk.\n// All calls to iput() must be inside a transaction in\n// case it has to free the inode.\nvoid\niput(struct inode *ip)\n{\n  acquire(&itable.lock);\n\n  if(ip->ref == 1 && ip->valid && ip->nlink == 0){\n    // inode has no links and no other references: truncate and free.\n\n    // ip->ref == 1 means no other process can have ip locked,\n    // so this acquiresleep() won't block (or deadlock).\n    acquiresleep(&ip->lock);\n\n    release(&itable.lock);\n\n    itrunc(ip);\n    ip->type = 0;\n    iupdate(ip);\n    ip->valid = 0;\n\n    releasesleep(&ip->lock);\n\n    acquire(&itable.lock);\n  }\n\n  ip->ref--;\n  release(&itable.lock);\n}\n\n// Common idiom: unlock, then put.\nvoid\niunlockput(struct inode *ip)\n{\n  iunlock(ip);\n  iput(ip);\n}\n\nvoid\nireclaim(int dev)\n{\n  for (int inum = 1; inum < sb.ninodes; inum++) {\n    struct inode *ip = 0;\n    struct buf *bp = bread(dev, IBLOCK(inum, sb));\n    struct dinode *dip = (struct dinode *)bp->data + inum % IPB;\n    if (dip->type != 0 && dip->nlink == 0) {  // is an orphaned inode\n      printf(\"ireclaim: orphaned inode %d\\n\", inum);\n      ip = iget(dev, inum);\n    }\n    brelse(bp);\n    if (ip) {\n      begin_op();\n      ilock(ip);\n      iunlock(ip);\n      iput(ip);\n      end_op();\n    }\n  }\n}\n\n// Inode content\n//\n// The content (data) associated with each inode is stored\n// in blocks on the disk. The first NDIRECT block numbers\n// are listed in ip->addrs[].  The next NINDIRECT blocks are\n// listed in block ip->addrs[NDIRECT].\n\n// Return the disk block address of the nth block in inode ip.\n// If there is no such block, bmap allocates one.\n// returns 0 if out of disk space.\nstatic uint\nbmap(struct inode *ip, uint bn)\n{\n  uint addr, *a;\n  struct buf *bp;\n\n  if(bn < NDIRECT){\n    if((addr = ip->addrs[bn]) == 0){\n      addr = balloc(ip->dev);\n      if(addr == 0)\n        return 0;\n      ip->addrs[bn] = addr;\n    }\n    return addr;\n  }\n  bn -= NDIRECT;\n\n  if(bn < NINDIRECT){\n    // Load indirect block, allocating if necessary.\n    if((addr = ip->addrs[NDIRECT]) == 0){\n      addr = balloc(ip->dev);\n      if(addr == 0)\n        return 0;\n      ip->addrs[NDIRECT] = addr;\n    }\n    bp = bread(ip->dev, addr);\n    a = (uint*)bp->data;\n    if((addr = a[bn]) == 0){\n      addr = balloc(ip->dev);\n      if(addr){\n        a[bn] = addr;\n        log_write(bp);\n      }\n    }\n    brelse(bp);\n    return addr;\n  }\n\n  panic(\"bmap: out of range\");\n}\n\n// Truncate inode (discard contents).\n// Caller must hold ip->lock.\nvoid\nitrunc(struct inode *ip)\n{\n  int i, j;\n  struct buf *bp;\n  uint *a;\n\n  for(i = 0; i < NDIRECT; i++){\n    if(ip->addrs[i]){\n      bfree(ip->dev, ip->addrs[i]);\n      ip->addrs[i] = 0;\n    }\n  }\n\n  if(ip->addrs[NDIRECT]){\n    bp = bread(ip->dev, ip->addrs[NDIRECT]);\n    a = (uint*)bp->data;\n    for(j = 0; j < NINDIRECT; j++){\n      if(a[j])\n        bfree(ip->dev, a[j]);\n    }\n    brelse(bp);\n    bfree(ip->dev, ip->addrs[NDIRECT]);\n    ip->addrs[NDIRECT] = 0;\n  }\n\n  ip->size = 0;\n  iupdate(ip);\n}\n\n// Copy stat information from inode.\n// Caller must hold ip->lock.\nvoid\nstati(struct inode *ip, struct stat *st)\n{\n  st->dev = ip->dev;\n  st->ino = ip->inum;\n  st->type = ip->type;\n  st->nlink = ip->nlink;\n  st->size = ip->size;\n}\n\n// Read data from inode.\n// Caller must hold ip->lock.\n// If user_dst==1, then dst is a user virtual address;\n// otherwise, dst is a kernel address.\nint\nreadi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)\n{\n  uint tot, m;\n  struct buf *bp;\n\n  if(off > ip->size || off + n < off)\n    return 0;\n  if(off + n > ip->size)\n    n = ip->size - off;\n\n  for(tot=0; tot<n; tot+=m, off+=m, dst+=m){\n    uint addr = bmap(ip, off/BSIZE);\n    if(addr == 0)\n      break;\n    bp = bread(ip->dev, addr);\n    m = min(n - tot, BSIZE - off%BSIZE);\n    if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) {\n      brelse(bp);\n      tot = -1;\n      break;\n    }\n    brelse(bp);\n  }\n  return tot;\n}\n\n// Write data to inode.\n// Caller must hold ip->lock.\n// If user_src==1, then src is a user virtual address;\n// otherwise, src is a kernel address.\n// Returns the number of bytes successfully written.\n// If the return value is less than the requested n,\n// there was an error of some kind.\nint\nwritei(struct inode *ip, int user_src, uint64 src, uint off, uint n)\n{\n  uint tot, m;\n  struct buf *bp;\n\n  if(off > ip->size || off + n < off)\n    return -1;\n  if(off + n > MAXFILE*BSIZE)\n    return -1;\n\n  for(tot=0; tot<n; tot+=m, off+=m, src+=m){\n    uint addr = bmap(ip, off/BSIZE);\n    if(addr == 0)\n      break;\n    bp = bread(ip->dev, addr);\n    m = min(n - tot, BSIZE - off%BSIZE);\n    if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) {\n      brelse(bp);\n      break;\n    }\n    log_write(bp);\n    brelse(bp);\n  }\n\n  if(off > ip->size)\n    ip->size = off;\n\n  // write the i-node back to disk even if the size didn't change\n  // because the loop above might have called bmap() and added a new\n  // block to ip->addrs[].\n  iupdate(ip);\n\n  return tot;\n}\n\n// Directories\n\nint\nnamecmp(const char *s, const char *t)\n{\n  return strncmp(s, t, DIRSIZ);\n}\n\n// Look for a directory entry in a directory.\n// If found, set *poff to byte offset of entry.\nstruct inode*\ndirlookup(struct inode *dp, char *name, uint *poff)\n{\n  uint off, inum;\n  struct dirent de;\n\n  if(dp->type != T_DIR)\n    panic(\"dirlookup not DIR\");\n\n  for(off = 0; off < dp->size; off += sizeof(de)){\n    if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))\n      panic(\"dirlookup read\");\n    if(de.inum == 0)\n      continue;\n    if(namecmp(name, de.name) == 0){\n      // entry matches path element\n      if(poff)\n        *poff = off;\n      inum = de.inum;\n      return iget(dp->dev, inum);\n    }\n  }\n\n  return 0;\n}\n\n// Write a new directory entry (name, inum) into the directory dp.\n// Returns 0 on success, -1 on failure (e.g. out of disk blocks).\nint\ndirlink(struct inode *dp, char *name, uint inum)\n{\n  int off;\n  struct dirent de;\n  struct inode *ip;\n\n  // Check that name is not present.\n  if((ip = dirlookup(dp, name, 0)) != 0){\n    iput(ip);\n    return -1;\n  }\n\n  // Look for an empty dirent.\n  for(off = 0; off < dp->size; off += sizeof(de)){\n    if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))\n      panic(\"dirlink read\");\n    if(de.inum == 0)\n      break;\n  }\n\n  strncpy(de.name, name, DIRSIZ);\n  de.inum = inum;\n  if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))\n    return -1;\n\n  return 0;\n}\n\n// Paths\n\n// Copy the next path element from path into name.\n// Return a pointer to the element following the copied one.\n// The returned path has no leading slashes,\n// so the caller can check *path=='\\0' to see if the name is the last one.\n// If no name to remove, return 0.\n//\n// Examples:\n//   skipelem(\"a/bb/c\", name) = \"bb/c\", setting name = \"a\"\n//   skipelem(\"///a//bb\", name) = \"bb\", setting name = \"a\"\n//   skipelem(\"a\", name) = \"\", setting name = \"a\"\n//   skipelem(\"\", name) = skipelem(\"////\", name) = 0\n//\nstatic char*\nskipelem(char *path, char *name)\n{\n  char *s;\n  int len;\n\n  while(*path == '/')\n    path++;\n  if(*path == 0)\n    return 0;\n  s = path;\n  while(*path != '/' && *path != 0)\n    path++;\n  len = path - s;\n  if(len >= DIRSIZ)\n    memmove(name, s, DIRSIZ);\n  else {\n    memmove(name, s, len);\n    name[len] = 0;\n  }\n  while(*path == '/')\n    path++;\n  return path;\n}\n\n// Look up and return the inode for a path name.\n// If parent != 0, return the inode for the parent and copy the final\n// path element into name, which must have room for DIRSIZ bytes.\n// Must be called inside a transaction since it calls iput().\nstatic struct inode*\nnamex(char *path, int nameiparent, char *name)\n{\n  struct inode *ip, *next;\n\n  if(*path == '/')\n    ip = iget(ROOTDEV, ROOTINO);\n  else\n    ip = idup(myproc()->cwd);\n\n  while((path = skipelem(path, name)) != 0){\n    ilock(ip);\n    if(ip->type != T_DIR){\n      iunlockput(ip);\n      return 0;\n    }\n    if(nameiparent && *path == '\\0'){\n      // Stop one level early.\n      iunlock(ip);\n      return ip;\n    }\n    if((next = dirlookup(ip, name, 0)) == 0){\n      iunlockput(ip);\n      return 0;\n    }\n    iunlockput(ip);\n    ip = next;\n  }\n  if(nameiparent){\n    iput(ip);\n    return 0;\n  }\n  return ip;\n}\n\nstruct inode*\nnamei(char *path)\n{\n  char name[DIRSIZ];\n  return namex(path, 0, name);\n}\n\nstruct inode*\nnameiparent(char *path, char *name)\n{\n  return namex(path, 1, name);\n}\n"
  },
  {
    "path": "kernel/fs.h",
    "content": "// On-disk file system format.\n// Both the kernel and user programs use this header file.\n\n\n#define ROOTINO  1   // root i-number\n#define BSIZE 1024  // block size\n\n// Disk layout:\n// [ boot block | super block | log | inode blocks |\n//                                          free bit map | data blocks]\n//\n// mkfs computes the super block and builds an initial file system. The\n// super block describes the disk layout:\nstruct superblock {\n  uint magic;        // Must be FSMAGIC\n  uint size;         // Size of file system image (blocks)\n  uint nblocks;      // Number of data blocks\n  uint ninodes;      // Number of inodes.\n  uint nlog;         // Number of log blocks\n  uint logstart;     // Block number of first log block\n  uint inodestart;   // Block number of first inode block\n  uint bmapstart;    // Block number of first free map block\n};\n\n#define FSMAGIC 0x10203040\n\n#define NDIRECT 12\n#define NINDIRECT (BSIZE / sizeof(uint))\n#define MAXFILE (NDIRECT + NINDIRECT)\n\n// On-disk inode structure\nstruct dinode {\n  short type;           // File type\n  short major;          // Major device number (T_DEVICE only)\n  short minor;          // Minor device number (T_DEVICE only)\n  short nlink;          // Number of links to inode in file system\n  uint size;            // Size of file (bytes)\n  uint addrs[NDIRECT+1];   // Data block addresses\n};\n\n// Inodes per block.\n#define IPB           (BSIZE / sizeof(struct dinode))\n\n// Block containing inode i\n#define IBLOCK(i, sb)     ((i) / IPB + sb.inodestart)\n\n// Bitmap bits per block\n#define BPB           (BSIZE*8)\n\n// Block of free map containing bit for block b\n#define BBLOCK(b, sb) ((b)/BPB + sb.bmapstart)\n\n// Directory is a file containing a sequence of dirent structures.\n#define DIRSIZ 14\n\n// The name field may have DIRSIZ characters and not end in a NUL\n// character.\nstruct dirent {\n  ushort inum;\n  char name[DIRSIZ] __attribute__((nonstring));\n};\n\n"
  },
  {
    "path": "kernel/kalloc.c",
    "content": "// Physical memory allocator, for user processes,\n// kernel stacks, page-table pages,\n// and pipe buffers. Allocates whole 4096-byte pages.\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"spinlock.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n\nvoid freerange(void *pa_start, void *pa_end);\n\nextern char end[]; // first address after kernel.\n                   // defined by kernel.ld.\n\nstruct run {\n  struct run *next;\n};\n\nstruct {\n  struct spinlock lock;\n  struct run *freelist;\n} kmem;\n\nvoid\nkinit()\n{\n  initlock(&kmem.lock, \"kmem\");\n  freerange(end, (void*)PHYSTOP);\n}\n\nvoid\nfreerange(void *pa_start, void *pa_end)\n{\n  char *p;\n  p = (char*)PGROUNDUP((uint64)pa_start);\n  for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)\n    kfree(p);\n}\n\n// Free the page of physical memory pointed at by pa,\n// which normally should have been returned by a\n// call to kalloc().  (The exception is when\n// initializing the allocator; see kinit above.)\nvoid\nkfree(void *pa)\n{\n  struct run *r;\n\n  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)\n    panic(\"kfree\");\n\n  // Fill with junk to catch dangling refs.\n  memset(pa, 1, PGSIZE);\n\n  r = (struct run*)pa;\n\n  acquire(&kmem.lock);\n  r->next = kmem.freelist;\n  kmem.freelist = r;\n  release(&kmem.lock);\n}\n\n// Allocate one 4096-byte page of physical memory.\n// Returns a pointer that the kernel can use.\n// Returns 0 if the memory cannot be allocated.\nvoid *\nkalloc(void)\n{\n  struct run *r;\n\n  acquire(&kmem.lock);\n  r = kmem.freelist;\n  if(r)\n    kmem.freelist = r->next;\n  release(&kmem.lock);\n\n  if(r)\n    memset((char*)r, 5, PGSIZE); // fill with junk\n  return (void*)r;\n}\n"
  },
  {
    "path": "kernel/kernel.ld",
    "content": "OUTPUT_ARCH( \"riscv\" )\nENTRY( _entry )\n\nSECTIONS\n{\n  /*\n   * ensure that entry.S / _entry is at 0x80000000,\n   * where qemu's -kernel jumps.\n   */\n  . = 0x80000000;\n\n  .text : {\n    kernel/entry.o(_entry)\n    *(.text .text.*)\n    . = ALIGN(0x1000);\n    _trampoline = .;\n    *(trampsec)\n    . = ALIGN(0x1000);\n    ASSERT(. - _trampoline == 0x1000, \"error: trampoline larger than one page\");\n    PROVIDE(etext = .);\n  }\n\n  .rodata : {\n    . = ALIGN(16);\n    *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */\n    . = ALIGN(16);\n    *(.rodata .rodata.*)\n  }\n\n  .data : {\n    . = ALIGN(16);\n    *(.sdata .sdata.*) /* do not need to distinguish this from .data */\n    . = ALIGN(16);\n    *(.data .data.*)\n  }\n\n  .bss : {\n    . = ALIGN(16);\n    *(.sbss .sbss.*) /* do not need to distinguish this from .bss */\n    . = ALIGN(16);\n    *(.bss .bss.*)\n  }\n\n  PROVIDE(end = .);\n}\n"
  },
  {
    "path": "kernel/kernelvec.S",
    "content": "        #\n        # interrupts and exceptions while in supervisor\n        # mode come here.\n        #\n        # the current stack is a kernel stack.\n        # push registers, call kerneltrap().\n        # when kerneltrap() returns, restore registers, return.\n        #\n.globl kerneltrap\n.globl kernelvec\n.align 4\nkernelvec:\n        # make room to save registers.\n        addi sp, sp, -256\n\n        # save caller-saved registers.\n        sd ra, 0(sp)\n        # sd sp, 8(sp)\n        sd gp, 16(sp)\n        sd tp, 24(sp)\n        sd t0, 32(sp)\n        sd t1, 40(sp)\n        sd t2, 48(sp)\n        sd a0, 72(sp)\n        sd a1, 80(sp)\n        sd a2, 88(sp)\n        sd a3, 96(sp)\n        sd a4, 104(sp)\n        sd a5, 112(sp)\n        sd a6, 120(sp)\n        sd a7, 128(sp)\n        sd t3, 216(sp)\n        sd t4, 224(sp)\n        sd t5, 232(sp)\n        sd t6, 240(sp)\n\n        # call the C trap handler in trap.c\n        call kerneltrap\n\n        # restore registers.\n        ld ra, 0(sp)\n        # ld sp, 8(sp)\n        ld gp, 16(sp)\n        # not tp (contains hartid), in case we moved CPUs\n        ld t0, 32(sp)\n        ld t1, 40(sp)\n        ld t2, 48(sp)\n        ld a0, 72(sp)\n        ld a1, 80(sp)\n        ld a2, 88(sp)\n        ld a3, 96(sp)\n        ld a4, 104(sp)\n        ld a5, 112(sp)\n        ld a6, 120(sp)\n        ld a7, 128(sp)\n        ld t3, 216(sp)\n        ld t4, 224(sp)\n        ld t5, 232(sp)\n        ld t6, 240(sp)\n\n        addi sp, sp, 256\n\n        # return to whatever we were doing in the kernel.\n        sret\n"
  },
  {
    "path": "kernel/log.c",
    "content": "#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"fs.h\"\n#include \"buf.h\"\n\n// Simple logging that allows concurrent FS system calls.\n//\n// A log transaction contains the updates of multiple FS system\n// calls. The logging system only commits when there are\n// no FS system calls active. Thus there is never\n// any reasoning required about whether a commit might\n// write an uncommitted system call's updates to disk.\n//\n// A system call should call begin_op()/end_op() to mark\n// its start and end. Usually begin_op() just increments\n// the count of in-progress FS system calls and returns.\n// But if it thinks the log is close to running out, it\n// sleeps until the last outstanding end_op() commits.\n//\n// The log is a physical re-do log containing disk blocks.\n// The on-disk log format:\n//   header block, containing block #s for block A, B, C, ...\n//   block A\n//   block B\n//   block C\n//   ...\n// Log appends are synchronous.\n\n// Contents of the header block, used for both the on-disk header block\n// and to keep track in memory of logged block# before commit.\nstruct logheader {\n  int n;\n  int block[LOGBLOCKS];\n};\n\nstruct log {\n  struct spinlock lock;\n  int start;\n  int outstanding; // how many FS sys calls are executing.\n  int committing;  // in commit(), please wait.\n  int dev;\n  struct logheader lh;\n};\nstruct log log;\n\nstatic void recover_from_log(void);\nstatic void commit();\n\nvoid\ninitlog(int dev, struct superblock *sb)\n{\n  if (sizeof(struct logheader) >= BSIZE)\n    panic(\"initlog: too big logheader\");\n\n  initlock(&log.lock, \"log\");\n  log.start = sb->logstart;\n  log.dev = dev;\n  recover_from_log();\n}\n\n// Copy committed blocks from log to their home location\nstatic void\ninstall_trans(int recovering)\n{\n  int tail;\n\n  for (tail = 0; tail < log.lh.n; tail++) {\n    if(recovering) {\n      printf(\"recovering tail %d dst %d\\n\", tail, log.lh.block[tail]);\n    }\n    struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block\n    struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst\n    memmove(dbuf->data, lbuf->data, BSIZE);  // copy block to dst\n    bwrite(dbuf);  // write dst to disk\n    if(recovering == 0)\n      bunpin(dbuf);\n    brelse(lbuf);\n    brelse(dbuf);\n  }\n}\n\n// Read the log header from disk into the in-memory log header\nstatic void\nread_head(void)\n{\n  struct buf *buf = bread(log.dev, log.start);\n  struct logheader *lh = (struct logheader *) (buf->data);\n  int i;\n  log.lh.n = lh->n;\n  for (i = 0; i < log.lh.n; i++) {\n    log.lh.block[i] = lh->block[i];\n  }\n  brelse(buf);\n}\n\n// Write in-memory log header to disk.\n// This is the true point at which the\n// current transaction commits.\nstatic void\nwrite_head(void)\n{\n  struct buf *buf = bread(log.dev, log.start);\n  struct logheader *hb = (struct logheader *) (buf->data);\n  int i;\n  hb->n = log.lh.n;\n  for (i = 0; i < log.lh.n; i++) {\n    hb->block[i] = log.lh.block[i];\n  }\n  bwrite(buf);\n  brelse(buf);\n}\n\nstatic void\nrecover_from_log(void)\n{\n  read_head();\n  install_trans(1); // if committed, copy from log to disk\n  log.lh.n = 0;\n  write_head(); // clear the log\n}\n\n// called at the start of each FS system call.\nvoid\nbegin_op(void)\n{\n  acquire(&log.lock);\n  while(1){\n    if(log.committing){\n      sleep(&log, &log.lock);\n    } else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGBLOCKS){\n      // this op might exhaust log space; wait for commit.\n      sleep(&log, &log.lock);\n    } else {\n      log.outstanding += 1;\n      release(&log.lock);\n      break;\n    }\n  }\n}\n\n// called at the end of each FS system call.\n// commits if this was the last outstanding operation.\nvoid\nend_op(void)\n{\n  int do_commit = 0;\n\n  acquire(&log.lock);\n  log.outstanding -= 1;\n  if(log.committing)\n    panic(\"log.committing\");\n  if(log.outstanding == 0){\n    do_commit = 1;\n    log.committing = 1;\n  } else {\n    // begin_op() may be waiting for log space,\n    // and decrementing log.outstanding has decreased\n    // the amount of reserved space.\n    wakeup(&log);\n  }\n  release(&log.lock);\n\n  if(do_commit){\n    // call commit w/o holding locks, since not allowed\n    // to sleep with locks.\n    commit();\n    acquire(&log.lock);\n    log.committing = 0;\n    wakeup(&log);\n    release(&log.lock);\n  }\n}\n\n// Copy modified blocks from cache to log.\nstatic void\nwrite_log(void)\n{\n  int tail;\n\n  for (tail = 0; tail < log.lh.n; tail++) {\n    struct buf *to = bread(log.dev, log.start+tail+1); // log block\n    struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block\n    memmove(to->data, from->data, BSIZE);\n    bwrite(to);  // write the log\n    brelse(from);\n    brelse(to);\n  }\n}\n\nstatic void\ncommit()\n{\n  if (log.lh.n > 0) {\n    write_log();     // Write modified blocks from cache to log\n    write_head();    // Write header to disk -- the real commit\n    install_trans(0); // Now install writes to home locations\n    log.lh.n = 0;\n    write_head();    // Erase the transaction from the log\n  }\n}\n\n// Caller has modified b->data and is done with the buffer.\n// Record the block number and pin in the cache by increasing refcnt.\n// commit()/write_log() will do the disk write.\n//\n// log_write() replaces bwrite(); a typical use is:\n//   bp = bread(...)\n//   modify bp->data[]\n//   log_write(bp)\n//   brelse(bp)\nvoid\nlog_write(struct buf *b)\n{\n  int i;\n\n  acquire(&log.lock);\n  if (log.lh.n >= LOGBLOCKS)\n    panic(\"too big a transaction\");\n  if (log.outstanding < 1)\n    panic(\"log_write outside of trans\");\n\n  for (i = 0; i < log.lh.n; i++) {\n    if (log.lh.block[i] == b->blockno)   // log absorption\n      break;\n  }\n  log.lh.block[i] = b->blockno;\n  if (i == log.lh.n) {  // Add new block to log?\n    bpin(b);\n    log.lh.n++;\n  }\n  release(&log.lock);\n}\n\n"
  },
  {
    "path": "kernel/main.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n\nvolatile static int started = 0;\n\n// start() jumps here in supervisor mode on all CPUs.\nvoid\nmain()\n{\n  if(cpuid() == 0){\n    consoleinit();\n    printfinit();\n    printf(\"\\n\");\n    printf(\"xv6 kernel is booting\\n\");\n    printf(\"\\n\");\n    kinit();         // physical page allocator\n    kvminit();       // create kernel page table\n    kvminithart();   // turn on paging\n    procinit();      // process table\n    trapinit();      // trap vectors\n    trapinithart();  // install kernel trap vector\n    plicinit();      // set up interrupt controller\n    plicinithart();  // ask PLIC for device interrupts\n    binit();         // buffer cache\n    iinit();         // inode table\n    fileinit();      // file table\n    virtio_disk_init(); // emulated hard disk\n    userinit();      // first user process\n    __sync_synchronize();\n    started = 1;\n  } else {\n    while(started == 0)\n      ;\n    __sync_synchronize();\n    printf(\"hart %d starting\\n\", cpuid());\n    kvminithart();    // turn on paging\n    trapinithart();   // install kernel trap vector\n    plicinithart();   // ask PLIC for device interrupts\n  }\n\n  scheduler();        \n}\n"
  },
  {
    "path": "kernel/memlayout.h",
    "content": "// Physical memory layout\n\n// qemu -machine virt is set up like this,\n// based on qemu's hw/riscv/virt.c:\n//\n// 00001000 -- boot ROM, provided by qemu\n// 02000000 -- CLINT\n// 0C000000 -- PLIC\n// 10000000 -- uart0 \n// 10001000 -- virtio disk \n// 80000000 -- qemu's boot ROM loads the kernel here,\n//             then jumps here.\n// unused RAM after 80000000.\n\n// the kernel uses physical memory thus:\n// 80000000 -- entry.S, then kernel text and data\n// end -- start of kernel page allocation area\n// PHYSTOP -- end RAM used by the kernel\n\n// qemu puts UART registers here in physical memory.\n#define UART0 0x10000000L\n#define UART0_IRQ 10\n\n// virtio mmio interface\n#define VIRTIO0 0x10001000\n#define VIRTIO0_IRQ 1\n\n// qemu puts platform-level interrupt controller (PLIC) here.\n#define PLIC 0x0c000000L\n#define PLIC_PRIORITY (PLIC + 0x0)\n#define PLIC_PENDING (PLIC + 0x1000)\n#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)\n#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)\n#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000)\n\n// the kernel expects there to be RAM\n// for use by the kernel and user pages\n// from physical address 0x80000000 to PHYSTOP.\n#define KERNBASE 0x80000000L\n#define PHYSTOP (KERNBASE + 128*1024*1024)\n\n// map the trampoline page to the highest address,\n// in both user and kernel space.\n#define TRAMPOLINE (MAXVA - PGSIZE)\n\n// map kernel stacks beneath the trampoline,\n// each surrounded by invalid guard pages.\n#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)\n\n// User memory layout.\n// Address zero first:\n//   text\n//   original data and bss\n//   fixed-size stack\n//   expandable heap\n//   ...\n//   TRAPFRAME (p->trapframe, used by the trampoline)\n//   TRAMPOLINE (the same page as in the kernel)\n#define TRAPFRAME (TRAMPOLINE - PGSIZE)\n"
  },
  {
    "path": "kernel/param.h",
    "content": "#define NPROC        64  // maximum number of processes\n#define NCPU          8  // maximum number of CPUs\n#define NOFILE       16  // open files per process\n#define NFILE       100  // open files per system\n#define NINODE       50  // maximum number of active i-nodes\n#define NDEV         10  // maximum major device number\n#define ROOTDEV       1  // device number of file system root disk\n#define MAXARG       32  // max exec arguments\n#define MAXOPBLOCKS  10  // max # of blocks any FS op writes\n#define LOGBLOCKS    (MAXOPBLOCKS*3)  // max data blocks in on-disk log\n#define NBUF         (MAXOPBLOCKS*3)  // size of disk block cache\n#define FSSIZE       2000  // size of file system in blocks\n#define MAXPATH      128   // maximum file path name\n#define USERSTACK    1     // user stack pages\n\n"
  },
  {
    "path": "kernel/pipe.c",
    "content": "#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"fs.h\"\n#include \"sleeplock.h\"\n#include \"file.h\"\n\n#define PIPESIZE 512\n\nstruct pipe {\n  struct spinlock lock;\n  char data[PIPESIZE];\n  uint nread;     // number of bytes read\n  uint nwrite;    // number of bytes written\n  int readopen;   // read fd is still open\n  int writeopen;  // write fd is still open\n};\n\nint\npipealloc(struct file **f0, struct file **f1)\n{\n  struct pipe *pi;\n\n  pi = 0;\n  *f0 = *f1 = 0;\n  if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)\n    goto bad;\n  if((pi = (struct pipe*)kalloc()) == 0)\n    goto bad;\n  pi->readopen = 1;\n  pi->writeopen = 1;\n  pi->nwrite = 0;\n  pi->nread = 0;\n  initlock(&pi->lock, \"pipe\");\n  (*f0)->type = FD_PIPE;\n  (*f0)->readable = 1;\n  (*f0)->writable = 0;\n  (*f0)->pipe = pi;\n  (*f1)->type = FD_PIPE;\n  (*f1)->readable = 0;\n  (*f1)->writable = 1;\n  (*f1)->pipe = pi;\n  return 0;\n\n bad:\n  if(pi)\n    kfree((char*)pi);\n  if(*f0)\n    fileclose(*f0);\n  if(*f1)\n    fileclose(*f1);\n  return -1;\n}\n\nvoid\npipeclose(struct pipe *pi, int writable)\n{\n  acquire(&pi->lock);\n  if(writable){\n    pi->writeopen = 0;\n    wakeup(&pi->nread);\n  } else {\n    pi->readopen = 0;\n    wakeup(&pi->nwrite);\n  }\n  if(pi->readopen == 0 && pi->writeopen == 0){\n    release(&pi->lock);\n    kfree((char*)pi);\n  } else\n    release(&pi->lock);\n}\n\nint\npipewrite(struct pipe *pi, uint64 addr, int n)\n{\n  int i = 0;\n  struct proc *pr = myproc();\n\n  acquire(&pi->lock);\n  while(i < n){\n    if(pi->readopen == 0 || killed(pr)){\n      release(&pi->lock);\n      return -1;\n    }\n    if(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full\n      wakeup(&pi->nread);\n      sleep(&pi->nwrite, &pi->lock);\n    } else {\n      char ch;\n      if(copyin(pr->pagetable, &ch, addr + i, 1) == -1)\n        break;\n      pi->data[pi->nwrite++ % PIPESIZE] = ch;\n      i++;\n    }\n  }\n  wakeup(&pi->nread);\n  release(&pi->lock);\n\n  return i;\n}\n\nint\npiperead(struct pipe *pi, uint64 addr, int n)\n{\n  int i;\n  struct proc *pr = myproc();\n  char ch;\n\n  acquire(&pi->lock);\n  while(pi->nread == pi->nwrite && pi->writeopen){  //DOC: pipe-empty\n    if(killed(pr)){\n      release(&pi->lock);\n      return -1;\n    }\n    sleep(&pi->nread, &pi->lock); //DOC: piperead-sleep\n  }\n  for(i = 0; i < n; i++){  //DOC: piperead-copy\n    if(pi->nread == pi->nwrite)\n      break;\n    ch = pi->data[pi->nread % PIPESIZE];\n    if(copyout(pr->pagetable, addr + i, &ch, 1) == -1) {\n      if(i == 0)\n        i = -1;\n      break;\n    }\n    pi->nread++;\n  }\n  wakeup(&pi->nwrite);  //DOC: piperead-wakeup\n  release(&pi->lock);\n  return i;\n}\n"
  },
  {
    "path": "kernel/plic.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n\n//\n// the riscv Platform Level Interrupt Controller (PLIC).\n//\n\nvoid\nplicinit(void)\n{\n  // set desired IRQ priorities non-zero (otherwise disabled).\n  *(uint32*)(PLIC + UART0_IRQ*4) = 1;\n  *(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;\n}\n\nvoid\nplicinithart(void)\n{\n  int hart = cpuid();\n  \n  // set enable bits for this hart's S-mode\n  // for the uart and virtio disk.\n  *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);\n\n  // set this hart's S-mode priority threshold to 0.\n  *(uint32*)PLIC_SPRIORITY(hart) = 0;\n}\n\n// ask the PLIC what interrupt we should serve.\nint\nplic_claim(void)\n{\n  int hart = cpuid();\n  int irq = *(uint32*)PLIC_SCLAIM(hart);\n  return irq;\n}\n\n// tell the PLIC we've served this IRQ.\nvoid\nplic_complete(int irq)\n{\n  int hart = cpuid();\n  *(uint32*)PLIC_SCLAIM(hart) = irq;\n}\n"
  },
  {
    "path": "kernel/printf.c",
    "content": "//\n// formatted console output -- printf, panic.\n//\n\n#include <stdarg.h>\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"fs.h\"\n#include \"file.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"proc.h\"\n\nvolatile int panicking = 0; // printing a panic message\nvolatile int panicked = 0; // spinning forever at end of a panic\n\n// lock to avoid interleaving concurrent printf's.\nstatic struct {\n  struct spinlock lock;\n} pr;\n\nstatic char digits[] = \"0123456789abcdef\";\n\nstatic void\nprintint(long long xx, int base, int sign)\n{\n  char buf[20];\n  int i;\n  unsigned long long x;\n\n  if(sign && (sign = (xx < 0)))\n    x = -xx;\n  else\n    x = xx;\n\n  i = 0;\n  do {\n    buf[i++] = digits[x % base];\n  } while((x /= base) != 0);\n\n  if(sign)\n    buf[i++] = '-';\n\n  while(--i >= 0)\n    consputc(buf[i]);\n}\n\nstatic void\nprintptr(uint64 x)\n{\n  int i;\n  consputc('0');\n  consputc('x');\n  for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)\n    consputc(digits[x >> (sizeof(uint64) * 8 - 4)]);\n}\n\n// Print to the console.\nint\nprintf(char *fmt, ...)\n{\n  va_list ap;\n  int i, cx, c0, c1, c2;\n  char *s;\n\n  if(panicking == 0)\n    acquire(&pr.lock);\n\n  va_start(ap, fmt);\n  for(i = 0; (cx = fmt[i] & 0xff) != 0; i++){\n    if(cx != '%'){\n      consputc(cx);\n      continue;\n    }\n    i++;\n    c0 = fmt[i+0] & 0xff;\n    c1 = c2 = 0;\n    if(c0) c1 = fmt[i+1] & 0xff;\n    if(c1) c2 = fmt[i+2] & 0xff;\n    if(c0 == 'd'){\n      printint(va_arg(ap, int), 10, 1);\n    } else if(c0 == 'l' && c1 == 'd'){\n      printint(va_arg(ap, uint64), 10, 1);\n      i += 1;\n    } else if(c0 == 'l' && c1 == 'l' && c2 == 'd'){\n      printint(va_arg(ap, uint64), 10, 1);\n      i += 2;\n    } else if(c0 == 'u'){\n      printint(va_arg(ap, uint32), 10, 0);\n    } else if(c0 == 'l' && c1 == 'u'){\n      printint(va_arg(ap, uint64), 10, 0);\n      i += 1;\n    } else if(c0 == 'l' && c1 == 'l' && c2 == 'u'){\n      printint(va_arg(ap, uint64), 10, 0);\n      i += 2;\n    } else if(c0 == 'x'){\n      printint(va_arg(ap, uint32), 16, 0);\n    } else if(c0 == 'l' && c1 == 'x'){\n      printint(va_arg(ap, uint64), 16, 0);\n      i += 1;\n    } else if(c0 == 'l' && c1 == 'l' && c2 == 'x'){\n      printint(va_arg(ap, uint64), 16, 0);\n      i += 2;\n    } else if(c0 == 'p'){\n      printptr(va_arg(ap, uint64));\n    } else if(c0 == 'c'){\n      consputc(va_arg(ap, uint));\n    } else if(c0 == 's'){\n      if((s = va_arg(ap, char*)) == 0)\n        s = \"(null)\";\n      for(; *s; s++)\n        consputc(*s);\n    } else if(c0 == '%'){\n      consputc('%');\n    } else if(c0 == 0){\n      break;\n    } else {\n      // Print unknown % sequence to draw attention.\n      consputc('%');\n      consputc(c0);\n    }\n\n  }\n  va_end(ap);\n\n  if(panicking == 0)\n    release(&pr.lock);\n\n  return 0;\n}\n\nvoid\npanic(char *s)\n{\n  panicking = 1;\n  printf(\"panic: \");\n  printf(\"%s\\n\", s);\n  panicked = 1; // freeze uart output from other CPUs\n  for(;;)\n    ;\n}\n\nvoid\nprintfinit(void)\n{\n  initlock(&pr.lock, \"pr\");\n}\n"
  },
  {
    "path": "kernel/proc.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"defs.h\"\n\nstruct cpu cpus[NCPU];\n\nstruct proc proc[NPROC];\n\nstruct proc *initproc;\n\nint nextpid = 1;\nstruct spinlock pid_lock;\n\nextern void forkret(void);\nstatic void freeproc(struct proc *p);\n\nextern char trampoline[]; // trampoline.S\n\n// helps ensure that wakeups of wait()ing\n// parents are not lost. helps obey the\n// memory model when using p->parent.\n// must be acquired before any p->lock.\nstruct spinlock wait_lock;\n\n// Allocate a page for each process's kernel stack.\n// Map it high in memory, followed by an invalid\n// guard page.\nvoid\nproc_mapstacks(pagetable_t kpgtbl)\n{\n  struct proc *p;\n  \n  for(p = proc; p < &proc[NPROC]; p++) {\n    char *pa = kalloc();\n    if(pa == 0)\n      panic(\"kalloc\");\n    uint64 va = KSTACK((int) (p - proc));\n    kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);\n  }\n}\n\n// initialize the proc table.\nvoid\nprocinit(void)\n{\n  struct proc *p;\n  \n  initlock(&pid_lock, \"nextpid\");\n  initlock(&wait_lock, \"wait_lock\");\n  for(p = proc; p < &proc[NPROC]; p++) {\n      initlock(&p->lock, \"proc\");\n      p->state = UNUSED;\n      p->kstack = KSTACK((int) (p - proc));\n  }\n}\n\n// Must be called with interrupts disabled,\n// to prevent race with process being moved\n// to a different CPU.\nint\ncpuid()\n{\n  int id = r_tp();\n  return id;\n}\n\n// Return this CPU's cpu struct.\n// Interrupts must be disabled.\nstruct cpu*\nmycpu(void)\n{\n  int id = cpuid();\n  struct cpu *c = &cpus[id];\n  return c;\n}\n\n// Return the current struct proc *, or zero if none.\nstruct proc*\nmyproc(void)\n{\n  push_off();\n  struct cpu *c = mycpu();\n  struct proc *p = c->proc;\n  pop_off();\n  return p;\n}\n\nint\nallocpid()\n{\n  int pid;\n  \n  acquire(&pid_lock);\n  pid = nextpid;\n  nextpid = nextpid + 1;\n  release(&pid_lock);\n\n  return pid;\n}\n\n// Look in the process table for an UNUSED proc.\n// If found, initialize state required to run in the kernel,\n// and return with p->lock held.\n// If there are no free procs, or a memory allocation fails, return 0.\nstatic struct proc*\nallocproc(void)\n{\n  struct proc *p;\n\n  for(p = proc; p < &proc[NPROC]; p++) {\n    acquire(&p->lock);\n    if(p->state == UNUSED) {\n      goto found;\n    } else {\n      release(&p->lock);\n    }\n  }\n  return 0;\n\nfound:\n  p->pid = allocpid();\n  p->state = USED;\n\n  // Allocate a trapframe page.\n  if((p->trapframe = (struct trapframe *)kalloc()) == 0){\n    freeproc(p);\n    release(&p->lock);\n    return 0;\n  }\n\n  // An empty user page table.\n  p->pagetable = proc_pagetable(p);\n  if(p->pagetable == 0){\n    freeproc(p);\n    release(&p->lock);\n    return 0;\n  }\n\n  // Set up new context to start executing at forkret,\n  // which returns to user space.\n  memset(&p->context, 0, sizeof(p->context));\n  p->context.ra = (uint64)forkret;\n  p->context.sp = p->kstack + PGSIZE;\n\n  return p;\n}\n\n// free a proc structure and the data hanging from it,\n// including user pages.\n// p->lock must be held.\nstatic void\nfreeproc(struct proc *p)\n{\n  if(p->trapframe)\n    kfree((void*)p->trapframe);\n  p->trapframe = 0;\n  if(p->pagetable)\n    proc_freepagetable(p->pagetable, p->sz);\n  p->pagetable = 0;\n  p->sz = 0;\n  p->pid = 0;\n  p->parent = 0;\n  p->name[0] = 0;\n  p->chan = 0;\n  p->killed = 0;\n  p->xstate = 0;\n  p->state = UNUSED;\n}\n\n// Create a user page table for a given process, with no user memory,\n// but with trampoline and trapframe pages.\npagetable_t\nproc_pagetable(struct proc *p)\n{\n  pagetable_t pagetable;\n\n  // An empty page table.\n  pagetable = uvmcreate();\n  if(pagetable == 0)\n    return 0;\n\n  // map the trampoline code (for system call return)\n  // at the highest user virtual address.\n  // only the supervisor uses it, on the way\n  // to/from user space, so not PTE_U.\n  if(mappages(pagetable, TRAMPOLINE, PGSIZE,\n              (uint64)trampoline, PTE_R | PTE_X) < 0){\n    uvmfree(pagetable, 0);\n    return 0;\n  }\n\n  // map the trapframe page just below the trampoline page, for\n  // trampoline.S.\n  if(mappages(pagetable, TRAPFRAME, PGSIZE,\n              (uint64)(p->trapframe), PTE_R | PTE_W) < 0){\n    uvmunmap(pagetable, TRAMPOLINE, 1, 0);\n    uvmfree(pagetable, 0);\n    return 0;\n  }\n\n  return pagetable;\n}\n\n// Free a process's page table, and free the\n// physical memory it refers to.\nvoid\nproc_freepagetable(pagetable_t pagetable, uint64 sz)\n{\n  uvmunmap(pagetable, TRAMPOLINE, 1, 0);\n  uvmunmap(pagetable, TRAPFRAME, 1, 0);\n  uvmfree(pagetable, sz);\n}\n\n// Set up first user process.\nvoid\nuserinit(void)\n{\n  struct proc *p;\n\n  p = allocproc();\n  initproc = p;\n  \n  p->cwd = namei(\"/\");\n\n  p->state = RUNNABLE;\n\n  release(&p->lock);\n}\n\n// Grow or shrink user memory by n bytes.\n// Return 0 on success, -1 on failure.\nint\ngrowproc(int n)\n{\n  uint64 sz;\n  struct proc *p = myproc();\n\n  sz = p->sz;\n  if(n > 0){\n    if(sz + n > TRAPFRAME) {\n      return -1;\n    }\n    if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) {\n      return -1;\n    }\n  } else if(n < 0){\n    sz = uvmdealloc(p->pagetable, sz, sz + n);\n  }\n  p->sz = sz;\n  return 0;\n}\n\n// Create a new process, copying the parent.\n// Sets up child kernel stack to return as if from fork() system call.\nint\nkfork(void)\n{\n  int i, pid;\n  struct proc *np;\n  struct proc *p = myproc();\n\n  // Allocate process.\n  if((np = allocproc()) == 0){\n    return -1;\n  }\n\n  // Copy user memory from parent to child.\n  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){\n    freeproc(np);\n    release(&np->lock);\n    return -1;\n  }\n  np->sz = p->sz;\n\n  // copy saved user registers.\n  *(np->trapframe) = *(p->trapframe);\n\n  // Cause fork to return 0 in the child.\n  np->trapframe->a0 = 0;\n\n  // increment reference counts on open file descriptors.\n  for(i = 0; i < NOFILE; i++)\n    if(p->ofile[i])\n      np->ofile[i] = filedup(p->ofile[i]);\n  np->cwd = idup(p->cwd);\n\n  safestrcpy(np->name, p->name, sizeof(p->name));\n\n  pid = np->pid;\n\n  release(&np->lock);\n\n  acquire(&wait_lock);\n  np->parent = p;\n  release(&wait_lock);\n\n  acquire(&np->lock);\n  np->state = RUNNABLE;\n  release(&np->lock);\n\n  return pid;\n}\n\n// Pass p's abandoned children to init.\n// Caller must hold wait_lock.\nvoid\nreparent(struct proc *p)\n{\n  struct proc *pp;\n\n  for(pp = proc; pp < &proc[NPROC]; pp++){\n    if(pp->parent == p){\n      pp->parent = initproc;\n      wakeup(initproc);\n    }\n  }\n}\n\n// Exit the current process.  Does not return.\n// An exited process remains in the zombie state\n// until its parent calls wait().\nvoid\nkexit(int status)\n{\n  struct proc *p = myproc();\n\n  if(p == initproc)\n    panic(\"init exiting\");\n\n  // Close all open files.\n  for(int fd = 0; fd < NOFILE; fd++){\n    if(p->ofile[fd]){\n      struct file *f = p->ofile[fd];\n      fileclose(f);\n      p->ofile[fd] = 0;\n    }\n  }\n\n  begin_op();\n  iput(p->cwd);\n  end_op();\n  p->cwd = 0;\n\n  acquire(&wait_lock);\n\n  // Give any children to init.\n  reparent(p);\n\n  // Parent might be sleeping in wait().\n  wakeup(p->parent);\n  \n  acquire(&p->lock);\n\n  p->xstate = status;\n  p->state = ZOMBIE;\n\n  release(&wait_lock);\n\n  // Jump into the scheduler, never to return.\n  sched();\n  panic(\"zombie exit\");\n}\n\n// Wait for a child process to exit and return its pid.\n// Return -1 if this process has no children.\nint\nkwait(uint64 addr)\n{\n  struct proc *pp;\n  int havekids, pid;\n  struct proc *p = myproc();\n\n  acquire(&wait_lock);\n\n  for(;;){\n    // Scan through table looking for exited children.\n    havekids = 0;\n    for(pp = proc; pp < &proc[NPROC]; pp++){\n      if(pp->parent == p){\n        // make sure the child isn't still in exit() or swtch().\n        acquire(&pp->lock);\n\n        havekids = 1;\n        if(pp->state == ZOMBIE){\n          // Found one.\n          pid = pp->pid;\n          if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate,\n                                  sizeof(pp->xstate)) < 0) {\n            release(&pp->lock);\n            release(&wait_lock);\n            return -1;\n          }\n          freeproc(pp);\n          release(&pp->lock);\n          release(&wait_lock);\n          return pid;\n        }\n        release(&pp->lock);\n      }\n    }\n\n    // No point waiting if we don't have any children.\n    if(!havekids || killed(p)){\n      release(&wait_lock);\n      return -1;\n    }\n    \n    // Wait for a child to exit.\n    sleep(p, &wait_lock);  //DOC: wait-sleep\n  }\n}\n\n// Per-CPU process scheduler.\n// Each CPU calls scheduler() after setting itself up.\n// Scheduler never returns.  It loops, doing:\n//  - choose a process to run.\n//  - swtch to start running that process.\n//  - eventually that process transfers control\n//    via swtch back to the scheduler.\nvoid\nscheduler(void)\n{\n  struct proc *p;\n  struct cpu *c = mycpu();\n\n  c->proc = 0;\n  for(;;){\n    // The most recent process to run may have had interrupts\n    // turned off; enable them to avoid a deadlock if all\n    // processes are waiting. Then turn them back off\n    // to avoid a possible race between an interrupt\n    // and wfi.\n    intr_on();\n    intr_off();\n\n    int found = 0;\n    for(p = proc; p < &proc[NPROC]; p++) {\n      acquire(&p->lock);\n      if(p->state == RUNNABLE) {\n        // Switch to chosen process.  It is the process's job\n        // to release its lock and then reacquire it\n        // before jumping back to us.\n        p->state = RUNNING;\n        c->proc = p;\n        swtch(&c->context, &p->context);\n\n        // Process is done running for now.\n        // It should have changed its p->state before coming back.\n        c->proc = 0;\n        found = 1;\n      }\n      release(&p->lock);\n    }\n    if(found == 0) {\n      // nothing to run; stop running on this core until an interrupt.\n      asm volatile(\"wfi\");\n    }\n  }\n}\n\n// Switch to scheduler.  Must hold only p->lock\n// and have changed proc->state. Saves and restores\n// intena because intena is a property of this\n// kernel thread, not this CPU. It should\n// be proc->intena and proc->noff, but that would\n// break in the few places where a lock is held but\n// there's no process.\nvoid\nsched(void)\n{\n  int intena;\n  struct proc *p = myproc();\n\n  if(!holding(&p->lock))\n    panic(\"sched p->lock\");\n  if(mycpu()->noff != 1)\n    panic(\"sched locks\");\n  if(p->state == RUNNING)\n    panic(\"sched RUNNING\");\n  if(intr_get())\n    panic(\"sched interruptible\");\n\n  intena = mycpu()->intena;\n  swtch(&p->context, &mycpu()->context);\n  mycpu()->intena = intena;\n}\n\n// Give up the CPU for one scheduling round.\nvoid\nyield(void)\n{\n  struct proc *p = myproc();\n  acquire(&p->lock);\n  p->state = RUNNABLE;\n  sched();\n  release(&p->lock);\n}\n\n// A fork child's very first scheduling by scheduler()\n// will swtch to forkret.\nvoid\nforkret(void)\n{\n  extern char userret[];\n  static int first = 1;\n  struct proc *p = myproc();\n\n  // Still holding p->lock from scheduler.\n  release(&p->lock);\n\n  if (first) {\n    // File system initialization must be run in the context of a\n    // regular process (e.g., because it calls sleep), and thus cannot\n    // be run from main().\n    fsinit(ROOTDEV);\n\n    first = 0;\n    // ensure other cores see first=0.\n    __sync_synchronize();\n\n    // We can invoke kexec() now that file system is initialized.\n    // Put the return value (argc) of kexec into a0.\n    p->trapframe->a0 = kexec(\"/init\", (char *[]){ \"/init\", 0 });\n    if (p->trapframe->a0 == -1) {\n      panic(\"exec\");\n    }\n  }\n\n  // return to user space, mimicing usertrap()'s return.\n  prepare_return();\n  uint64 satp = MAKE_SATP(p->pagetable);\n  uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);\n  ((void (*)(uint64))trampoline_userret)(satp);\n}\n\n// Sleep on channel chan, releasing condition lock lk.\n// Re-acquires lk when awakened.\nvoid\nsleep(void *chan, struct spinlock *lk)\n{\n  struct proc *p = myproc();\n  \n  // Must acquire p->lock in order to\n  // change p->state and then call sched.\n  // Once we hold p->lock, we can be\n  // guaranteed that we won't miss any wakeup\n  // (wakeup locks p->lock),\n  // so it's okay to release lk.\n\n  acquire(&p->lock);  //DOC: sleeplock1\n  release(lk);\n\n  // Go to sleep.\n  p->chan = chan;\n  p->state = SLEEPING;\n\n  sched();\n\n  // Tidy up.\n  p->chan = 0;\n\n  // Reacquire original lock.\n  release(&p->lock);\n  acquire(lk);\n}\n\n// Wake up all processes sleeping on channel chan.\n// Caller should hold the condition lock.\nvoid\nwakeup(void *chan)\n{\n  struct proc *p;\n\n  for(p = proc; p < &proc[NPROC]; p++) {\n    if(p != myproc()){\n      acquire(&p->lock);\n      if(p->state == SLEEPING && p->chan == chan) {\n        p->state = RUNNABLE;\n      }\n      release(&p->lock);\n    }\n  }\n}\n\n// Kill the process with the given pid.\n// The victim won't exit until it tries to return\n// to user space (see usertrap() in trap.c).\nint\nkkill(int pid)\n{\n  struct proc *p;\n\n  for(p = proc; p < &proc[NPROC]; p++){\n    acquire(&p->lock);\n    if(p->pid == pid){\n      p->killed = 1;\n      if(p->state == SLEEPING){\n        // Wake process from sleep().\n        p->state = RUNNABLE;\n      }\n      release(&p->lock);\n      return 0;\n    }\n    release(&p->lock);\n  }\n  return -1;\n}\n\nvoid\nsetkilled(struct proc *p)\n{\n  acquire(&p->lock);\n  p->killed = 1;\n  release(&p->lock);\n}\n\nint\nkilled(struct proc *p)\n{\n  int k;\n  \n  acquire(&p->lock);\n  k = p->killed;\n  release(&p->lock);\n  return k;\n}\n\n// Copy to either a user address, or kernel address,\n// depending on usr_dst.\n// Returns 0 on success, -1 on error.\nint\neither_copyout(int user_dst, uint64 dst, void *src, uint64 len)\n{\n  struct proc *p = myproc();\n  if(user_dst){\n    return copyout(p->pagetable, dst, src, len);\n  } else {\n    memmove((char *)dst, src, len);\n    return 0;\n  }\n}\n\n// Copy from either a user address, or kernel address,\n// depending on usr_src.\n// Returns 0 on success, -1 on error.\nint\neither_copyin(void *dst, int user_src, uint64 src, uint64 len)\n{\n  struct proc *p = myproc();\n  if(user_src){\n    return copyin(p->pagetable, dst, src, len);\n  } else {\n    memmove(dst, (char*)src, len);\n    return 0;\n  }\n}\n\n// Print a process listing to console.  For debugging.\n// Runs when user types ^P on console.\n// No lock to avoid wedging a stuck machine further.\nvoid\nprocdump(void)\n{\n  static char *states[] = {\n  [UNUSED]    \"unused\",\n  [USED]      \"used\",\n  [SLEEPING]  \"sleep \",\n  [RUNNABLE]  \"runble\",\n  [RUNNING]   \"run   \",\n  [ZOMBIE]    \"zombie\"\n  };\n  struct proc *p;\n  char *state;\n\n  printf(\"\\n\");\n  for(p = proc; p < &proc[NPROC]; p++){\n    if(p->state == UNUSED)\n      continue;\n    if(p->state >= 0 && p->state < NELEM(states) && states[p->state])\n      state = states[p->state];\n    else\n      state = \"???\";\n    printf(\"%d %s %s\", p->pid, state, p->name);\n    printf(\"\\n\");\n  }\n}\n"
  },
  {
    "path": "kernel/proc.h",
    "content": "// Saved registers for kernel context switches.\nstruct context {\n  uint64 ra;\n  uint64 sp;\n\n  // callee-saved\n  uint64 s0;\n  uint64 s1;\n  uint64 s2;\n  uint64 s3;\n  uint64 s4;\n  uint64 s5;\n  uint64 s6;\n  uint64 s7;\n  uint64 s8;\n  uint64 s9;\n  uint64 s10;\n  uint64 s11;\n};\n\n// Per-CPU state.\nstruct cpu {\n  struct proc *proc;          // The process running on this cpu, or null.\n  struct context context;     // swtch() here to enter scheduler().\n  int noff;                   // Depth of push_off() nesting.\n  int intena;                 // Were interrupts enabled before push_off()?\n};\n\nextern struct cpu cpus[NCPU];\n\n// per-process data for the trap handling code in trampoline.S.\n// sits in a page by itself just under the trampoline page in the\n// user page table. not specially mapped in the kernel page table.\n// uservec in trampoline.S saves user registers in the trapframe,\n// then initializes registers from the trapframe's\n// kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap.\n// usertrapret() and userret in trampoline.S set up\n// the trapframe's kernel_*, restore user registers from the\n// trapframe, switch to the user page table, and enter user space.\n// the trapframe includes callee-saved user registers like s0-s11 because the\n// return-to-user path via usertrapret() doesn't return through\n// the entire kernel call stack.\nstruct trapframe {\n  /*   0 */ uint64 kernel_satp;   // kernel page table\n  /*   8 */ uint64 kernel_sp;     // top of process's kernel stack\n  /*  16 */ uint64 kernel_trap;   // usertrap()\n  /*  24 */ uint64 epc;           // saved user program counter\n  /*  32 */ uint64 kernel_hartid; // saved kernel tp\n  /*  40 */ uint64 ra;\n  /*  48 */ uint64 sp;\n  /*  56 */ uint64 gp;\n  /*  64 */ uint64 tp;\n  /*  72 */ uint64 t0;\n  /*  80 */ uint64 t1;\n  /*  88 */ uint64 t2;\n  /*  96 */ uint64 s0;\n  /* 104 */ uint64 s1;\n  /* 112 */ uint64 a0;\n  /* 120 */ uint64 a1;\n  /* 128 */ uint64 a2;\n  /* 136 */ uint64 a3;\n  /* 144 */ uint64 a4;\n  /* 152 */ uint64 a5;\n  /* 160 */ uint64 a6;\n  /* 168 */ uint64 a7;\n  /* 176 */ uint64 s2;\n  /* 184 */ uint64 s3;\n  /* 192 */ uint64 s4;\n  /* 200 */ uint64 s5;\n  /* 208 */ uint64 s6;\n  /* 216 */ uint64 s7;\n  /* 224 */ uint64 s8;\n  /* 232 */ uint64 s9;\n  /* 240 */ uint64 s10;\n  /* 248 */ uint64 s11;\n  /* 256 */ uint64 t3;\n  /* 264 */ uint64 t4;\n  /* 272 */ uint64 t5;\n  /* 280 */ uint64 t6;\n};\n\nenum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };\n\n// Per-process state\nstruct proc {\n  struct spinlock lock;\n\n  // p->lock must be held when using these:\n  enum procstate state;        // Process state\n  void *chan;                  // If non-zero, sleeping on chan\n  int killed;                  // If non-zero, have been killed\n  int xstate;                  // Exit status to be returned to parent's wait\n  int pid;                     // Process ID\n\n  // wait_lock must be held when using this:\n  struct proc *parent;         // Parent process\n\n  // these are private to the process, so p->lock need not be held.\n  uint64 kstack;               // Virtual address of kernel stack\n  uint64 sz;                   // Size of process memory (bytes)\n  pagetable_t pagetable;       // User page table\n  struct trapframe *trapframe; // data page for trampoline.S\n  struct context context;      // swtch() here to run process\n  struct file *ofile[NOFILE];  // Open files\n  struct inode *cwd;           // Current directory\n  char name[16];               // Process name (debugging)\n};\n"
  },
  {
    "path": "kernel/riscv.h",
    "content": "#ifndef __ASSEMBLER__\n\n// which hart (core) is this?\nstatic inline uint64\nr_mhartid()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, mhartid\" : \"=r\" (x) );\n  return x;\n}\n\n// Machine Status Register, mstatus\n\n#define MSTATUS_MPP_MASK (3L << 11) // previous mode.\n#define MSTATUS_MPP_M (3L << 11)\n#define MSTATUS_MPP_S (1L << 11)\n#define MSTATUS_MPP_U (0L << 11)\n\nstatic inline uint64\nr_mstatus()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, mstatus\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_mstatus(uint64 x)\n{\n  asm volatile(\"csrw mstatus, %0\" : : \"r\" (x));\n}\n\n// machine exception program counter, holds the\n// instruction address to which a return from\n// exception will go.\nstatic inline void \nw_mepc(uint64 x)\n{\n  asm volatile(\"csrw mepc, %0\" : : \"r\" (x));\n}\n\n// Supervisor Status Register, sstatus\n\n#define SSTATUS_SPP (1L << 8)  // Previous mode, 1=Supervisor, 0=User\n#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable\n#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable\n#define SSTATUS_SIE (1L << 1)  // Supervisor Interrupt Enable\n#define SSTATUS_UIE (1L << 0)  // User Interrupt Enable\n\nstatic inline uint64\nr_sstatus()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, sstatus\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_sstatus(uint64 x)\n{\n  asm volatile(\"csrw sstatus, %0\" : : \"r\" (x));\n}\n\n// Supervisor Interrupt Pending\nstatic inline uint64\nr_sip()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, sip\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_sip(uint64 x)\n{\n  asm volatile(\"csrw sip, %0\" : : \"r\" (x));\n}\n\n// Supervisor Interrupt Enable\n#define SIE_SEIE (1L << 9) // external\n#define SIE_STIE (1L << 5) // timer\nstatic inline uint64\nr_sie()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, sie\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_sie(uint64 x)\n{\n  asm volatile(\"csrw sie, %0\" : : \"r\" (x));\n}\n\n// Machine-mode Interrupt Enable\n#define MIE_STIE (1L << 5)  // supervisor timer\nstatic inline uint64\nr_mie()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, mie\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_mie(uint64 x)\n{\n  asm volatile(\"csrw mie, %0\" : : \"r\" (x));\n}\n\n// supervisor exception program counter, holds the\n// instruction address to which a return from\n// exception will go.\nstatic inline void \nw_sepc(uint64 x)\n{\n  asm volatile(\"csrw sepc, %0\" : : \"r\" (x));\n}\n\nstatic inline uint64\nr_sepc()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, sepc\" : \"=r\" (x) );\n  return x;\n}\n\n// Machine Exception Delegation\nstatic inline uint64\nr_medeleg()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, medeleg\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_medeleg(uint64 x)\n{\n  asm volatile(\"csrw medeleg, %0\" : : \"r\" (x));\n}\n\n// Machine Interrupt Delegation\nstatic inline uint64\nr_mideleg()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, mideleg\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_mideleg(uint64 x)\n{\n  asm volatile(\"csrw mideleg, %0\" : : \"r\" (x));\n}\n\n// Supervisor Trap-Vector Base Address\n// low two bits are mode.\nstatic inline void \nw_stvec(uint64 x)\n{\n  asm volatile(\"csrw stvec, %0\" : : \"r\" (x));\n}\n\nstatic inline uint64\nr_stvec()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, stvec\" : \"=r\" (x) );\n  return x;\n}\n\n// Supervisor Timer Comparison Register\nstatic inline uint64\nr_stimecmp()\n{\n  uint64 x;\n  // asm volatile(\"csrr %0, stimecmp\" : \"=r\" (x) );\n  asm volatile(\"csrr %0, 0x14d\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_stimecmp(uint64 x)\n{\n  // asm volatile(\"csrw stimecmp, %0\" : : \"r\" (x));\n  asm volatile(\"csrw 0x14d, %0\" : : \"r\" (x));\n}\n\n// Machine Environment Configuration Register\nstatic inline uint64\nr_menvcfg()\n{\n  uint64 x;\n  // asm volatile(\"csrr %0, menvcfg\" : \"=r\" (x) );\n  asm volatile(\"csrr %0, 0x30a\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_menvcfg(uint64 x)\n{\n  // asm volatile(\"csrw menvcfg, %0\" : : \"r\" (x));\n  asm volatile(\"csrw 0x30a, %0\" : : \"r\" (x));\n}\n\n// Physical Memory Protection\nstatic inline void\nw_pmpcfg0(uint64 x)\n{\n  asm volatile(\"csrw pmpcfg0, %0\" : : \"r\" (x));\n}\n\nstatic inline void\nw_pmpaddr0(uint64 x)\n{\n  asm volatile(\"csrw pmpaddr0, %0\" : : \"r\" (x));\n}\n\n// use riscv's sv39 page table scheme.\n#define SATP_SV39 (8L << 60)\n\n#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12))\n\n// supervisor address translation and protection;\n// holds the address of the page table.\nstatic inline void \nw_satp(uint64 x)\n{\n  asm volatile(\"csrw satp, %0\" : : \"r\" (x));\n}\n\nstatic inline uint64\nr_satp()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, satp\" : \"=r\" (x) );\n  return x;\n}\n\n// Supervisor Trap Cause\nstatic inline uint64\nr_scause()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, scause\" : \"=r\" (x) );\n  return x;\n}\n\n// Supervisor Trap Value\nstatic inline uint64\nr_stval()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, stval\" : \"=r\" (x) );\n  return x;\n}\n\n// Machine-mode Counter-Enable\nstatic inline void \nw_mcounteren(uint64 x)\n{\n  asm volatile(\"csrw mcounteren, %0\" : : \"r\" (x));\n}\n\nstatic inline uint64\nr_mcounteren()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, mcounteren\" : \"=r\" (x) );\n  return x;\n}\n\n// machine-mode cycle counter\nstatic inline uint64\nr_time()\n{\n  uint64 x;\n  asm volatile(\"csrr %0, time\" : \"=r\" (x) );\n  return x;\n}\n\n// enable device interrupts\nstatic inline void\nintr_on()\n{\n  w_sstatus(r_sstatus() | SSTATUS_SIE);\n}\n\n// disable device interrupts\nstatic inline void\nintr_off()\n{\n  w_sstatus(r_sstatus() & ~SSTATUS_SIE);\n}\n\n// are device interrupts enabled?\nstatic inline int\nintr_get()\n{\n  uint64 x = r_sstatus();\n  return (x & SSTATUS_SIE) != 0;\n}\n\nstatic inline uint64\nr_sp()\n{\n  uint64 x;\n  asm volatile(\"mv %0, sp\" : \"=r\" (x) );\n  return x;\n}\n\n// read and write tp, the thread pointer, which xv6 uses to hold\n// this core's hartid (core number), the index into cpus[].\nstatic inline uint64\nr_tp()\n{\n  uint64 x;\n  asm volatile(\"mv %0, tp\" : \"=r\" (x) );\n  return x;\n}\n\nstatic inline void \nw_tp(uint64 x)\n{\n  asm volatile(\"mv tp, %0\" : : \"r\" (x));\n}\n\nstatic inline uint64\nr_ra()\n{\n  uint64 x;\n  asm volatile(\"mv %0, ra\" : \"=r\" (x) );\n  return x;\n}\n\n// flush the TLB.\nstatic inline void\nsfence_vma()\n{\n  // the zero, zero means flush all TLB entries.\n  asm volatile(\"sfence.vma zero, zero\");\n}\n\ntypedef uint64 pte_t;\ntypedef uint64 *pagetable_t; // 512 PTEs\n\n#endif // __ASSEMBLER__\n\n#define PGSIZE 4096 // bytes per page\n#define PGSHIFT 12  // bits of offset within a page\n\n#define PGROUNDUP(sz)  (((sz)+PGSIZE-1) & ~(PGSIZE-1))\n#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))\n\n#define PTE_V (1L << 0) // valid\n#define PTE_R (1L << 1)\n#define PTE_W (1L << 2)\n#define PTE_X (1L << 3)\n#define PTE_U (1L << 4) // user can access\n\n// shift a physical address to the right place for a PTE.\n#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)\n\n#define PTE2PA(pte) (((pte) >> 10) << 12)\n\n#define PTE_FLAGS(pte) ((pte) & 0x3FF)\n\n// extract the three 9-bit page table indices from a virtual address.\n#define PXMASK          0x1FF // 9 bits\n#define PXSHIFT(level)  (PGSHIFT+(9*(level)))\n#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)\n\n// one beyond the highest possible virtual address.\n// MAXVA is actually one bit less than the max allowed by\n// Sv39, to avoid having to sign-extend virtual addresses\n// that have the high bit set.\n#define MAXVA (1L << (9 + 9 + 9 + 12 - 1))\n"
  },
  {
    "path": "kernel/sleeplock.c",
    "content": "// Sleeping locks\n\n#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"sleeplock.h\"\n\nvoid\ninitsleeplock(struct sleeplock *lk, char *name)\n{\n  initlock(&lk->lk, \"sleep lock\");\n  lk->name = name;\n  lk->locked = 0;\n  lk->pid = 0;\n}\n\nvoid\nacquiresleep(struct sleeplock *lk)\n{\n  acquire(&lk->lk);\n  while (lk->locked) {\n    sleep(lk, &lk->lk);\n  }\n  lk->locked = 1;\n  lk->pid = myproc()->pid;\n  release(&lk->lk);\n}\n\nvoid\nreleasesleep(struct sleeplock *lk)\n{\n  acquire(&lk->lk);\n  lk->locked = 0;\n  lk->pid = 0;\n  wakeup(lk);\n  release(&lk->lk);\n}\n\nint\nholdingsleep(struct sleeplock *lk)\n{\n  int r;\n  \n  acquire(&lk->lk);\n  r = lk->locked && (lk->pid == myproc()->pid);\n  release(&lk->lk);\n  return r;\n}\n\n\n\n"
  },
  {
    "path": "kernel/sleeplock.h",
    "content": "// Long-term locks for processes\nstruct sleeplock {\n  uint locked;       // Is the lock held?\n  struct spinlock lk; // spinlock protecting this sleep lock\n  \n  // For debugging:\n  char *name;        // Name of lock.\n  int pid;           // Process holding lock\n};\n\n"
  },
  {
    "path": "kernel/spinlock.c",
    "content": "// Mutual exclusion spin locks.\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"spinlock.h\"\n#include \"riscv.h\"\n#include \"proc.h\"\n#include \"defs.h\"\n\nvoid\ninitlock(struct spinlock *lk, char *name)\n{\n  lk->name = name;\n  lk->locked = 0;\n  lk->cpu = 0;\n}\n\n// Acquire the lock.\n// Loops (spins) until the lock is acquired.\nvoid\nacquire(struct spinlock *lk)\n{\n  push_off(); // disable interrupts to avoid deadlock.\n  if(holding(lk))\n    panic(\"acquire\");\n\n  // On RISC-V, sync_lock_test_and_set turns into an atomic swap:\n  //   a5 = 1\n  //   s1 = &lk->locked\n  //   amoswap.w.aq a5, a5, (s1)\n  while(__sync_lock_test_and_set(&lk->locked, 1) != 0)\n    ;\n\n  // Tell the C compiler and the processor to not move loads or stores\n  // past this point, to ensure that the critical section's memory\n  // references happen strictly after the lock is acquired.\n  // On RISC-V, this emits a fence instruction.\n  __sync_synchronize();\n\n  // Record info about lock acquisition for holding() and debugging.\n  lk->cpu = mycpu();\n}\n\n// Release the lock.\nvoid\nrelease(struct spinlock *lk)\n{\n  if(!holding(lk))\n    panic(\"release\");\n\n  lk->cpu = 0;\n\n  // Tell the C compiler and the CPU to not move loads or stores\n  // past this point, to ensure that all the stores in the critical\n  // section are visible to other CPUs before the lock is released,\n  // and that loads in the critical section occur strictly before\n  // the lock is released.\n  // On RISC-V, this emits a fence instruction.\n  __sync_synchronize();\n\n  // Release the lock, equivalent to lk->locked = 0.\n  // This code doesn't use a C assignment, since the C standard\n  // implies that an assignment might be implemented with\n  // multiple store instructions.\n  // On RISC-V, sync_lock_release turns into an atomic swap:\n  //   s1 = &lk->locked\n  //   amoswap.w zero, zero, (s1)\n  __sync_lock_release(&lk->locked);\n\n  pop_off();\n}\n\n// Check whether this cpu is holding the lock.\n// Interrupts must be off.\nint\nholding(struct spinlock *lk)\n{\n  int r;\n  r = (lk->locked && lk->cpu == mycpu());\n  return r;\n}\n\n// push_off/pop_off are like intr_off()/intr_on() except that they are matched:\n// it takes two pop_off()s to undo two push_off()s.  Also, if interrupts\n// are initially off, then push_off, pop_off leaves them off.\n\nvoid\npush_off(void)\n{\n  int old = intr_get();\n\n  // disable interrupts to prevent an involuntary context\n  // switch while using mycpu().\n  intr_off();\n\n  if(mycpu()->noff == 0)\n    mycpu()->intena = old;\n  mycpu()->noff += 1;\n}\n\nvoid\npop_off(void)\n{\n  struct cpu *c = mycpu();\n  if(intr_get())\n    panic(\"pop_off - interruptible\");\n  if(c->noff < 1)\n    panic(\"pop_off\");\n  c->noff -= 1;\n  if(c->noff == 0 && c->intena)\n    intr_on();\n}\n"
  },
  {
    "path": "kernel/spinlock.h",
    "content": "// Mutual exclusion lock.\nstruct spinlock {\n  uint locked;       // Is the lock held?\n\n  // For debugging:\n  char *name;        // Name of lock.\n  struct cpu *cpu;   // The cpu holding the lock.\n};\n\n"
  },
  {
    "path": "kernel/start.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n\nvoid main();\nvoid timerinit();\n\n// entry.S needs one stack per CPU.\n__attribute__ ((aligned (16))) char stack0[4096 * NCPU];\n\n// entry.S jumps here in machine mode on stack0.\nvoid\nstart()\n{\n  // set M Previous Privilege mode to Supervisor, for mret.\n  unsigned long x = r_mstatus();\n  x &= ~MSTATUS_MPP_MASK;\n  x |= MSTATUS_MPP_S;\n  w_mstatus(x);\n\n  // set M Exception Program Counter to main, for mret.\n  // requires gcc -mcmodel=medany\n  w_mepc((uint64)main);\n\n  // disable paging for now.\n  w_satp(0);\n\n  // delegate all interrupts and exceptions to supervisor mode.\n  w_medeleg(0xffff);\n  w_mideleg(0xffff);\n  w_sie(r_sie() | SIE_SEIE | SIE_STIE);\n\n  // configure Physical Memory Protection to give supervisor mode\n  // access to all of physical memory.\n  w_pmpaddr0(0x3fffffffffffffull);\n  w_pmpcfg0(0xf);\n\n  // ask for clock interrupts.\n  timerinit();\n\n  // keep each CPU's hartid in its tp register, for cpuid().\n  int id = r_mhartid();\n  w_tp(id);\n\n  // switch to supervisor mode and jump to main().\n  asm volatile(\"mret\");\n}\n\n// ask each hart to generate timer interrupts.\nvoid\ntimerinit()\n{\n  // enable supervisor-mode timer interrupts.\n  w_mie(r_mie() | MIE_STIE);\n  \n  // enable the sstc extension (i.e. stimecmp).\n  w_menvcfg(r_menvcfg() | (1L << 63)); \n  \n  // allow supervisor to use stimecmp and time.\n  w_mcounteren(r_mcounteren() | 2);\n  \n  // ask for the very first timer interrupt.\n  w_stimecmp(r_time() + 1000000);\n}\n"
  },
  {
    "path": "kernel/stat.h",
    "content": "#define T_DIR     1   // Directory\n#define T_FILE    2   // File\n#define T_DEVICE  3   // Device\n\nstruct stat {\n  int dev;     // File system's disk device\n  uint ino;    // Inode number\n  short type;  // Type of file\n  short nlink; // Number of links to file\n  uint64 size; // Size of file in bytes\n};\n"
  },
  {
    "path": "kernel/string.c",
    "content": "#include \"types.h\"\n\nvoid*\nmemset(void *dst, int c, uint n)\n{\n  char *cdst = (char *) dst;\n  int i;\n  for(i = 0; i < n; i++){\n    cdst[i] = c;\n  }\n  return dst;\n}\n\nint\nmemcmp(const void *v1, const void *v2, uint n)\n{\n  const uchar *s1, *s2;\n\n  s1 = v1;\n  s2 = v2;\n  while(n-- > 0){\n    if(*s1 != *s2)\n      return *s1 - *s2;\n    s1++, s2++;\n  }\n\n  return 0;\n}\n\nvoid*\nmemmove(void *dst, const void *src, uint n)\n{\n  const char *s;\n  char *d;\n\n  if(n == 0)\n    return dst;\n  \n  s = src;\n  d = dst;\n  if(s < d && s + n > d){\n    s += n;\n    d += n;\n    while(n-- > 0)\n      *--d = *--s;\n  } else\n    while(n-- > 0)\n      *d++ = *s++;\n\n  return dst;\n}\n\n// memcpy exists to placate GCC.  Use memmove.\nvoid*\nmemcpy(void *dst, const void *src, uint n)\n{\n  return memmove(dst, src, n);\n}\n\nint\nstrncmp(const char *p, const char *q, uint n)\n{\n  while(n > 0 && *p && *p == *q)\n    n--, p++, q++;\n  if(n == 0)\n    return 0;\n  return (uchar)*p - (uchar)*q;\n}\n\nchar*\nstrncpy(char *s, const char *t, int n)\n{\n  char *os;\n\n  os = s;\n  while(n-- > 0 && (*s++ = *t++) != 0)\n    ;\n  while(n-- > 0)\n    *s++ = 0;\n  return os;\n}\n\n// Like strncpy but guaranteed to NUL-terminate.\nchar*\nsafestrcpy(char *s, const char *t, int n)\n{\n  char *os;\n\n  os = s;\n  if(n <= 0)\n    return os;\n  while(--n > 0 && (*s++ = *t++) != 0)\n    ;\n  *s = 0;\n  return os;\n}\n\nint\nstrlen(const char *s)\n{\n  int n;\n\n  for(n = 0; s[n]; n++)\n    ;\n  return n;\n}\n\n"
  },
  {
    "path": "kernel/swtch.S",
    "content": "# Context switch\n#\n#   void swtch(struct context *old, struct context *new);\n# \n# Save current registers in old. Load from new.\t\n\n\n.globl swtch\nswtch:\n        sd ra, 0(a0)\n        sd sp, 8(a0)\n        sd s0, 16(a0)\n        sd s1, 24(a0)\n        sd s2, 32(a0)\n        sd s3, 40(a0)\n        sd s4, 48(a0)\n        sd s5, 56(a0)\n        sd s6, 64(a0)\n        sd s7, 72(a0)\n        sd s8, 80(a0)\n        sd s9, 88(a0)\n        sd s10, 96(a0)\n        sd s11, 104(a0)\n\n        ld ra, 0(a1)\n        ld sp, 8(a1)\n        ld s0, 16(a1)\n        ld s1, 24(a1)\n        ld s2, 32(a1)\n        ld s3, 40(a1)\n        ld s4, 48(a1)\n        ld s5, 56(a1)\n        ld s6, 64(a1)\n        ld s7, 72(a1)\n        ld s8, 80(a1)\n        ld s9, 88(a1)\n        ld s10, 96(a1)\n        ld s11, 104(a1)\n        \n        ret\n\n\t\n"
  },
  {
    "path": "kernel/syscall.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"syscall.h\"\n#include \"defs.h\"\n\n// Fetch the uint64 at addr from the current process.\nint\nfetchaddr(uint64 addr, uint64 *ip)\n{\n  struct proc *p = myproc();\n  if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow\n    return -1;\n  if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)\n    return -1;\n  return 0;\n}\n\n// Fetch the nul-terminated string at addr from the current process.\n// Returns length of string, not including nul, or -1 for error.\nint\nfetchstr(uint64 addr, char *buf, int max)\n{\n  struct proc *p = myproc();\n  if(copyinstr(p->pagetable, buf, addr, max) < 0)\n    return -1;\n  return strlen(buf);\n}\n\nstatic uint64\nargraw(int n)\n{\n  struct proc *p = myproc();\n  switch (n) {\n  case 0:\n    return p->trapframe->a0;\n  case 1:\n    return p->trapframe->a1;\n  case 2:\n    return p->trapframe->a2;\n  case 3:\n    return p->trapframe->a3;\n  case 4:\n    return p->trapframe->a4;\n  case 5:\n    return p->trapframe->a5;\n  }\n  panic(\"argraw\");\n  return -1;\n}\n\n// Fetch the nth 32-bit system call argument.\nvoid\nargint(int n, int *ip)\n{\n  *ip = argraw(n);\n}\n\n// Retrieve an argument as a pointer.\n// Doesn't check for legality, since\n// copyin/copyout will do that.\nvoid\nargaddr(int n, uint64 *ip)\n{\n  *ip = argraw(n);\n}\n\n// Fetch the nth word-sized system call argument as a null-terminated string.\n// Copies into buf, at most max.\n// Returns string length if OK (including nul), -1 if error.\nint\nargstr(int n, char *buf, int max)\n{\n  uint64 addr;\n  argaddr(n, &addr);\n  return fetchstr(addr, buf, max);\n}\n\n// Prototypes for the functions that handle system calls.\nextern uint64 sys_fork(void);\nextern uint64 sys_exit(void);\nextern uint64 sys_wait(void);\nextern uint64 sys_pipe(void);\nextern uint64 sys_read(void);\nextern uint64 sys_kill(void);\nextern uint64 sys_exec(void);\nextern uint64 sys_fstat(void);\nextern uint64 sys_chdir(void);\nextern uint64 sys_dup(void);\nextern uint64 sys_getpid(void);\nextern uint64 sys_sbrk(void);\nextern uint64 sys_pause(void);\nextern uint64 sys_uptime(void);\nextern uint64 sys_open(void);\nextern uint64 sys_write(void);\nextern uint64 sys_mknod(void);\nextern uint64 sys_unlink(void);\nextern uint64 sys_link(void);\nextern uint64 sys_mkdir(void);\nextern uint64 sys_close(void);\n\n// An array mapping syscall numbers from syscall.h\n// to the function that handles the system call.\nstatic uint64 (*syscalls[])(void) = {\n[SYS_fork]    sys_fork,\n[SYS_exit]    sys_exit,\n[SYS_wait]    sys_wait,\n[SYS_pipe]    sys_pipe,\n[SYS_read]    sys_read,\n[SYS_kill]    sys_kill,\n[SYS_exec]    sys_exec,\n[SYS_fstat]   sys_fstat,\n[SYS_chdir]   sys_chdir,\n[SYS_dup]     sys_dup,\n[SYS_getpid]  sys_getpid,\n[SYS_sbrk]    sys_sbrk,\n[SYS_pause]   sys_pause,\n[SYS_uptime]  sys_uptime,\n[SYS_open]    sys_open,\n[SYS_write]   sys_write,\n[SYS_mknod]   sys_mknod,\n[SYS_unlink]  sys_unlink,\n[SYS_link]    sys_link,\n[SYS_mkdir]   sys_mkdir,\n[SYS_close]   sys_close,\n};\n\nvoid\nsyscall(void)\n{\n  int num;\n  struct proc *p = myproc();\n\n  num = p->trapframe->a7;\n  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {\n    // Use num to lookup the system call function for num, call it,\n    // and store its return value in p->trapframe->a0\n    p->trapframe->a0 = syscalls[num]();\n  } else {\n    printf(\"%d %s: unknown sys call %d\\n\",\n            p->pid, p->name, num);\n    p->trapframe->a0 = -1;\n  }\n}\n"
  },
  {
    "path": "kernel/syscall.h",
    "content": "// System call numbers\n#define SYS_fork    1\n#define SYS_exit    2\n#define SYS_wait    3\n#define SYS_pipe    4\n#define SYS_read    5\n#define SYS_kill    6\n#define SYS_exec    7\n#define SYS_fstat   8\n#define SYS_chdir   9\n#define SYS_dup    10\n#define SYS_getpid 11\n#define SYS_sbrk   12\n#define SYS_pause  13\n#define SYS_uptime 14\n#define SYS_open   15\n#define SYS_write  16\n#define SYS_mknod  17\n#define SYS_unlink 18\n#define SYS_link   19\n#define SYS_mkdir  20\n#define SYS_close  21\n"
  },
  {
    "path": "kernel/sysfile.c",
    "content": "//\n// File-system system calls.\n// Mostly argument checking, since we don't trust\n// user code, and calls into file.c and fs.c.\n//\n\n#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"stat.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"fs.h\"\n#include \"sleeplock.h\"\n#include \"file.h\"\n#include \"fcntl.h\"\n\n// Fetch the nth word-sized system call argument as a file descriptor\n// and return both the descriptor and the corresponding struct file.\nstatic int\nargfd(int n, int *pfd, struct file **pf)\n{\n  int fd;\n  struct file *f;\n\n  argint(n, &fd);\n  if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0)\n    return -1;\n  if(pfd)\n    *pfd = fd;\n  if(pf)\n    *pf = f;\n  return 0;\n}\n\n// Allocate a file descriptor for the given file.\n// Takes over file reference from caller on success.\nstatic int\nfdalloc(struct file *f)\n{\n  int fd;\n  struct proc *p = myproc();\n\n  for(fd = 0; fd < NOFILE; fd++){\n    if(p->ofile[fd] == 0){\n      p->ofile[fd] = f;\n      return fd;\n    }\n  }\n  return -1;\n}\n\nuint64\nsys_dup(void)\n{\n  struct file *f;\n  int fd;\n\n  if(argfd(0, 0, &f) < 0)\n    return -1;\n  if((fd=fdalloc(f)) < 0)\n    return -1;\n  filedup(f);\n  return fd;\n}\n\nuint64\nsys_read(void)\n{\n  struct file *f;\n  int n;\n  uint64 p;\n\n  argaddr(1, &p);\n  argint(2, &n);\n  if(argfd(0, 0, &f) < 0)\n    return -1;\n  return fileread(f, p, n);\n}\n\nuint64\nsys_write(void)\n{\n  struct file *f;\n  int n;\n  uint64 p;\n  \n  argaddr(1, &p);\n  argint(2, &n);\n  if(argfd(0, 0, &f) < 0)\n    return -1;\n\n  return filewrite(f, p, n);\n}\n\nuint64\nsys_close(void)\n{\n  int fd;\n  struct file *f;\n\n  if(argfd(0, &fd, &f) < 0)\n    return -1;\n  myproc()->ofile[fd] = 0;\n  fileclose(f);\n  return 0;\n}\n\nuint64\nsys_fstat(void)\n{\n  struct file *f;\n  uint64 st; // user pointer to struct stat\n\n  argaddr(1, &st);\n  if(argfd(0, 0, &f) < 0)\n    return -1;\n  return filestat(f, st);\n}\n\n// Create the path new as a link to the same inode as old.\nuint64\nsys_link(void)\n{\n  char name[DIRSIZ], new[MAXPATH], old[MAXPATH];\n  struct inode *dp, *ip;\n\n  if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)\n    return -1;\n\n  begin_op();\n  if((ip = namei(old)) == 0){\n    end_op();\n    return -1;\n  }\n\n  ilock(ip);\n  if(ip->type == T_DIR){\n    iunlockput(ip);\n    end_op();\n    return -1;\n  }\n\n  ip->nlink++;\n  iupdate(ip);\n  iunlock(ip);\n\n  if((dp = nameiparent(new, name)) == 0)\n    goto bad;\n  ilock(dp);\n  if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){\n    iunlockput(dp);\n    goto bad;\n  }\n  iunlockput(dp);\n  iput(ip);\n\n  end_op();\n\n  return 0;\n\nbad:\n  ilock(ip);\n  ip->nlink--;\n  iupdate(ip);\n  iunlockput(ip);\n  end_op();\n  return -1;\n}\n\n// Is the directory dp empty except for \".\" and \"..\" ?\nstatic int\nisdirempty(struct inode *dp)\n{\n  int off;\n  struct dirent de;\n\n  for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){\n    if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))\n      panic(\"isdirempty: readi\");\n    if(de.inum != 0)\n      return 0;\n  }\n  return 1;\n}\n\nuint64\nsys_unlink(void)\n{\n  struct inode *ip, *dp;\n  struct dirent de;\n  char name[DIRSIZ], path[MAXPATH];\n  uint off;\n\n  if(argstr(0, path, MAXPATH) < 0)\n    return -1;\n\n  begin_op();\n  if((dp = nameiparent(path, name)) == 0){\n    end_op();\n    return -1;\n  }\n\n  ilock(dp);\n\n  // Cannot unlink \".\" or \"..\".\n  if(namecmp(name, \".\") == 0 || namecmp(name, \"..\") == 0)\n    goto bad;\n\n  if((ip = dirlookup(dp, name, &off)) == 0)\n    goto bad;\n  ilock(ip);\n\n  if(ip->nlink < 1)\n    panic(\"unlink: nlink < 1\");\n  if(ip->type == T_DIR && !isdirempty(ip)){\n    iunlockput(ip);\n    goto bad;\n  }\n\n  memset(&de, 0, sizeof(de));\n  if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))\n    panic(\"unlink: writei\");\n  if(ip->type == T_DIR){\n    dp->nlink--;\n    iupdate(dp);\n  }\n  iunlockput(dp);\n\n  ip->nlink--;\n  iupdate(ip);\n  iunlockput(ip);\n\n  end_op();\n\n  return 0;\n\nbad:\n  iunlockput(dp);\n  end_op();\n  return -1;\n}\n\nstatic struct inode*\ncreate(char *path, short type, short major, short minor)\n{\n  struct inode *ip, *dp;\n  char name[DIRSIZ];\n\n  if((dp = nameiparent(path, name)) == 0)\n    return 0;\n\n  ilock(dp);\n\n  if((ip = dirlookup(dp, name, 0)) != 0){\n    iunlockput(dp);\n    ilock(ip);\n    if(type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE))\n      return ip;\n    iunlockput(ip);\n    return 0;\n  }\n\n  if((ip = ialloc(dp->dev, type)) == 0){\n    iunlockput(dp);\n    return 0;\n  }\n\n  ilock(ip);\n  ip->major = major;\n  ip->minor = minor;\n  ip->nlink = 1;\n  iupdate(ip);\n\n  if(type == T_DIR){  // Create . and .. entries.\n    // No ip->nlink++ for \".\": avoid cyclic ref count.\n    if(dirlink(ip, \".\", ip->inum) < 0 || dirlink(ip, \"..\", dp->inum) < 0)\n      goto fail;\n  }\n\n  if(dirlink(dp, name, ip->inum) < 0)\n    goto fail;\n\n  if(type == T_DIR){\n    // now that success is guaranteed:\n    dp->nlink++;  // for \"..\"\n    iupdate(dp);\n  }\n\n  iunlockput(dp);\n\n  return ip;\n\n fail:\n  // something went wrong. de-allocate ip.\n  ip->nlink = 0;\n  iupdate(ip);\n  iunlockput(ip);\n  iunlockput(dp);\n  return 0;\n}\n\nuint64\nsys_open(void)\n{\n  char path[MAXPATH];\n  int fd, omode;\n  struct file *f;\n  struct inode *ip;\n  int n;\n\n  argint(1, &omode);\n  if((n = argstr(0, path, MAXPATH)) < 0)\n    return -1;\n\n  begin_op();\n\n  if(omode & O_CREATE){\n    ip = create(path, T_FILE, 0, 0);\n    if(ip == 0){\n      end_op();\n      return -1;\n    }\n  } else {\n    if((ip = namei(path)) == 0){\n      end_op();\n      return -1;\n    }\n    ilock(ip);\n    if(ip->type == T_DIR && omode != O_RDONLY){\n      iunlockput(ip);\n      end_op();\n      return -1;\n    }\n  }\n\n  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){\n    iunlockput(ip);\n    end_op();\n    return -1;\n  }\n\n  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){\n    if(f)\n      fileclose(f);\n    iunlockput(ip);\n    end_op();\n    return -1;\n  }\n\n  if(ip->type == T_DEVICE){\n    f->type = FD_DEVICE;\n    f->major = ip->major;\n  } else {\n    f->type = FD_INODE;\n    f->off = 0;\n  }\n  f->ip = ip;\n  f->readable = !(omode & O_WRONLY);\n  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);\n\n  if((omode & O_TRUNC) && ip->type == T_FILE){\n    itrunc(ip);\n  }\n\n  iunlock(ip);\n  end_op();\n\n  return fd;\n}\n\nuint64\nsys_mkdir(void)\n{\n  char path[MAXPATH];\n  struct inode *ip;\n\n  begin_op();\n  if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){\n    end_op();\n    return -1;\n  }\n  iunlockput(ip);\n  end_op();\n  return 0;\n}\n\nuint64\nsys_mknod(void)\n{\n  struct inode *ip;\n  char path[MAXPATH];\n  int major, minor;\n\n  begin_op();\n  argint(1, &major);\n  argint(2, &minor);\n  if((argstr(0, path, MAXPATH)) < 0 ||\n     (ip = create(path, T_DEVICE, major, minor)) == 0){\n    end_op();\n    return -1;\n  }\n  iunlockput(ip);\n  end_op();\n  return 0;\n}\n\nuint64\nsys_chdir(void)\n{\n  char path[MAXPATH];\n  struct inode *ip;\n  struct proc *p = myproc();\n  \n  begin_op();\n  if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){\n    end_op();\n    return -1;\n  }\n  ilock(ip);\n  if(ip->type != T_DIR){\n    iunlockput(ip);\n    end_op();\n    return -1;\n  }\n  iunlock(ip);\n  iput(p->cwd);\n  end_op();\n  p->cwd = ip;\n  return 0;\n}\n\nuint64\nsys_exec(void)\n{\n  char path[MAXPATH], *argv[MAXARG];\n  int i;\n  uint64 uargv, uarg;\n\n  argaddr(1, &uargv);\n  if(argstr(0, path, MAXPATH) < 0) {\n    return -1;\n  }\n  memset(argv, 0, sizeof(argv));\n  for(i=0;; i++){\n    if(i >= NELEM(argv)){\n      goto bad;\n    }\n    if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){\n      goto bad;\n    }\n    if(uarg == 0){\n      argv[i] = 0;\n      break;\n    }\n    argv[i] = kalloc();\n    if(argv[i] == 0)\n      goto bad;\n    if(fetchstr(uarg, argv[i], PGSIZE) < 0)\n      goto bad;\n  }\n\n  int ret = kexec(path, argv);\n\n  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)\n    kfree(argv[i]);\n\n  return ret;\n\n bad:\n  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)\n    kfree(argv[i]);\n  return -1;\n}\n\nuint64\nsys_pipe(void)\n{\n  uint64 fdarray; // user pointer to array of two integers\n  struct file *rf, *wf;\n  int fd0, fd1;\n  struct proc *p = myproc();\n\n  argaddr(0, &fdarray);\n  if(pipealloc(&rf, &wf) < 0)\n    return -1;\n  fd0 = -1;\n  if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){\n    if(fd0 >= 0)\n      p->ofile[fd0] = 0;\n    fileclose(rf);\n    fileclose(wf);\n    return -1;\n  }\n  if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||\n     copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){\n    p->ofile[fd0] = 0;\n    p->ofile[fd1] = 0;\n    fileclose(rf);\n    fileclose(wf);\n    return -1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "kernel/sysproc.c",
    "content": "#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"vm.h\"\n\nuint64\nsys_exit(void)\n{\n  int n;\n  argint(0, &n);\n  kexit(n);\n  return 0;  // not reached\n}\n\nuint64\nsys_getpid(void)\n{\n  return myproc()->pid;\n}\n\nuint64\nsys_fork(void)\n{\n  return kfork();\n}\n\nuint64\nsys_wait(void)\n{\n  uint64 p;\n  argaddr(0, &p);\n  return kwait(p);\n}\n\nuint64\nsys_sbrk(void)\n{\n  uint64 addr;\n  int t;\n  int n;\n\n  argint(0, &n);\n  argint(1, &t);\n  addr = myproc()->sz;\n\n  if(t == SBRK_EAGER || n < 0) {\n    if(growproc(n) < 0) {\n      return -1;\n    }\n  } else {\n    // Lazily allocate memory for this process: increase its memory\n    // size but don't allocate memory. If the processes uses the\n    // memory, vmfault() will allocate it.\n    if(addr + n < addr)\n      return -1;\n    if(addr + n > TRAPFRAME)\n      return -1;\n    myproc()->sz += n;\n  }\n  return addr;\n}\n\nuint64\nsys_pause(void)\n{\n  int n;\n  uint ticks0;\n\n  argint(0, &n);\n  if(n < 0)\n    n = 0;\n  acquire(&tickslock);\n  ticks0 = ticks;\n  while(ticks - ticks0 < n){\n    if(killed(myproc())){\n      release(&tickslock);\n      return -1;\n    }\n    sleep(&ticks, &tickslock);\n  }\n  release(&tickslock);\n  return 0;\n}\n\nuint64\nsys_kill(void)\n{\n  int pid;\n\n  argint(0, &pid);\n  return kkill(pid);\n}\n\n// return how many clock tick interrupts have occurred\n// since start.\nuint64\nsys_uptime(void)\n{\n  uint xticks;\n\n  acquire(&tickslock);\n  xticks = ticks;\n  release(&tickslock);\n  return xticks;\n}\n"
  },
  {
    "path": "kernel/trampoline.S",
    "content": "        #\n        # low-level code to handle traps from user space into\n        # the kernel, and returns from kernel to user.\n        #\n        # the kernel maps the page holding this code\n        # at the same virtual address (TRAMPOLINE)\n        # in user and kernel space so that it continues\n        # to work when it switches page tables.\n        # kernel.ld causes this code to start at \n        # a page boundary.\n        #\n\n#include \"riscv.h\"\n#include \"memlayout.h\"\n\n.section trampsec\n.globl trampoline\n.globl usertrap\ntrampoline:\n.align 4\n.globl uservec\nuservec:    \n\t#\n        # trap.c sets stvec to point here, so\n        # traps from user space start here,\n        # in supervisor mode, but with a\n        # user page table.\n        #\n\n        # save user a0 in sscratch so\n        # a0 can be used to get at TRAPFRAME.\n        csrw sscratch, a0\n\n        # each process has a separate p->trapframe memory area,\n        # but it's mapped to the same virtual address\n        # (TRAPFRAME) in every process's user page table.\n        li a0, TRAPFRAME\n        \n        # save the user registers in TRAPFRAME\n        sd ra, 40(a0)\n        sd sp, 48(a0)\n        sd gp, 56(a0)\n        sd tp, 64(a0)\n        sd t0, 72(a0)\n        sd t1, 80(a0)\n        sd t2, 88(a0)\n        sd s0, 96(a0)\n        sd s1, 104(a0)\n        sd a1, 120(a0)\n        sd a2, 128(a0)\n        sd a3, 136(a0)\n        sd a4, 144(a0)\n        sd a5, 152(a0)\n        sd a6, 160(a0)\n        sd a7, 168(a0)\n        sd s2, 176(a0)\n        sd s3, 184(a0)\n        sd s4, 192(a0)\n        sd s5, 200(a0)\n        sd s6, 208(a0)\n        sd s7, 216(a0)\n        sd s8, 224(a0)\n        sd s9, 232(a0)\n        sd s10, 240(a0)\n        sd s11, 248(a0)\n        sd t3, 256(a0)\n        sd t4, 264(a0)\n        sd t5, 272(a0)\n        sd t6, 280(a0)\n\n\t# save the user a0 in p->trapframe->a0\n        csrr t0, sscratch\n        sd t0, 112(a0)\n\n        # initialize kernel stack pointer, from p->trapframe->kernel_sp\n        ld sp, 8(a0)\n\n        # make tp hold the current hartid, from p->trapframe->kernel_hartid\n        ld tp, 32(a0)\n\n        # load the address of usertrap(), from p->trapframe->kernel_trap\n        ld t0, 16(a0)\n\n        # fetch the kernel page table address, from p->trapframe->kernel_satp.\n        ld t1, 0(a0)\n\n        # wait for any previous memory operations to complete, so that\n        # they use the user page table.\n        sfence.vma zero, zero\n\n        # install the kernel page table.\n        csrw satp, t1\n\n        # flush now-stale user entries from the TLB.\n        sfence.vma zero, zero\n\n        # call usertrap()\n        jalr t0\n\n.globl userret\nuserret:\n        # usertrap() returns here, with user satp in a0.\n        # return from kernel to user.\n\n        # switch to the user page table.\n        sfence.vma zero, zero\n        csrw satp, a0\n        sfence.vma zero, zero\n\n        li a0, TRAPFRAME\n\n        # restore all but a0 from TRAPFRAME\n        ld ra, 40(a0)\n        ld sp, 48(a0)\n        ld gp, 56(a0)\n        ld tp, 64(a0)\n        ld t0, 72(a0)\n        ld t1, 80(a0)\n        ld t2, 88(a0)\n        ld s0, 96(a0)\n        ld s1, 104(a0)\n        ld a1, 120(a0)\n        ld a2, 128(a0)\n        ld a3, 136(a0)\n        ld a4, 144(a0)\n        ld a5, 152(a0)\n        ld a6, 160(a0)\n        ld a7, 168(a0)\n        ld s2, 176(a0)\n        ld s3, 184(a0)\n        ld s4, 192(a0)\n        ld s5, 200(a0)\n        ld s6, 208(a0)\n        ld s7, 216(a0)\n        ld s8, 224(a0)\n        ld s9, 232(a0)\n        ld s10, 240(a0)\n        ld s11, 248(a0)\n        ld t3, 256(a0)\n        ld t4, 264(a0)\n        ld t5, 272(a0)\n        ld t6, 280(a0)\n\n\t# restore user a0\n        ld a0, 112(a0)\n        \n        # return to user mode and user pc.\n        # usertrapret() set up sstatus and sepc.\n        sret\n"
  },
  {
    "path": "kernel/trap.c",
    "content": "#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"defs.h\"\n\nstruct spinlock tickslock;\nuint ticks;\n\nextern char trampoline[], uservec[];\n\n// in kernelvec.S, calls kerneltrap().\nvoid kernelvec();\n\nextern int devintr();\n\nvoid\ntrapinit(void)\n{\n  initlock(&tickslock, \"time\");\n}\n\n// set up to take exceptions and traps while in the kernel.\nvoid\ntrapinithart(void)\n{\n  w_stvec((uint64)kernelvec);\n}\n\n//\n// handle an interrupt, exception, or system call from user space.\n// called from, and returns to, trampoline.S\n// return value is user satp for trampoline.S to switch to.\n//\nuint64\nusertrap(void)\n{\n  int which_dev = 0;\n\n  if((r_sstatus() & SSTATUS_SPP) != 0)\n    panic(\"usertrap: not from user mode\");\n\n  // send interrupts and exceptions to kerneltrap(),\n  // since we're now in the kernel.\n  w_stvec((uint64)kernelvec);  //DOC: kernelvec\n\n  struct proc *p = myproc();\n  \n  // save user program counter.\n  p->trapframe->epc = r_sepc();\n  \n  if(r_scause() == 8){\n    // system call\n\n    if(killed(p))\n      kexit(-1);\n\n    // sepc points to the ecall instruction,\n    // but we want to return to the next instruction.\n    p->trapframe->epc += 4;\n\n    // an interrupt will change sepc, scause, and sstatus,\n    // so enable only now that we're done with those registers.\n    intr_on();\n\n    syscall();\n  } else if((which_dev = devintr()) != 0){\n    // ok\n  } else if((r_scause() == 15 || r_scause() == 13) &&\n            vmfault(p->pagetable, r_stval(), (r_scause() == 13)? 1 : 0) != 0) {\n    // page fault on lazily-allocated page\n  } else {\n    printf(\"usertrap(): unexpected scause 0x%lx pid=%d\\n\", r_scause(), p->pid);\n    printf(\"            sepc=0x%lx stval=0x%lx\\n\", r_sepc(), r_stval());\n    setkilled(p);\n  }\n\n  if(killed(p))\n    kexit(-1);\n\n  // give up the CPU if this is a timer interrupt.\n  if(which_dev == 2)\n    yield();\n\n  prepare_return();\n\n  // the user page table to switch to, for trampoline.S\n  uint64 satp = MAKE_SATP(p->pagetable);\n\n  // return to trampoline.S; satp value in a0.\n  return satp;\n}\n\n//\n// set up trapframe and control registers for a return to user space\n//\nvoid\nprepare_return(void)\n{\n  struct proc *p = myproc();\n\n  // we're about to switch the destination of traps from\n  // kerneltrap() to usertrap(). because a trap from kernel\n  // code to usertrap would be a disaster, turn off interrupts.\n  intr_off();\n\n  // send syscalls, interrupts, and exceptions to uservec in trampoline.S\n  uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);\n  w_stvec(trampoline_uservec);\n\n  // set up trapframe values that uservec will need when\n  // the process next traps into the kernel.\n  p->trapframe->kernel_satp = r_satp();         // kernel page table\n  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack\n  p->trapframe->kernel_trap = (uint64)usertrap;\n  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()\n\n  // set up the registers that trampoline.S's sret will use\n  // to get to user space.\n  \n  // set S Previous Privilege mode to User.\n  unsigned long x = r_sstatus();\n  x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode\n  x |= SSTATUS_SPIE; // enable interrupts in user mode\n  w_sstatus(x);\n\n  // set S Exception Program Counter to the saved user pc.\n  w_sepc(p->trapframe->epc);\n}\n\n// interrupts and exceptions from kernel code go here via kernelvec,\n// on whatever the current kernel stack is.\nvoid \nkerneltrap()\n{\n  int which_dev = 0;\n  uint64 sepc = r_sepc();\n  uint64 sstatus = r_sstatus();\n  uint64 scause = r_scause();\n  \n  if((sstatus & SSTATUS_SPP) == 0)\n    panic(\"kerneltrap: not from supervisor mode\");\n  if(intr_get() != 0)\n    panic(\"kerneltrap: interrupts enabled\");\n\n  if((which_dev = devintr()) == 0){\n    // interrupt or trap from an unknown source\n    printf(\"scause=0x%lx sepc=0x%lx stval=0x%lx\\n\", scause, r_sepc(), r_stval());\n    panic(\"kerneltrap\");\n  }\n\n  // give up the CPU if this is a timer interrupt.\n  if(which_dev == 2 && myproc() != 0)\n    yield();\n\n  // the yield() may have caused some traps to occur,\n  // so restore trap registers for use by kernelvec.S's sepc instruction.\n  w_sepc(sepc);\n  w_sstatus(sstatus);\n}\n\nvoid\nclockintr()\n{\n  if(cpuid() == 0){\n    acquire(&tickslock);\n    ticks++;\n    wakeup(&ticks);\n    release(&tickslock);\n  }\n\n  // ask for the next timer interrupt. this also clears\n  // the interrupt request. 1000000 is about a tenth\n  // of a second.\n  w_stimecmp(r_time() + 1000000);\n}\n\n// check if it's an external interrupt or software interrupt,\n// and handle it.\n// returns 2 if timer interrupt,\n// 1 if other device,\n// 0 if not recognized.\nint\ndevintr()\n{\n  uint64 scause = r_scause();\n\n  if(scause == 0x8000000000000009L){\n    // this is a supervisor external interrupt, via PLIC.\n\n    // irq indicates which device interrupted.\n    int irq = plic_claim();\n\n    if(irq == UART0_IRQ){\n      uartintr();\n    } else if(irq == VIRTIO0_IRQ){\n      virtio_disk_intr();\n    } else if(irq){\n      printf(\"unexpected interrupt irq=%d\\n\", irq);\n    }\n\n    // the PLIC allows each device to raise at most one\n    // interrupt at a time; tell the PLIC the device is\n    // now allowed to interrupt again.\n    if(irq)\n      plic_complete(irq);\n\n    return 1;\n  } else if(scause == 0x8000000000000005L){\n    // timer interrupt.\n    clockintr();\n    return 2;\n  } else {\n    return 0;\n  }\n}\n\n"
  },
  {
    "path": "kernel/types.h",
    "content": "typedef unsigned int   uint;\ntypedef unsigned short ushort;\ntypedef unsigned char  uchar;\n\ntypedef unsigned char uint8;\ntypedef unsigned short uint16;\ntypedef unsigned int  uint32;\ntypedef unsigned long uint64;\n\ntypedef uint64 pde_t;\n"
  },
  {
    "path": "kernel/uart.c",
    "content": "//\n// low-level driver for 16550a UART.\n//\n\n#include \"types.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"riscv.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"defs.h\"\n\n// the UART control registers are memory-mapped\n// at address UART0. this macro returns the\n// address of one of the registers.\n#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg)))\n\n#define ReadReg(reg) (*(Reg(reg)))\n#define WriteReg(reg, v) (*(Reg(reg)) = (v))\n\n// the UART control registers.\n// some have different meanings for read vs write.\n// see http://byterunner.com/16550.html\n#define RHR 0                 // receive holding register (for input bytes)\n#define THR 0                 // transmit holding register (for output bytes)\n#define IER 1                 // interrupt enable register\n#define IER_RX_ENABLE (1<<0)\n#define IER_TX_ENABLE (1<<1)\n#define FCR 2                 // FIFO control register\n#define FCR_FIFO_ENABLE (1<<0)\n#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs\n#define ISR 2                 // interrupt status register\n#define LCR 3                 // line control register\n#define LCR_EIGHT_BITS (3<<0)\n#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate\n#define LSR 5                 // line status register\n#define LSR_RX_READY (1<<0)   // input is waiting to be read from RHR\n#define LSR_TX_IDLE (1<<5)    // THR can accept another character to send\n\n// for sending threads to synchronize with uart \"ready\" interrupts.\nstatic struct spinlock tx_lock;\nstatic int tx_busy;           // is the UART busy sending?\nstatic int tx_chan;           // &tx_chan is the \"wait channel\"\n\nextern volatile int panicking; // from printf.c\nextern volatile int panicked; // from printf.c\n\nvoid\nuartinit(void)\n{\n  // disable interrupts.\n  WriteReg(IER, 0x00);\n\n  // special mode to set baud rate.\n  WriteReg(LCR, LCR_BAUD_LATCH);\n\n  // LSB for baud rate of 38.4K.\n  WriteReg(0, 0x03);\n\n  // MSB for baud rate of 38.4K.\n  WriteReg(1, 0x00);\n\n  // leave set-baud mode,\n  // and set word length to 8 bits, no parity.\n  WriteReg(LCR, LCR_EIGHT_BITS);\n\n  // reset and enable FIFOs.\n  WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);\n\n  // enable transmit and receive interrupts.\n  WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);\n\n  initlock(&tx_lock, \"uart\");\n}\n\n// transmit buf[] to the uart. it blocks if the\n// uart is busy, so it cannot be called from\n// interrupts, only from write() system calls.\nvoid\nuartwrite(char buf[], int n)\n{\n  acquire(&tx_lock);\n\n  int i = 0;\n  while(i < n){ \n    while(tx_busy != 0){\n      // wait for a UART transmit-complete interrupt\n      // to set tx_busy to 0.\n      sleep(&tx_chan, &tx_lock);\n    }   \n      \n    WriteReg(THR, buf[i]);\n    i += 1;\n    tx_busy = 1;\n  }\n\n  release(&tx_lock);\n}\n\n\n// write a byte to the uart without using\n// interrupts, for use by kernel printf() and\n// to echo characters. it spins waiting for the uart's\n// output register to be empty.\nvoid\nuartputc_sync(int c)\n{\n  if(panicking == 0)\n    push_off();\n\n  if(panicked){\n    for(;;)\n      ;\n  }\n\n  // wait for UART to set Transmit Holding Empty in LSR.\n  while((ReadReg(LSR) & LSR_TX_IDLE) == 0)\n    ;\n  WriteReg(THR, c);\n\n  if(panicking == 0)\n    pop_off();\n}\n\n// try to read one input character from the UART.\n// return -1 if none is waiting.\nint\nuartgetc(void)\n{\n  if(ReadReg(LSR) & LSR_RX_READY){\n    // input data is ready.\n    return ReadReg(RHR);\n  } else {\n    return -1;\n  }\n}\n\n// handle a uart interrupt, raised because input has\n// arrived, or the uart is ready for more output, or\n// both. called from devintr().\nvoid\nuartintr(void)\n{\n  ReadReg(ISR); // acknowledge the interrupt\n\n  acquire(&tx_lock);\n  if(ReadReg(LSR) & LSR_TX_IDLE){\n    // UART finished transmitting; wake up sending thread.\n    tx_busy = 0;\n    wakeup(&tx_chan);\n  }\n  release(&tx_lock);\n\n  // read and process incoming characters, if any.\n  while(1){\n    int c = uartgetc();\n    if(c == -1)\n      break;\n    consoleintr(c);\n  }\n}\n"
  },
  {
    "path": "kernel/virtio.h",
    "content": "//\n// virtio device definitions.\n// for both the mmio interface, and virtio descriptors.\n// only tested with qemu.\n//\n// the virtio spec:\n// https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf\n//\n\n// virtio mmio control registers, mapped starting at 0x10001000.\n// from qemu virtio_mmio.h\n#define VIRTIO_MMIO_MAGIC_VALUE\t\t0x000 // 0x74726976\n#define VIRTIO_MMIO_VERSION\t\t0x004 // version; should be 2\n#define VIRTIO_MMIO_DEVICE_ID\t\t0x008 // device type; 1 is net, 2 is disk\n#define VIRTIO_MMIO_VENDOR_ID\t\t0x00c // 0x554d4551\n#define VIRTIO_MMIO_DEVICE_FEATURES\t0x010\n#define VIRTIO_MMIO_DRIVER_FEATURES\t0x020\n#define VIRTIO_MMIO_QUEUE_SEL\t\t0x030 // select queue, write-only\n#define VIRTIO_MMIO_QUEUE_NUM_MAX\t0x034 // max size of current queue, read-only\n#define VIRTIO_MMIO_QUEUE_NUM\t\t0x038 // size of current queue, write-only\n#define VIRTIO_MMIO_QUEUE_READY\t\t0x044 // ready bit\n#define VIRTIO_MMIO_QUEUE_NOTIFY\t0x050 // write-only\n#define VIRTIO_MMIO_INTERRUPT_STATUS\t0x060 // read-only\n#define VIRTIO_MMIO_INTERRUPT_ACK\t0x064 // write-only\n#define VIRTIO_MMIO_STATUS\t\t0x070 // read/write\n#define VIRTIO_MMIO_QUEUE_DESC_LOW\t0x080 // physical address for descriptor table, write-only\n#define VIRTIO_MMIO_QUEUE_DESC_HIGH\t0x084\n#define VIRTIO_MMIO_DRIVER_DESC_LOW\t0x090 // physical address for available ring, write-only\n#define VIRTIO_MMIO_DRIVER_DESC_HIGH\t0x094\n#define VIRTIO_MMIO_DEVICE_DESC_LOW\t0x0a0 // physical address for used ring, write-only\n#define VIRTIO_MMIO_DEVICE_DESC_HIGH\t0x0a4\n\n// status register bits, from qemu virtio_config.h\n#define VIRTIO_CONFIG_S_ACKNOWLEDGE\t1\n#define VIRTIO_CONFIG_S_DRIVER\t\t2\n#define VIRTIO_CONFIG_S_DRIVER_OK\t4\n#define VIRTIO_CONFIG_S_FEATURES_OK\t8\n\n// device feature bits\n#define VIRTIO_BLK_F_RO              5\t/* Disk is read-only */\n#define VIRTIO_BLK_F_SCSI            7\t/* Supports scsi command passthru */\n#define VIRTIO_BLK_F_CONFIG_WCE     11\t/* Writeback mode available in config */\n#define VIRTIO_BLK_F_MQ             12\t/* support more than one vq */\n#define VIRTIO_F_ANY_LAYOUT         27\n#define VIRTIO_RING_F_INDIRECT_DESC 28\n#define VIRTIO_RING_F_EVENT_IDX     29\n\n// this many virtio descriptors.\n// must be a power of two.\n#define NUM 8\n\n// a single descriptor, from the spec.\nstruct virtq_desc {\n  uint64 addr;\n  uint32 len;\n  uint16 flags;\n  uint16 next;\n};\n#define VRING_DESC_F_NEXT  1 // chained with another descriptor\n#define VRING_DESC_F_WRITE 2 // device writes (vs read)\n\n// the (entire) avail ring, from the spec.\nstruct virtq_avail {\n  uint16 flags; // always zero\n  uint16 idx;   // driver will write ring[idx] next\n  uint16 ring[NUM]; // descriptor numbers of chain heads\n  uint16 unused;\n};\n\n// one entry in the \"used\" ring, with which the\n// device tells the driver about completed requests.\nstruct virtq_used_elem {\n  uint32 id;   // index of start of completed descriptor chain\n  uint32 len;\n};\n\nstruct virtq_used {\n  uint16 flags; // always zero\n  uint16 idx;   // device increments when it adds a ring[] entry\n  struct virtq_used_elem ring[NUM];\n};\n\n// these are specific to virtio block devices, e.g. disks,\n// described in Section 5.2 of the spec.\n\n#define VIRTIO_BLK_T_IN  0 // read the disk\n#define VIRTIO_BLK_T_OUT 1 // write the disk\n\n// the format of the first descriptor in a disk request.\n// to be followed by two more descriptors containing\n// the block, and a one-byte status.\nstruct virtio_blk_req {\n  uint32 type; // VIRTIO_BLK_T_IN or ..._OUT\n  uint32 reserved;\n  uint64 sector;\n};\n"
  },
  {
    "path": "kernel/virtio_disk.c",
    "content": "//\n// driver for qemu's virtio disk device.\n// uses qemu's mmio interface to virtio.\n//\n// qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0\n//\n\n#include \"types.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"param.h\"\n#include \"memlayout.h\"\n#include \"spinlock.h\"\n#include \"sleeplock.h\"\n#include \"fs.h\"\n#include \"buf.h\"\n#include \"virtio.h\"\n\n// the address of virtio mmio register r.\n#define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))\n\nstatic struct disk {\n  // a set (not a ring) of DMA descriptors, with which the\n  // driver tells the device where to read and write individual\n  // disk operations. there are NUM descriptors.\n  // most commands consist of a \"chain\" (a linked list) of a couple of\n  // these descriptors.\n  struct virtq_desc *desc;\n\n  // a ring in which the driver writes descriptor numbers\n  // that the driver would like the device to process.  it only\n  // includes the head descriptor of each chain. the ring has\n  // NUM elements.\n  struct virtq_avail *avail;\n\n  // a ring in which the device writes descriptor numbers that\n  // the device has finished processing (just the head of each chain).\n  // there are NUM used ring entries.\n  struct virtq_used *used;\n\n  // our own book-keeping.\n  char free[NUM];  // is a descriptor free?\n  uint16 used_idx; // we've looked this far in used[2..NUM].\n\n  // track info about in-flight operations,\n  // for use when completion interrupt arrives.\n  // indexed by first descriptor index of chain.\n  struct {\n    struct buf *b;\n    char status;\n  } info[NUM];\n\n  // disk command headers.\n  // one-for-one with descriptors, for convenience.\n  struct virtio_blk_req ops[NUM];\n  \n  struct spinlock vdisk_lock;\n  \n} disk;\n\nvoid\nvirtio_disk_init(void)\n{\n  uint32 status = 0;\n\n  initlock(&disk.vdisk_lock, \"virtio_disk\");\n\n  if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||\n     *R(VIRTIO_MMIO_VERSION) != 2 ||\n     *R(VIRTIO_MMIO_DEVICE_ID) != 2 ||\n     *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){\n    panic(\"could not find virtio disk\");\n  }\n  \n  // reset device\n  *R(VIRTIO_MMIO_STATUS) = status;\n\n  // set ACKNOWLEDGE status bit\n  status |= VIRTIO_CONFIG_S_ACKNOWLEDGE;\n  *R(VIRTIO_MMIO_STATUS) = status;\n\n  // set DRIVER status bit\n  status |= VIRTIO_CONFIG_S_DRIVER;\n  *R(VIRTIO_MMIO_STATUS) = status;\n\n  // negotiate features\n  uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES);\n  features &= ~(1 << VIRTIO_BLK_F_RO);\n  features &= ~(1 << VIRTIO_BLK_F_SCSI);\n  features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);\n  features &= ~(1 << VIRTIO_BLK_F_MQ);\n  features &= ~(1 << VIRTIO_F_ANY_LAYOUT);\n  features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);\n  features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);\n  *R(VIRTIO_MMIO_DRIVER_FEATURES) = features;\n\n  // tell device that feature negotiation is complete.\n  status |= VIRTIO_CONFIG_S_FEATURES_OK;\n  *R(VIRTIO_MMIO_STATUS) = status;\n\n  // re-read status to ensure FEATURES_OK is set.\n  status = *R(VIRTIO_MMIO_STATUS);\n  if(!(status & VIRTIO_CONFIG_S_FEATURES_OK))\n    panic(\"virtio disk FEATURES_OK unset\");\n\n  // initialize queue 0.\n  *R(VIRTIO_MMIO_QUEUE_SEL) = 0;\n\n  // ensure queue 0 is not in use.\n  if(*R(VIRTIO_MMIO_QUEUE_READY))\n    panic(\"virtio disk should not be ready\");\n\n  // check maximum queue size.\n  uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX);\n  if(max == 0)\n    panic(\"virtio disk has no queue 0\");\n  if(max < NUM)\n    panic(\"virtio disk max queue too short\");\n\n  // allocate and zero queue memory.\n  disk.desc = kalloc();\n  disk.avail = kalloc();\n  disk.used = kalloc();\n  if(!disk.desc || !disk.avail || !disk.used)\n    panic(\"virtio disk kalloc\");\n  memset(disk.desc, 0, PGSIZE);\n  memset(disk.avail, 0, PGSIZE);\n  memset(disk.used, 0, PGSIZE);\n\n  // set queue size.\n  *R(VIRTIO_MMIO_QUEUE_NUM) = NUM;\n\n  // write physical addresses.\n  *R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc;\n  *R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32;\n  *R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail;\n  *R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32;\n  *R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used;\n  *R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32;\n\n  // queue is ready.\n  *R(VIRTIO_MMIO_QUEUE_READY) = 0x1;\n\n  // all NUM descriptors start out unused.\n  for(int i = 0; i < NUM; i++)\n    disk.free[i] = 1;\n\n  // tell device we're completely ready.\n  status |= VIRTIO_CONFIG_S_DRIVER_OK;\n  *R(VIRTIO_MMIO_STATUS) = status;\n\n  // plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.\n}\n\n// find a free descriptor, mark it non-free, return its index.\nstatic int\nalloc_desc()\n{\n  for(int i = 0; i < NUM; i++){\n    if(disk.free[i]){\n      disk.free[i] = 0;\n      return i;\n    }\n  }\n  return -1;\n}\n\n// mark a descriptor as free.\nstatic void\nfree_desc(int i)\n{\n  if(i >= NUM)\n    panic(\"free_desc 1\");\n  if(disk.free[i])\n    panic(\"free_desc 2\");\n  disk.desc[i].addr = 0;\n  disk.desc[i].len = 0;\n  disk.desc[i].flags = 0;\n  disk.desc[i].next = 0;\n  disk.free[i] = 1;\n  wakeup(&disk.free[0]);\n}\n\n// free a chain of descriptors.\nstatic void\nfree_chain(int i)\n{\n  while(1){\n    int flag = disk.desc[i].flags;\n    int nxt = disk.desc[i].next;\n    free_desc(i);\n    if(flag & VRING_DESC_F_NEXT)\n      i = nxt;\n    else\n      break;\n  }\n}\n\n// allocate three descriptors (they need not be contiguous).\n// disk transfers always use three descriptors.\nstatic int\nalloc3_desc(int *idx)\n{\n  for(int i = 0; i < 3; i++){\n    idx[i] = alloc_desc();\n    if(idx[i] < 0){\n      for(int j = 0; j < i; j++)\n        free_desc(idx[j]);\n      return -1;\n    }\n  }\n  return 0;\n}\n\nvoid\nvirtio_disk_rw(struct buf *b, int write)\n{\n  uint64 sector = b->blockno * (BSIZE / 512);\n\n  acquire(&disk.vdisk_lock);\n\n  // the spec's Section 5.2 says that legacy block operations use\n  // three descriptors: one for type/reserved/sector, one for the\n  // data, one for a 1-byte status result.\n\n  // allocate the three descriptors.\n  int idx[3];\n  while(1){\n    if(alloc3_desc(idx) == 0) {\n      break;\n    }\n    sleep(&disk.free[0], &disk.vdisk_lock);\n  }\n\n  // format the three descriptors.\n  // qemu's virtio-blk.c reads them.\n\n  struct virtio_blk_req *buf0 = &disk.ops[idx[0]];\n\n  if(write)\n    buf0->type = VIRTIO_BLK_T_OUT; // write the disk\n  else\n    buf0->type = VIRTIO_BLK_T_IN; // read the disk\n  buf0->reserved = 0;\n  buf0->sector = sector;\n\n  disk.desc[idx[0]].addr = (uint64) buf0;\n  disk.desc[idx[0]].len = sizeof(struct virtio_blk_req);\n  disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;\n  disk.desc[idx[0]].next = idx[1];\n\n  disk.desc[idx[1]].addr = (uint64) b->data;\n  disk.desc[idx[1]].len = BSIZE;\n  if(write)\n    disk.desc[idx[1]].flags = 0; // device reads b->data\n  else\n    disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data\n  disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT;\n  disk.desc[idx[1]].next = idx[2];\n\n  disk.info[idx[0]].status = 0xff; // device writes 0 on success\n  disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status;\n  disk.desc[idx[2]].len = 1;\n  disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status\n  disk.desc[idx[2]].next = 0;\n\n  // record struct buf for virtio_disk_intr().\n  b->disk = 1;\n  disk.info[idx[0]].b = b;\n\n  // tell the device the first index in our chain of descriptors.\n  disk.avail->ring[disk.avail->idx % NUM] = idx[0];\n\n  __sync_synchronize();\n\n  // tell the device another avail ring entry is available.\n  disk.avail->idx += 1; // not % NUM ...\n\n  __sync_synchronize();\n\n  *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number\n\n  // Wait for virtio_disk_intr() to say request has finished.\n  while(b->disk == 1) {\n    sleep(b, &disk.vdisk_lock);\n  }\n\n  disk.info[idx[0]].b = 0;\n  free_chain(idx[0]);\n\n  release(&disk.vdisk_lock);\n}\n\nvoid\nvirtio_disk_intr()\n{\n  acquire(&disk.vdisk_lock);\n\n  // the device won't raise another interrupt until we tell it\n  // we've seen this interrupt, which the following line does.\n  // this may race with the device writing new entries to\n  // the \"used\" ring, in which case we may process the new\n  // completion entries in this interrupt, and have nothing to do\n  // in the next interrupt, which is harmless.\n  *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3;\n\n  __sync_synchronize();\n\n  // the device increments disk.used->idx when it\n  // adds an entry to the used ring.\n\n  while(disk.used_idx != disk.used->idx){\n    __sync_synchronize();\n    int id = disk.used->ring[disk.used_idx % NUM].id;\n\n    if(disk.info[id].status != 0)\n      panic(\"virtio_disk_intr status\");\n\n    struct buf *b = disk.info[id].b;\n    b->disk = 0;   // disk is done with buf\n    wakeup(b);\n\n    disk.used_idx += 1;\n  }\n\n  release(&disk.vdisk_lock);\n}\n"
  },
  {
    "path": "kernel/vm.c",
    "content": "#include \"param.h\"\n#include \"types.h\"\n#include \"memlayout.h\"\n#include \"elf.h\"\n#include \"riscv.h\"\n#include \"defs.h\"\n#include \"spinlock.h\"\n#include \"proc.h\"\n#include \"fs.h\"\n\n/*\n * the kernel's page table.\n */\npagetable_t kernel_pagetable;\n\nextern char etext[];  // kernel.ld sets this to end of kernel code.\n\nextern char trampoline[]; // trampoline.S\n\n// Make a direct-map page table for the kernel.\npagetable_t\nkvmmake(void)\n{\n  pagetable_t kpgtbl;\n\n  kpgtbl = (pagetable_t) kalloc();\n  memset(kpgtbl, 0, PGSIZE);\n\n  // uart registers\n  kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W);\n\n  // virtio mmio disk interface\n  kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);\n\n  // PLIC\n  kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);\n\n  // map kernel text executable and read-only.\n  kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);\n\n  // map kernel data and the physical RAM we'll make use of.\n  kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);\n\n  // map the trampoline for trap entry/exit to\n  // the highest virtual address in the kernel.\n  kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);\n\n  // allocate and map a kernel stack for each process.\n  proc_mapstacks(kpgtbl);\n  \n  return kpgtbl;\n}\n\n// add a mapping to the kernel page table.\n// only used when booting.\n// does not flush TLB or enable paging.\nvoid\nkvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm)\n{\n  if(mappages(kpgtbl, va, sz, pa, perm) != 0)\n    panic(\"kvmmap\");\n}\n\n// Initialize the kernel_pagetable, shared by all CPUs.\nvoid\nkvminit(void)\n{\n  kernel_pagetable = kvmmake();\n}\n\n// Switch the current CPU's h/w page table register to\n// the kernel's page table, and enable paging.\nvoid\nkvminithart()\n{\n  // wait for any previous writes to the page table memory to finish.\n  sfence_vma();\n\n  w_satp(MAKE_SATP(kernel_pagetable));\n\n  // flush stale entries from the TLB.\n  sfence_vma();\n}\n\n// Return the address of the PTE in page table pagetable\n// that corresponds to virtual address va.  If alloc!=0,\n// create any required page-table pages.\n//\n// The risc-v Sv39 scheme has three levels of page-table\n// pages. A page-table page contains 512 64-bit PTEs.\n// A 64-bit virtual address is split into five fields:\n//   39..63 -- must be zero.\n//   30..38 -- 9 bits of level-2 index.\n//   21..29 -- 9 bits of level-1 index.\n//   12..20 -- 9 bits of level-0 index.\n//    0..11 -- 12 bits of byte offset within the page.\npte_t *\nwalk(pagetable_t pagetable, uint64 va, int alloc)\n{\n  if(va >= MAXVA)\n    panic(\"walk\");\n\n  for(int level = 2; level > 0; level--) {\n    pte_t *pte = &pagetable[PX(level, va)];\n    if(*pte & PTE_V) {\n      pagetable = (pagetable_t)PTE2PA(*pte);\n    } else {\n      if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)\n        return 0;\n      memset(pagetable, 0, PGSIZE);\n      *pte = PA2PTE(pagetable) | PTE_V;\n    }\n  }\n  return &pagetable[PX(0, va)];\n}\n\n// Look up a virtual address, return the physical address,\n// or 0 if not mapped.\n// Can only be used to look up user pages.\nuint64\nwalkaddr(pagetable_t pagetable, uint64 va)\n{\n  pte_t *pte;\n  uint64 pa;\n\n  if(va >= MAXVA)\n    return 0;\n\n  pte = walk(pagetable, va, 0);\n  if(pte == 0)\n    return 0;\n  if((*pte & PTE_V) == 0)\n    return 0;\n  if((*pte & PTE_U) == 0)\n    return 0;\n  pa = PTE2PA(*pte);\n  return pa;\n}\n\n// Create PTEs for virtual addresses starting at va that refer to\n// physical addresses starting at pa.\n// va and size MUST be page-aligned.\n// Returns 0 on success, -1 if walk() couldn't\n// allocate a needed page-table page.\nint\nmappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)\n{\n  uint64 a, last;\n  pte_t *pte;\n\n  if((va % PGSIZE) != 0)\n    panic(\"mappages: va not aligned\");\n\n  if((size % PGSIZE) != 0)\n    panic(\"mappages: size not aligned\");\n\n  if(size == 0)\n    panic(\"mappages: size\");\n  \n  a = va;\n  last = va + size - PGSIZE;\n  for(;;){\n    if((pte = walk(pagetable, a, 1)) == 0)\n      return -1;\n    if(*pte & PTE_V)\n      panic(\"mappages: remap\");\n    *pte = PA2PTE(pa) | perm | PTE_V;\n    if(a == last)\n      break;\n    a += PGSIZE;\n    pa += PGSIZE;\n  }\n  return 0;\n}\n\n// create an empty user page table.\n// returns 0 if out of memory.\npagetable_t\nuvmcreate()\n{\n  pagetable_t pagetable;\n  pagetable = (pagetable_t) kalloc();\n  if(pagetable == 0)\n    return 0;\n  memset(pagetable, 0, PGSIZE);\n  return pagetable;\n}\n\n// Remove npages of mappings starting from va. va must be\n// page-aligned. It's OK if the mappings don't exist.\n// Optionally free the physical memory.\nvoid\nuvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)\n{\n  uint64 a;\n  pte_t *pte;\n\n  if((va % PGSIZE) != 0)\n    panic(\"uvmunmap: not aligned\");\n\n  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){\n    if((pte = walk(pagetable, a, 0)) == 0) // leaf page table entry allocated?\n      continue;   \n    if((*pte & PTE_V) == 0)  // has physical page been allocated?\n      continue;\n    if(do_free){\n      uint64 pa = PTE2PA(*pte);\n      kfree((void*)pa);\n    }\n    *pte = 0;\n  }\n}\n\n// Allocate PTEs and physical memory to grow a process from oldsz to\n// newsz, which need not be page aligned.  Returns new size or 0 on error.\nuint64\nuvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)\n{\n  char *mem;\n  uint64 a;\n\n  if(newsz < oldsz)\n    return oldsz;\n\n  oldsz = PGROUNDUP(oldsz);\n  for(a = oldsz; a < newsz; a += PGSIZE){\n    mem = kalloc();\n    if(mem == 0){\n      uvmdealloc(pagetable, a, oldsz);\n      return 0;\n    }\n    memset(mem, 0, PGSIZE);\n    if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){\n      kfree(mem);\n      uvmdealloc(pagetable, a, oldsz);\n      return 0;\n    }\n  }\n  return newsz;\n}\n\n// Deallocate user pages to bring the process size from oldsz to\n// newsz.  oldsz and newsz need not be page-aligned, nor does newsz\n// need to be less than oldsz.  oldsz can be larger than the actual\n// process size.  Returns the new process size.\nuint64\nuvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)\n{\n  if(newsz >= oldsz)\n    return oldsz;\n\n  if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){\n    int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;\n    uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);\n  }\n\n  return newsz;\n}\n\n// Recursively free page-table pages.\n// All leaf mappings must already have been removed.\nvoid\nfreewalk(pagetable_t pagetable)\n{\n  // there are 2^9 = 512 PTEs in a page table.\n  for(int i = 0; i < 512; i++){\n    pte_t pte = pagetable[i];\n    if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){\n      // this PTE points to a lower-level page table.\n      uint64 child = PTE2PA(pte);\n      freewalk((pagetable_t)child);\n      pagetable[i] = 0;\n    } else if(pte & PTE_V){\n      panic(\"freewalk: leaf\");\n    }\n  }\n  kfree((void*)pagetable);\n}\n\n// Free user memory pages,\n// then free page-table pages.\nvoid\nuvmfree(pagetable_t pagetable, uint64 sz)\n{\n  if(sz > 0)\n    uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);\n  freewalk(pagetable);\n}\n\n// Given a parent process's page table, copy\n// its memory into a child's page table.\n// Copies both the page table and the\n// physical memory.\n// returns 0 on success, -1 on failure.\n// frees any allocated pages on failure.\nint\nuvmcopy(pagetable_t old, pagetable_t new, uint64 sz)\n{\n  pte_t *pte;\n  uint64 pa, i;\n  uint flags;\n  char *mem;\n\n  for(i = 0; i < sz; i += PGSIZE){\n    if((pte = walk(old, i, 0)) == 0)\n      continue;   // page table entry hasn't been allocated\n    if((*pte & PTE_V) == 0)\n      continue;   // physical page hasn't been allocated\n    pa = PTE2PA(*pte);\n    flags = PTE_FLAGS(*pte);\n    if((mem = kalloc()) == 0)\n      goto err;\n    memmove(mem, (char*)pa, PGSIZE);\n    if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){\n      kfree(mem);\n      goto err;\n    }\n  }\n  return 0;\n\n err:\n  uvmunmap(new, 0, i / PGSIZE, 1);\n  return -1;\n}\n\n// mark a PTE invalid for user access.\n// used by exec for the user stack guard page.\nvoid\nuvmclear(pagetable_t pagetable, uint64 va)\n{\n  pte_t *pte;\n  \n  pte = walk(pagetable, va, 0);\n  if(pte == 0)\n    panic(\"uvmclear\");\n  *pte &= ~PTE_U;\n}\n\n// Copy from kernel to user.\n// Copy len bytes from src to virtual address dstva in a given page table.\n// Return 0 on success, -1 on error.\nint\ncopyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)\n{\n  uint64 n, va0, pa0;\n  pte_t *pte;\n\n  while(len > 0){\n    va0 = PGROUNDDOWN(dstva);\n    if(va0 >= MAXVA)\n      return -1;\n  \n    pa0 = walkaddr(pagetable, va0);\n    if(pa0 == 0) {\n      if((pa0 = vmfault(pagetable, va0, 0)) == 0) {\n        return -1;\n      }\n    }\n\n    pte = walk(pagetable, va0, 0);\n    // forbid copyout over read-only user text pages.\n    if((*pte & PTE_W) == 0)\n      return -1;\n      \n    n = PGSIZE - (dstva - va0);\n    if(n > len)\n      n = len;\n    memmove((void *)(pa0 + (dstva - va0)), src, n);\n\n    len -= n;\n    src += n;\n    dstva = va0 + PGSIZE;\n  }\n  return 0;\n}\n\n// Copy from user to kernel.\n// Copy len bytes to dst from virtual address srcva in a given page table.\n// Return 0 on success, -1 on error.\nint\ncopyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)\n{\n  uint64 n, va0, pa0;\n\n  while(len > 0){\n    va0 = PGROUNDDOWN(srcva);\n    pa0 = walkaddr(pagetable, va0);\n    if(pa0 == 0) {\n      if((pa0 = vmfault(pagetable, va0, 0)) == 0) {\n        return -1;\n      }\n    }\n    n = PGSIZE - (srcva - va0);\n    if(n > len)\n      n = len;\n    memmove(dst, (void *)(pa0 + (srcva - va0)), n);\n\n    len -= n;\n    dst += n;\n    srcva = va0 + PGSIZE;\n  }\n  return 0;\n}\n\n// Copy a null-terminated string from user to kernel.\n// Copy bytes to dst from virtual address srcva in a given page table,\n// until a '\\0', or max.\n// Return 0 on success, -1 on error.\nint\ncopyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)\n{\n  uint64 n, va0, pa0;\n  int got_null = 0;\n\n  while(got_null == 0 && max > 0){\n    va0 = PGROUNDDOWN(srcva);\n    pa0 = walkaddr(pagetable, va0);\n    if(pa0 == 0)\n      return -1;\n    n = PGSIZE - (srcva - va0);\n    if(n > max)\n      n = max;\n\n    char *p = (char *) (pa0 + (srcva - va0));\n    while(n > 0){\n      if(*p == '\\0'){\n        *dst = '\\0';\n        got_null = 1;\n        break;\n      } else {\n        *dst = *p;\n      }\n      --n;\n      --max;\n      p++;\n      dst++;\n    }\n\n    srcva = va0 + PGSIZE;\n  }\n  if(got_null){\n    return 0;\n  } else {\n    return -1;\n  }\n}\n\n// allocate and map user memory if process is referencing a page\n// that was lazily allocated in sys_sbrk().\n// returns 0 if va is invalid or already mapped, or if\n// out of physical memory, and physical address if successful.\nuint64\nvmfault(pagetable_t pagetable, uint64 va, int read)\n{\n  uint64 mem;\n  struct proc *p = myproc();\n\n  if (va >= p->sz)\n    return 0;\n  va = PGROUNDDOWN(va);\n  if(ismapped(pagetable, va)) {\n    return 0;\n  }\n  mem = (uint64) kalloc();\n  if(mem == 0)\n    return 0;\n  memset((void *) mem, 0, PGSIZE);\n  if (mappages(p->pagetable, va, PGSIZE, mem, PTE_W|PTE_U|PTE_R) != 0) {\n    kfree((void *)mem);\n    return 0;\n  }\n  return mem;\n}\n\nint\nismapped(pagetable_t pagetable, uint64 va)\n{\n  pte_t *pte = walk(pagetable, va, 0);\n  if (pte == 0) {\n    return 0;\n  }\n  if (*pte & PTE_V){\n    return 1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "kernel/vm.h",
    "content": "#define SBRK_EAGER 1\n#define SBRK_LAZY  2\n"
  },
  {
    "path": "test-xv6.py",
    "content": "#!/usr/bin/env python3\n\n#\n# python script that tests xv6 without having to boot it and type to its shell\n#\n# ./test-xv6.py usertests  (runs usertests)\n# ./test-xv6.py -q usertests (runs the quick tests of usertests)\n# ./test-xv6.py crash  (runs the crash tests)\n# ./test-xv6.py log (runs the log crash test)\n\nimport argparse, os, inspect, re, signal, subprocess, sys, time\nfrom subprocess import run\n\nparser = argparse.ArgumentParser()\nparser.add_argument('testrex', help=\"test name or regular expression\")\nparser.add_argument(\"-q\", action='store_true', help=\"usertests quick\")\nargs = parser.parse_args()\n\nclass QEMU(object):\n\n    def __init__(self, reset=False):\n        if reset:\n            self.build_xv6()\n            self.reset_fs()\n        q = [\"make\", \"qemu\"]\n        self.proc = subprocess.Popen(q, stdin=subprocess.PIPE,\n                                      stdout=subprocess.PIPE,\n                                      stderr=subprocess.STDOUT)\n        self.output = \"\"\n        self.outbytes = bytearray()       \n        time.sleep(1)\n\n    def reset_fs(self):\n        try:\n            run([\"rm\", \"fs.img\"], check=True)\n            run([\"make\", \"fs.img\"], check=True)\n        except subprocess.CalledProcessError as e:\n            print(f\"Command failed with exit code {e.returncode}\")\n\n    def build_xv6(self):\n        try:\n            run([\"make\", \"kernel/kernel\"], check=True)\n        except subprocess.CalledProcessError as e:\n            print(f\"Command failed with exit code {e.returncode}\")\n\n    def save_output(self):\n      try:\n        with open(\"test-xv6.out\", \"w\") as f:\n            f.write(self.out)\n            f.close()\n      except OSError as e:\n        print(\"Provided a bad results path. Error:\", e)     \n        \n    def cmd(self, c):\n        if isinstance(c, str):\n            c = c.encode('utf-8')\n        self.proc.stdin.write(c)\n        self.proc.stdin.flush()\n        \n    def crash(self):\n        ps = run(['ps', '-opid', '--no-headers', '--ppid', str(self.proc.pid)], stdout=subprocess.PIPE, encoding='utf8')\n        kids = [int(line) for line in ps.stdout.splitlines()]\n        if len(kids) == 0:\n            print(\"no qemu\")\n            os.exit(1)\n        print(\"kill\", kids[0])\n        os.kill(kids[0], signal.SIGKILL)\n\n    def stop(self):\n        self.proc.terminate()\n\n    def read(self):\n        buf = os.read(self.proc.stdout.fileno(), 4096)\n        self.outbytes.extend(buf)\n        self.output = self.outbytes.decode(\"utf-8\", \"replace\")\n\n    def lines(self):\n        return self.output.splitlines()\n\n    def error(self):\n        print(\"FAIL: match failed\", regexps)\n        self.save_output()\n        self.stop()\n        sys.exit(1)\n\n    def match(self, *regexps, exit=True):\n        lines = self.lines()\n        last = -1\n        for i, line in enumerate(lines):\n            if any(re.match(r, line) for r in regexps):\n                print(line)\n                last = i\n        if last == -1 and exit:\n            self.error()\n        l = \"\"\n        if last >= 0:\n            l = lines[last]\n        return last >= 0, l\n\n    def monitor(self, *regexps, progress=\"\", timeout):\n        deadline = time.time() + timeout\n        while True:\n            time.sleep(1)\n            timeleft = deadline - time.time()\n            if timeleft < 0:\n                self.error()\n            self.read()\n            ok, _ = self.match(*regexps, exit=False)\n            if ok:\n                return\n            ok, line = self.match(progress, exit=False)\n            if ok:\n                print(line)\n\ndef crash_log():\n    q = QEMU(True)\n    q.cmd(\"logstress f0 f1 f2 f3 f4 f5\\n\")\n    time.sleep(2)\n    q.crash()\n    q.stop()\n\ndef recover_log():\n    q = QEMU()\n    time.sleep(2)\n    q.read()\n    ok, _ = q.match('^recovering', exit=False)\n    if ok:\n        q.cmd(\"ls\\n\")\n        time.sleep(2)\n        q.read()\n        q.match('f5')\n    q.stop()\n    return ok\n\ndef forphan():\n    q = QEMU(True)\n    q.cmd(\"forphan\\n\")\n    time.sleep(5)\n    q.read()\n    q.match('wait')\n    q.crash()\n    q.stop()\n\ndef dorphan():\n    q = QEMU(True)\n    q.cmd(\"dorphan\\n\")\n    time.sleep(5)\n    q.read()\n    q.match('wait')\n    q.crash()\n    q.stop()\n\ndef recover_orphan():\n    q = QEMU()\n    time.sleep(2)\n    q.read()\n    q.match('^ireclaim')\n    q.stop()\n\ndef test_log():\n    print(\"Test recovery of log\")\n    for i in range(5):\n        crash_log()\n        ok = recover_log()\n        if ok:\n            print(\"OK\")\n            return\n        print(\"log attempt \", i+1)\n    print(\"FAIL\")\n    sys.exit(1)\n    \ndef test_forphan():\n    print(\"Test recovery of an orphaned file\")\n    forphan()\n    recover_orphan()\n    print(\"OK\")\n\ndef test_dorphan():\n    print(\"Test recovery of an orphaned file\")\n    dorphan()\n    recover_orphan()\n    print(\"OK\")\n\ndef test_crash():\n    test_log()\n    test_forphan()\n    test_dorphan()\n\ndef test_usertests(test=\"\"):\n    timeout = 600\n    opt = \"\"\n    if args.q:\n        opt = \" -q\"\n        timeout = 300\n    elif test != \"\":\n        opt += \" \" + test\n    q = QEMU(True)\n    q.cmd(\"usertests\" + opt + \"\\n\")\n    q.monitor('^ALL TESTS PASSED', progress='test', timeout=timeout)\n    q.stop()\n\ndef main():\n    print(args)\n    rex = r'%s' % args.testrex\n    funcs = [(obj,name) for name,obj in inspect.getmembers(sys.modules[__name__]) \n                     if (inspect.isfunction(obj) and \n                         name.startswith('test'))]\n    none = True\n    for (f,n) in funcs:\n        if re.search(rex, n):\n            none = False\n            f()\n    if none:\n        test_usertests(test=args.testrex)\n\nmain()\n"
  },
  {
    "path": "user/cat.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\nchar buf[512];\n\nvoid\ncat(int fd)\n{\n  int n;\n\n  while((n = read(fd, buf, sizeof(buf))) > 0) {\n    if (write(1, buf, n) != n) {\n      fprintf(2, \"cat: write error\\n\");\n      exit(1);\n    }\n  }\n  if(n < 0){\n    fprintf(2, \"cat: read error\\n\");\n    exit(1);\n  }\n}\n\nint\nmain(int argc, char *argv[])\n{\n  int fd, i;\n\n  if(argc <= 1){\n    cat(0);\n    exit(0);\n  }\n\n  for(i = 1; i < argc; i++){\n    if((fd = open(argv[i], O_RDONLY)) < 0){\n      fprintf(2, \"cat: cannot open %s\\n\", argv[i]);\n      exit(1);\n    }\n    cat(fd);\n    close(fd);\n  }\n  exit(0);\n}\n"
  },
  {
    "path": "user/dorphan.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\n// Create an orphaned directory and check if test-xv6.py recovers it.\n\n#define BUFSZ 500\n\nchar buf[BUFSZ];\n\nint\nmain(int argc, char **argv)\n{\n  char *s = argv[0];\n\n  if(mkdir(\"dd\") != 0){\n    printf(\"%s: mkdir dd failed\\n\", s);\n    exit(1);\n  }\n\n  if(chdir(\"dd\") != 0){\n    printf(\"%s: chdir dd failed\\n\", s);\n    exit(1);\n  }\n\n  if (unlink(\"../dd\") < 0) {\n    printf(\"%s: unlink failed\\n\", s);\n    exit(1);\n  }\n  printf(\"wait for kill and reclaim\\n\");\n  // sit around until killed\n  for(;;) pause(1000);\n}\n"
  },
  {
    "path": "user/echo.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(int argc, char *argv[])\n{\n  int i;\n\n  for(i = 1; i < argc; i++){\n    write(1, argv[i], strlen(argv[i]));\n    if(i + 1 < argc){\n      write(1, \" \", 1);\n    } else {\n      write(1, \"\\n\", 1);\n    }\n  }\n  exit(0);\n}\n"
  },
  {
    "path": "user/forktest.c",
    "content": "// Test that fork fails gracefully.\n// Tiny executable so that the limit can be filling the proc table.\n\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\n#define N  1000\n\nvoid\nprint(const char *s)\n{\n  write(1, s, strlen(s));\n}\n\nvoid\nforktest(void)\n{\n  int n, pid;\n\n  print(\"fork test\\n\");\n\n  for(n=0; n<N; n++){\n    pid = fork();\n    if(pid < 0)\n      break;\n    if(pid == 0)\n      exit(0);\n  }\n\n  if(n == N){\n    print(\"fork claimed to work N times!\\n\");\n    exit(1);\n  }\n\n  for(; n > 0; n--){\n    if(wait(0) < 0){\n      print(\"wait stopped early\\n\");\n      exit(1);\n    }\n  }\n\n  if(wait(0) != -1){\n    print(\"wait got too many\\n\");\n    exit(1);\n  }\n\n  print(\"fork test OK\\n\");\n}\n\nint\nmain(void)\n{\n  forktest();\n  exit(0);\n}\n"
  },
  {
    "path": "user/forphan.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\n// Create an orphaned file and check if test-xv6.py recovers it.\n\n#define BUFSZ 500\n\nchar buf[BUFSZ];\n\nint\nmain(int argc, char **argv)\n{\n  int fd = 0;\n  char *s = argv[0];\n  struct stat st;\n  char *ff = \"file0\";\n  \n  if ((fd = open(ff, O_CREATE|O_WRONLY)) < 0) {\n    printf(\"%s: open failed\\n\", s);\n    exit(1);\n  }\n  if(fstat(fd, &st) < 0){\n    fprintf(2, \"%s: cannot stat %s\\n\", s, \"ff\");\n    exit(1);\n  }\n  if (unlink(ff) < 0) {\n    printf(\"%s: unlink failed\\n\", s);\n    exit(1);\n  }\n  if (open(ff, O_RDONLY) != -1) {\n    printf(\"%s: open successed\\n\", s);\n    exit(1);\n  }\n  printf(\"wait for kill and reclaim %d\\n\", st.ino);\n  // sit around until killed\n  for(;;) pause(1000);\n}\n"
  },
  {
    "path": "user/grep.c",
    "content": "// Simple grep.  Only supports ^ . * $ operators.\n\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\nchar buf[1024];\nint match(char*, char*);\n\nvoid\ngrep(char *pattern, int fd)\n{\n  int n, m;\n  char *p, *q;\n\n  m = 0;\n  while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){\n    m += n;\n    buf[m] = '\\0';\n    p = buf;\n    while((q = strchr(p, '\\n')) != 0){\n      *q = 0;\n      if(match(pattern, p)){\n        *q = '\\n';\n        write(1, p, q+1 - p);\n      }\n      p = q+1;\n    }\n    if(m > 0){\n      m -= p - buf;\n      memmove(buf, p, m);\n    }\n  }\n}\n\nint\nmain(int argc, char *argv[])\n{\n  int fd, i;\n  char *pattern;\n\n  if(argc <= 1){\n    fprintf(2, \"usage: grep pattern [file ...]\\n\");\n    exit(1);\n  }\n  pattern = argv[1];\n\n  if(argc <= 2){\n    grep(pattern, 0);\n    exit(0);\n  }\n\n  for(i = 2; i < argc; i++){\n    if((fd = open(argv[i], O_RDONLY)) < 0){\n      printf(\"grep: cannot open %s\\n\", argv[i]);\n      exit(1);\n    }\n    grep(pattern, fd);\n    close(fd);\n  }\n  exit(0);\n}\n\n// Regexp matcher from Kernighan & Pike,\n// The Practice of Programming, Chapter 9, or\n// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html\n\nint matchhere(char*, char*);\nint matchstar(int, char*, char*);\n\nint\nmatch(char *re, char *text)\n{\n  if(re[0] == '^')\n    return matchhere(re+1, text);\n  do{  // must look at empty string\n    if(matchhere(re, text))\n      return 1;\n  }while(*text++ != '\\0');\n  return 0;\n}\n\n// matchhere: search for re at beginning of text\nint matchhere(char *re, char *text)\n{\n  if(re[0] == '\\0')\n    return 1;\n  if(re[1] == '*')\n    return matchstar(re[0], re+2, text);\n  if(re[0] == '$' && re[1] == '\\0')\n    return *text == '\\0';\n  if(*text!='\\0' && (re[0]=='.' || re[0]==*text))\n    return matchhere(re+1, text+1);\n  return 0;\n}\n\n// matchstar: search for c*re at beginning of text\nint matchstar(int c, char *re, char *text)\n{\n  do{  // a * matches zero or more instances\n    if(matchhere(re, text))\n      return 1;\n  }while(*text!='\\0' && (*text++==c || c=='.'));\n  return 0;\n}\n\n"
  },
  {
    "path": "user/grind.c",
    "content": "//\n// run random system calls in parallel forever.\n//\n\n#include \"kernel/param.h\"\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/fcntl.h\"\n#include \"kernel/syscall.h\"\n#include \"kernel/memlayout.h\"\n#include \"kernel/riscv.h\"\n\n// from FreeBSD.\nint\ndo_rand(unsigned long *ctx)\n{\n/*\n * Compute x = (7^5 * x) mod (2^31 - 1)\n * without overflowing 31 bits:\n *      (2^31 - 1) = 127773 * (7^5) + 2836\n * From \"Random number generators: good ones are hard to find\",\n * Park and Miller, Communications of the ACM, vol. 31, no. 10,\n * October 1988, p. 1195.\n */\n    long hi, lo, x;\n\n    /* Transform to [1, 0x7ffffffe] range. */\n    x = (*ctx % 0x7ffffffe) + 1;\n    hi = x / 127773;\n    lo = x % 127773;\n    x = 16807 * lo - 2836 * hi;\n    if (x < 0)\n        x += 0x7fffffff;\n    /* Transform to [0, 0x7ffffffd] range. */\n    x--;\n    *ctx = x;\n    return (x);\n}\n\nunsigned long rand_next = 1;\n\nint\nrand(void)\n{\n    return (do_rand(&rand_next));\n}\n\nvoid\ngo(int which_child)\n{\n  int fd = -1;\n  static char buf[999];\n  char *break0 = sbrk(0);\n  uint64 iters = 0;\n\n  mkdir(\"grindir\");\n  if(chdir(\"grindir\") != 0){\n    printf(\"grind: chdir grindir failed\\n\");\n    exit(1);\n  }\n  chdir(\"/\");\n  \n  while(1){\n    iters++;\n    if((iters % 500) == 0)\n      write(1, which_child?\"B\":\"A\", 1);\n    int what = rand() % 23;\n    if(what == 1){\n      close(open(\"grindir/../a\", O_CREATE|O_RDWR));\n    } else if(what == 2){\n      close(open(\"grindir/../grindir/../b\", O_CREATE|O_RDWR));\n    } else if(what == 3){\n      unlink(\"grindir/../a\");\n    } else if(what == 4){\n      if(chdir(\"grindir\") != 0){\n        printf(\"grind: chdir grindir failed\\n\");\n        exit(1);\n      }\n      unlink(\"../b\");\n      chdir(\"/\");\n    } else if(what == 5){\n      close(fd);\n      fd = open(\"/grindir/../a\", O_CREATE|O_RDWR);\n    } else if(what == 6){\n      close(fd);\n      fd = open(\"/./grindir/./../b\", O_CREATE|O_RDWR);\n    } else if(what == 7){\n      write(fd, buf, sizeof(buf));\n    } else if(what == 8){\n      read(fd, buf, sizeof(buf));\n    } else if(what == 9){\n      mkdir(\"grindir/../a\");\n      close(open(\"a/../a/./a\", O_CREATE|O_RDWR));\n      unlink(\"a/a\");\n    } else if(what == 10){\n      mkdir(\"/../b\");\n      close(open(\"grindir/../b/b\", O_CREATE|O_RDWR));\n      unlink(\"b/b\");\n    } else if(what == 11){\n      unlink(\"b\");\n      link(\"../grindir/./../a\", \"../b\");\n    } else if(what == 12){\n      unlink(\"../grindir/../a\");\n      link(\".././b\", \"/grindir/../a\");\n    } else if(what == 13){\n      int pid = fork();\n      if(pid == 0){\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      wait(0);\n    } else if(what == 14){\n      int pid = fork();\n      if(pid == 0){\n        fork();\n        fork();\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      wait(0);\n    } else if(what == 15){\n      sbrk(6011);\n    } else if(what == 16){\n      if(sbrk(0) > break0)\n        sbrk(-(sbrk(0) - break0));\n    } else if(what == 17){\n      int pid = fork();\n      if(pid == 0){\n        close(open(\"a\", O_CREATE|O_RDWR));\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      if(chdir(\"../grindir/..\") != 0){\n        printf(\"grind: chdir failed\\n\");\n        exit(1);\n      }\n      kill(pid);\n      wait(0);\n    } else if(what == 18){\n      int pid = fork();\n      if(pid == 0){\n        kill(getpid());\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      wait(0);\n    } else if(what == 19){\n      int fds[2];\n      if(pipe(fds) < 0){\n        printf(\"grind: pipe failed\\n\");\n        exit(1);\n      }\n      int pid = fork();\n      if(pid == 0){\n        fork();\n        fork();\n        if(write(fds[1], \"x\", 1) != 1)\n          printf(\"grind: pipe write failed\\n\");\n        char c;\n        if(read(fds[0], &c, 1) != 1)\n          printf(\"grind: pipe read failed\\n\");\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      close(fds[0]);\n      close(fds[1]);\n      wait(0);\n    } else if(what == 20){\n      int pid = fork();\n      if(pid == 0){\n        unlink(\"a\");\n        mkdir(\"a\");\n        chdir(\"a\");\n        unlink(\"../a\");\n        fd = open(\"x\", O_CREATE|O_RDWR);\n        unlink(\"x\");\n        exit(0);\n      } else if(pid < 0){\n        printf(\"grind: fork failed\\n\");\n        exit(1);\n      }\n      wait(0);\n    } else if(what == 21){\n      unlink(\"c\");\n      // should always succeed. check that there are free i-nodes,\n      // file descriptors, blocks.\n      int fd1 = open(\"c\", O_CREATE|O_RDWR);\n      if(fd1 < 0){\n        printf(\"grind: create c failed\\n\");\n        exit(1);\n      }\n      if(write(fd1, \"x\", 1) != 1){\n        printf(\"grind: write c failed\\n\");\n        exit(1);\n      }\n      struct stat st;\n      if(fstat(fd1, &st) != 0){\n        printf(\"grind: fstat failed\\n\");\n        exit(1);\n      }\n      if(st.size != 1){\n        printf(\"grind: fstat reports wrong size %d\\n\", (int)st.size);\n        exit(1);\n      }\n      if(st.ino > 200){\n        printf(\"grind: fstat reports crazy i-number %d\\n\", st.ino);\n        exit(1);\n      }\n      close(fd1);\n      unlink(\"c\");\n    } else if(what == 22){\n      // echo hi | cat\n      int aa[2], bb[2];\n      if(pipe(aa) < 0){\n        fprintf(2, \"grind: pipe failed\\n\");\n        exit(1);\n      }\n      if(pipe(bb) < 0){\n        fprintf(2, \"grind: pipe failed\\n\");\n        exit(1);\n      }\n      int pid1 = fork();\n      if(pid1 == 0){\n        close(bb[0]);\n        close(bb[1]);\n        close(aa[0]);\n        close(1);\n        if(dup(aa[1]) != 1){\n          fprintf(2, \"grind: dup failed\\n\");\n          exit(1);\n        }\n        close(aa[1]);\n        char *args[3] = { \"echo\", \"hi\", 0 };\n        exec(\"grindir/../echo\", args);\n        fprintf(2, \"grind: echo: not found\\n\");\n        exit(2);\n      } else if(pid1 < 0){\n        fprintf(2, \"grind: fork failed\\n\");\n        exit(3);\n      }\n      int pid2 = fork();\n      if(pid2 == 0){\n        close(aa[1]);\n        close(bb[0]);\n        close(0);\n        if(dup(aa[0]) != 0){\n          fprintf(2, \"grind: dup failed\\n\");\n          exit(4);\n        }\n        close(aa[0]);\n        close(1);\n        if(dup(bb[1]) != 1){\n          fprintf(2, \"grind: dup failed\\n\");\n          exit(5);\n        }\n        close(bb[1]);\n        char *args[2] = { \"cat\", 0 };\n        exec(\"/cat\", args);\n        fprintf(2, \"grind: cat: not found\\n\");\n        exit(6);\n      } else if(pid2 < 0){\n        fprintf(2, \"grind: fork failed\\n\");\n        exit(7);\n      }\n      close(aa[0]);\n      close(aa[1]);\n      close(bb[1]);\n      char buf[4] = { 0, 0, 0, 0 };\n      read(bb[0], buf+0, 1);\n      read(bb[0], buf+1, 1);\n      read(bb[0], buf+2, 1);\n      close(bb[0]);\n      int st1, st2;\n      wait(&st1);\n      wait(&st2);\n      if(st1 != 0 || st2 != 0 || strcmp(buf, \"hi\\n\") != 0){\n        printf(\"grind: exec pipeline failed %d %d \\\"%s\\\"\\n\", st1, st2, buf);\n        exit(1);\n      }\n    }\n  }\n}\n\nvoid\niter()\n{\n  unlink(\"a\");\n  unlink(\"b\");\n  \n  int pid1 = fork();\n  if(pid1 < 0){\n    printf(\"grind: fork failed\\n\");\n    exit(1);\n  }\n  if(pid1 == 0){\n    rand_next ^= 31;\n    go(0);\n    exit(0);\n  }\n\n  int pid2 = fork();\n  if(pid2 < 0){\n    printf(\"grind: fork failed\\n\");\n    exit(1);\n  }\n  if(pid2 == 0){\n    rand_next ^= 7177;\n    go(1);\n    exit(0);\n  }\n\n  int st1 = -1;\n  wait(&st1);\n  if(st1 != 0){\n    kill(pid1);\n    kill(pid2);\n  }\n  int st2 = -1;\n  wait(&st2);\n\n  exit(0);\n}\n\nint\nmain()\n{\n  while(1){\n    int pid = fork();\n    if(pid == 0){\n      iter();\n      exit(0);\n    }\n    if(pid > 0){\n      wait(0);\n    }\n    pause(20);\n    rand_next += 1;\n  }\n}\n"
  },
  {
    "path": "user/init.c",
    "content": "// init: The initial user-level program\n\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/spinlock.h\"\n#include \"kernel/sleeplock.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/file.h\"\n#include \"user/user.h\"\n#include \"kernel/fcntl.h\"\n\nchar *argv[] = { \"sh\", 0 };\n\nint\nmain(void)\n{\n  int pid, wpid;\n\n  if(open(\"console\", O_RDWR) < 0){\n    mknod(\"console\", CONSOLE, 0);\n    open(\"console\", O_RDWR);\n  }\n  dup(0);  // stdout\n  dup(0);  // stderr\n\n  for(;;){\n    printf(\"init: starting sh\\n\");\n    pid = fork();\n    if(pid < 0){\n      printf(\"init: fork failed\\n\");\n      exit(1);\n    }\n    if(pid == 0){\n      exec(\"sh\", argv);\n      printf(\"init: exec sh failed\\n\");\n      exit(1);\n    }\n\n    for(;;){\n      // this call to wait() returns if the shell exits,\n      // or if a parentless process exits.\n      wpid = wait((int *) 0);\n      if(wpid == pid){\n        // the shell exited; restart it.\n        break;\n      } else if(wpid < 0){\n        printf(\"init: wait returned an error\\n\");\n        exit(1);\n      } else {\n        // it was a parentless process; do nothing.\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "user/kill.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(int argc, char **argv)\n{\n  int i;\n\n  if(argc < 2){\n    fprintf(2, \"usage: kill pid...\\n\");\n    exit(1);\n  }\n  for(i=1; i<argc; i++)\n    kill(atoi(argv[i]));\n  exit(0);\n}\n"
  },
  {
    "path": "user/ln.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(int argc, char *argv[])\n{\n  if(argc != 3){\n    fprintf(2, \"Usage: ln old new\\n\");\n    exit(1);\n  }\n  if(link(argv[1], argv[2]) < 0)\n    fprintf(2, \"link %s %s: failed\\n\", argv[1], argv[2]);\n  exit(0);\n}\n"
  },
  {
    "path": "user/logstress.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\n// Stress xv6 logging system by having several processes writing\n// concurrently to their own file (e.g., logstress f1 f2 f3 f4)\n\n#define BUFSZ 500\n\nchar buf[BUFSZ];\n\nint\nmain(int argc, char **argv)\n{\n  int fd, n;\n  enum { N = 250, SZ=2000 };\n  \n  for (int i = 1; i < argc; i++){\n    int pid1 = fork();\n    if(pid1 < 0){\n      printf(\"%s: fork failed\\n\", argv[0]);\n      exit(1);\n    }\n    if(pid1 == 0) {\n      fd = open(argv[i], O_CREATE | O_RDWR);\n      if(fd < 0){\n        printf(\"%s: create %s failed\\n\", argv[0], argv[i]);\n        exit(1);\n      }\n      memset(buf, '0'+i, SZ);\n      for(i = 0; i < N; i++){\n        if((n = write(fd, buf, SZ)) != SZ){\n          printf(\"write failed %d\\n\", n);\n          exit(1);\n        }\n      }\n      exit(0);\n    }\n  }\n  int xstatus;\n  for(int i = 1; i < argc; i++){\n    wait(&xstatus);\n    if(xstatus != 0)\n      exit(xstatus);\n  }\n  return 0;\n}\n"
  },
  {
    "path": "user/ls.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/fcntl.h\"\n\nchar*\nfmtname(char *path)\n{\n  static char buf[DIRSIZ+1];\n  char *p;\n\n  // Find first character after last slash.\n  for(p=path+strlen(path); p >= path && *p != '/'; p--)\n    ;\n  p++;\n\n  // Return blank-padded name.\n  if(strlen(p) >= DIRSIZ)\n    return p;\n  memmove(buf, p, strlen(p));\n  memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));\n  buf[sizeof(buf)-1] = '\\0';\n  return buf;\n}\n\nvoid\nls(char *path)\n{\n  char buf[512], *p;\n  int fd;\n  struct dirent de;\n  struct stat st;\n\n  if((fd = open(path, O_RDONLY)) < 0){\n    fprintf(2, \"ls: cannot open %s\\n\", path);\n    return;\n  }\n\n  if(fstat(fd, &st) < 0){\n    fprintf(2, \"ls: cannot stat %s\\n\", path);\n    close(fd);\n    return;\n  }\n\n  switch(st.type){\n  case T_DEVICE:\n  case T_FILE:\n    printf(\"%s %d %d %d\\n\", fmtname(path), st.type, st.ino, (int) st.size);\n    break;\n\n  case T_DIR:\n    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){\n      printf(\"ls: path too long\\n\");\n      break;\n    }\n    strcpy(buf, path);\n    p = buf+strlen(buf);\n    *p++ = '/';\n    while(read(fd, &de, sizeof(de)) == sizeof(de)){\n      if(de.inum == 0)\n        continue;\n      memmove(p, de.name, DIRSIZ);\n      p[DIRSIZ] = 0;\n      if(stat(buf, &st) < 0){\n        printf(\"ls: cannot stat %s\\n\", buf);\n        continue;\n      }\n      printf(\"%s %d %d %d\\n\", fmtname(buf), st.type, st.ino, (int) st.size);\n    }\n    break;\n  }\n  close(fd);\n}\n\nint\nmain(int argc, char *argv[])\n{\n  int i;\n\n  if(argc < 2){\n    ls(\".\");\n    exit(0);\n  }\n  for(i=1; i<argc; i++)\n    ls(argv[i]);\n  exit(0);\n}\n"
  },
  {
    "path": "user/mkdir.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(int argc, char *argv[])\n{\n  int i;\n\n  if(argc < 2){\n    fprintf(2, \"Usage: mkdir files...\\n\");\n    exit(1);\n  }\n\n  for(i = 1; i < argc; i++){\n    if(mkdir(argv[i]) < 0){\n      fprintf(2, \"mkdir: %s failed to create\\n\", argv[i]);\n      break;\n    }\n  }\n\n  exit(0);\n}\n"
  },
  {
    "path": "user/printf.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\n#include <stdarg.h>\n\nstatic char digits[] = \"0123456789ABCDEF\";\n\nstatic void\nputc(int fd, char c)\n{\n  write(fd, &c, 1);\n}\n\nstatic void\nprintint(int fd, long long xx, int base, int sgn)\n{\n  char buf[20];\n  int i, neg;\n  unsigned long long x;\n\n  neg = 0;\n  if(sgn && xx < 0){\n    neg = 1;\n    x = -xx;\n  } else {\n    x = xx;\n  }\n\n  i = 0;\n  do{\n    buf[i++] = digits[x % base];\n  }while((x /= base) != 0);\n  if(neg)\n    buf[i++] = '-';\n\n  while(--i >= 0)\n    putc(fd, buf[i]);\n}\n\nstatic void\nprintptr(int fd, uint64 x) {\n  int i;\n  putc(fd, '0');\n  putc(fd, 'x');\n  for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)\n    putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);\n}\n\n// Print to the given fd. Only understands %d, %x, %p, %c, %s.\nvoid\nvprintf(int fd, const char *fmt, va_list ap)\n{\n  char *s;\n  int c0, c1, c2, i, state;\n\n  state = 0;\n  for(i = 0; fmt[i]; i++){\n    c0 = fmt[i] & 0xff;\n    if(state == 0){\n      if(c0 == '%'){\n        state = '%';\n      } else {\n        putc(fd, c0);\n      }\n    } else if(state == '%'){\n      c1 = c2 = 0;\n      if(c0) c1 = fmt[i+1] & 0xff;\n      if(c1) c2 = fmt[i+2] & 0xff;\n      if(c0 == 'd'){\n        printint(fd, va_arg(ap, int), 10, 1);\n      } else if(c0 == 'l' && c1 == 'd'){\n        printint(fd, va_arg(ap, uint64), 10, 1);\n        i += 1;\n      } else if(c0 == 'l' && c1 == 'l' && c2 == 'd'){\n        printint(fd, va_arg(ap, uint64), 10, 1);\n        i += 2;\n      } else if(c0 == 'u'){\n        printint(fd, va_arg(ap, uint32), 10, 0);\n      } else if(c0 == 'l' && c1 == 'u'){\n        printint(fd, va_arg(ap, uint64), 10, 0);\n        i += 1;\n      } else if(c0 == 'l' && c1 == 'l' && c2 == 'u'){\n        printint(fd, va_arg(ap, uint64), 10, 0);\n        i += 2;\n      } else if(c0 == 'x'){\n        printint(fd, va_arg(ap, uint32), 16, 0);\n      } else if(c0 == 'l' && c1 == 'x'){\n        printint(fd, va_arg(ap, uint64), 16, 0);\n        i += 1;\n      } else if(c0 == 'l' && c1 == 'l' && c2 == 'x'){\n        printint(fd, va_arg(ap, uint64), 16, 0);\n        i += 2;\n      } else if(c0 == 'p'){\n        printptr(fd, va_arg(ap, uint64));\n      } else if(c0 == 'c'){\n        putc(fd, va_arg(ap, uint32));\n      } else if(c0 == 's'){\n        if((s = va_arg(ap, char*)) == 0)\n          s = \"(null)\";\n        for(; *s; s++)\n          putc(fd, *s);\n      } else if(c0 == '%'){\n        putc(fd, '%');\n      } else {\n        // Unknown % sequence.  Print it to draw attention.\n        putc(fd, '%');\n        putc(fd, c0);\n      }\n\n      state = 0;\n    }\n  }\n}\n\nvoid\nfprintf(int fd, const char *fmt, ...)\n{\n  va_list ap;\n\n  va_start(ap, fmt);\n  vprintf(fd, fmt, ap);\n}\n\nvoid\nprintf(const char *fmt, ...)\n{\n  va_list ap;\n\n  va_start(ap, fmt);\n  vprintf(1, fmt, ap);\n}\n"
  },
  {
    "path": "user/rm.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(int argc, char *argv[])\n{\n  int i;\n\n  if(argc < 2){\n    fprintf(2, \"Usage: rm files...\\n\");\n    exit(1);\n  }\n\n  for(i = 1; i < argc; i++){\n    if(unlink(argv[i]) < 0){\n      fprintf(2, \"rm: %s failed to delete\\n\", argv[i]);\n      break;\n    }\n  }\n\n  exit(0);\n}\n"
  },
  {
    "path": "user/sh.c",
    "content": "// Shell.\n\n#include \"kernel/types.h\"\n#include \"user/user.h\"\n#include \"kernel/fcntl.h\"\n\n// Parsed command representation\n#define EXEC  1\n#define REDIR 2\n#define PIPE  3\n#define LIST  4\n#define BACK  5\n\n#define MAXARGS 10\n\nstruct cmd {\n  int type;\n};\n\nstruct execcmd {\n  int type;\n  char *argv[MAXARGS];\n  char *eargv[MAXARGS];\n};\n\nstruct redircmd {\n  int type;\n  struct cmd *cmd;\n  char *file;\n  char *efile;\n  int mode;\n  int fd;\n};\n\nstruct pipecmd {\n  int type;\n  struct cmd *left;\n  struct cmd *right;\n};\n\nstruct listcmd {\n  int type;\n  struct cmd *left;\n  struct cmd *right;\n};\n\nstruct backcmd {\n  int type;\n  struct cmd *cmd;\n};\n\nint fork1(void);  // Fork but panics on failure.\nvoid panic(char*);\nstruct cmd *parsecmd(char*);\nvoid runcmd(struct cmd*) __attribute__((noreturn));\n\n// Execute cmd.  Never returns.\nvoid\nruncmd(struct cmd *cmd)\n{\n  int p[2];\n  struct backcmd *bcmd;\n  struct execcmd *ecmd;\n  struct listcmd *lcmd;\n  struct pipecmd *pcmd;\n  struct redircmd *rcmd;\n\n  if(cmd == 0)\n    exit(1);\n\n  switch(cmd->type){\n  default:\n    panic(\"runcmd\");\n\n  case EXEC:\n    ecmd = (struct execcmd*)cmd;\n    if(ecmd->argv[0] == 0)\n      exit(1);\n    exec(ecmd->argv[0], ecmd->argv);\n    fprintf(2, \"exec %s failed\\n\", ecmd->argv[0]);\n    break;\n\n  case REDIR:\n    rcmd = (struct redircmd*)cmd;\n    close(rcmd->fd);\n    if(open(rcmd->file, rcmd->mode) < 0){\n      fprintf(2, \"open %s failed\\n\", rcmd->file);\n      exit(1);\n    }\n    runcmd(rcmd->cmd);\n    break;\n\n  case LIST:\n    lcmd = (struct listcmd*)cmd;\n    if(fork1() == 0)\n      runcmd(lcmd->left);\n    wait(0);\n    runcmd(lcmd->right);\n    break;\n\n  case PIPE:\n    pcmd = (struct pipecmd*)cmd;\n    if(pipe(p) < 0)\n      panic(\"pipe\");\n    if(fork1() == 0){\n      close(1);\n      dup(p[1]);\n      close(p[0]);\n      close(p[1]);\n      runcmd(pcmd->left);\n    }\n    if(fork1() == 0){\n      close(0);\n      dup(p[0]);\n      close(p[0]);\n      close(p[1]);\n      runcmd(pcmd->right);\n    }\n    close(p[0]);\n    close(p[1]);\n    wait(0);\n    wait(0);\n    break;\n\n  case BACK:\n    bcmd = (struct backcmd*)cmd;\n    if(fork1() == 0)\n      runcmd(bcmd->cmd);\n    break;\n  }\n  exit(0);\n}\n\nint\ngetcmd(char *buf, int nbuf)\n{\n  write(2, \"$ \", 2);\n  memset(buf, 0, nbuf);\n  gets(buf, nbuf);\n  if(buf[0] == 0) // EOF\n    return -1;\n  return 0;\n}\n\nint\nmain(void)\n{\n  static char buf[100];\n  int fd;\n\n  // Ensure that three file descriptors are open.\n  while((fd = open(\"console\", O_RDWR)) >= 0){\n    if(fd >= 3){\n      close(fd);\n      break;\n    }\n  }\n\n  // Read and run input commands.\n  while(getcmd(buf, sizeof(buf)) >= 0){\n    char *cmd = buf;\n    while (*cmd == ' ' || *cmd == '\\t')\n      cmd++;\n    if (*cmd == '\\n') // is a blank command\n      continue;\n    if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){\n      // Chdir must be called by the parent, not the child.\n      cmd[strlen(cmd)-1] = 0;  // chop \\n\n      if(chdir(cmd+3) < 0)\n        fprintf(2, \"cannot cd %s\\n\", cmd+3);\n    } else {\n      if(fork1() == 0)\n        runcmd(parsecmd(cmd));\n      wait(0);\n    }\n  }\n  exit(0);\n}\n\nvoid\npanic(char *s)\n{\n  fprintf(2, \"%s\\n\", s);\n  exit(1);\n}\n\nint\nfork1(void)\n{\n  int pid;\n\n  pid = fork();\n  if(pid == -1)\n    panic(\"fork\");\n  return pid;\n}\n\n//PAGEBREAK!\n// Constructors\n\nstruct cmd*\nexeccmd(void)\n{\n  struct execcmd *cmd;\n\n  cmd = malloc(sizeof(*cmd));\n  memset(cmd, 0, sizeof(*cmd));\n  cmd->type = EXEC;\n  return (struct cmd*)cmd;\n}\n\nstruct cmd*\nredircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)\n{\n  struct redircmd *cmd;\n\n  cmd = malloc(sizeof(*cmd));\n  memset(cmd, 0, sizeof(*cmd));\n  cmd->type = REDIR;\n  cmd->cmd = subcmd;\n  cmd->file = file;\n  cmd->efile = efile;\n  cmd->mode = mode;\n  cmd->fd = fd;\n  return (struct cmd*)cmd;\n}\n\nstruct cmd*\npipecmd(struct cmd *left, struct cmd *right)\n{\n  struct pipecmd *cmd;\n\n  cmd = malloc(sizeof(*cmd));\n  memset(cmd, 0, sizeof(*cmd));\n  cmd->type = PIPE;\n  cmd->left = left;\n  cmd->right = right;\n  return (struct cmd*)cmd;\n}\n\nstruct cmd*\nlistcmd(struct cmd *left, struct cmd *right)\n{\n  struct listcmd *cmd;\n\n  cmd = malloc(sizeof(*cmd));\n  memset(cmd, 0, sizeof(*cmd));\n  cmd->type = LIST;\n  cmd->left = left;\n  cmd->right = right;\n  return (struct cmd*)cmd;\n}\n\nstruct cmd*\nbackcmd(struct cmd *subcmd)\n{\n  struct backcmd *cmd;\n\n  cmd = malloc(sizeof(*cmd));\n  memset(cmd, 0, sizeof(*cmd));\n  cmd->type = BACK;\n  cmd->cmd = subcmd;\n  return (struct cmd*)cmd;\n}\n//PAGEBREAK!\n// Parsing\n\nchar whitespace[] = \" \\t\\r\\n\\v\";\nchar symbols[] = \"<|>&;()\";\n\nint\ngettoken(char **ps, char *es, char **q, char **eq)\n{\n  char *s;\n  int ret;\n\n  s = *ps;\n  while(s < es && strchr(whitespace, *s))\n    s++;\n  if(q)\n    *q = s;\n  ret = *s;\n  switch(*s){\n  case 0:\n    break;\n  case '|':\n  case '(':\n  case ')':\n  case ';':\n  case '&':\n  case '<':\n    s++;\n    break;\n  case '>':\n    s++;\n    if(*s == '>'){\n      ret = '+';\n      s++;\n    }\n    break;\n  default:\n    ret = 'a';\n    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))\n      s++;\n    break;\n  }\n  if(eq)\n    *eq = s;\n\n  while(s < es && strchr(whitespace, *s))\n    s++;\n  *ps = s;\n  return ret;\n}\n\nint\npeek(char **ps, char *es, char *toks)\n{\n  char *s;\n\n  s = *ps;\n  while(s < es && strchr(whitespace, *s))\n    s++;\n  *ps = s;\n  return *s && strchr(toks, *s);\n}\n\nstruct cmd *parseline(char**, char*);\nstruct cmd *parsepipe(char**, char*);\nstruct cmd *parseexec(char**, char*);\nstruct cmd *nulterminate(struct cmd*);\n\nstruct cmd*\nparsecmd(char *s)\n{\n  char *es;\n  struct cmd *cmd;\n\n  es = s + strlen(s);\n  cmd = parseline(&s, es);\n  peek(&s, es, \"\");\n  if(s != es){\n    fprintf(2, \"leftovers: %s\\n\", s);\n    panic(\"syntax\");\n  }\n  nulterminate(cmd);\n  return cmd;\n}\n\nstruct cmd*\nparseline(char **ps, char *es)\n{\n  struct cmd *cmd;\n\n  cmd = parsepipe(ps, es);\n  while(peek(ps, es, \"&\")){\n    gettoken(ps, es, 0, 0);\n    cmd = backcmd(cmd);\n  }\n  if(peek(ps, es, \";\")){\n    gettoken(ps, es, 0, 0);\n    cmd = listcmd(cmd, parseline(ps, es));\n  }\n  return cmd;\n}\n\nstruct cmd*\nparsepipe(char **ps, char *es)\n{\n  struct cmd *cmd;\n\n  cmd = parseexec(ps, es);\n  if(peek(ps, es, \"|\")){\n    gettoken(ps, es, 0, 0);\n    cmd = pipecmd(cmd, parsepipe(ps, es));\n  }\n  return cmd;\n}\n\nstruct cmd*\nparseredirs(struct cmd *cmd, char **ps, char *es)\n{\n  int tok;\n  char *q, *eq;\n\n  while(peek(ps, es, \"<>\")){\n    tok = gettoken(ps, es, 0, 0);\n    if(gettoken(ps, es, &q, &eq) != 'a')\n      panic(\"missing file for redirection\");\n    switch(tok){\n    case '<':\n      cmd = redircmd(cmd, q, eq, O_RDONLY, 0);\n      break;\n    case '>':\n      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);\n      break;\n    case '+':  // >>\n      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);\n      break;\n    }\n  }\n  return cmd;\n}\n\nstruct cmd*\nparseblock(char **ps, char *es)\n{\n  struct cmd *cmd;\n\n  if(!peek(ps, es, \"(\"))\n    panic(\"parseblock\");\n  gettoken(ps, es, 0, 0);\n  cmd = parseline(ps, es);\n  if(!peek(ps, es, \")\"))\n    panic(\"syntax - missing )\");\n  gettoken(ps, es, 0, 0);\n  cmd = parseredirs(cmd, ps, es);\n  return cmd;\n}\n\nstruct cmd*\nparseexec(char **ps, char *es)\n{\n  char *q, *eq;\n  int tok, argc;\n  struct execcmd *cmd;\n  struct cmd *ret;\n\n  if(peek(ps, es, \"(\"))\n    return parseblock(ps, es);\n\n  ret = execcmd();\n  cmd = (struct execcmd*)ret;\n\n  argc = 0;\n  ret = parseredirs(ret, ps, es);\n  while(!peek(ps, es, \"|)&;\")){\n    if((tok=gettoken(ps, es, &q, &eq)) == 0)\n      break;\n    if(tok != 'a')\n      panic(\"syntax\");\n    cmd->argv[argc] = q;\n    cmd->eargv[argc] = eq;\n    argc++;\n    if(argc >= MAXARGS)\n      panic(\"too many args\");\n    ret = parseredirs(ret, ps, es);\n  }\n  cmd->argv[argc] = 0;\n  cmd->eargv[argc] = 0;\n  return ret;\n}\n\n// NUL-terminate all the counted strings.\nstruct cmd*\nnulterminate(struct cmd *cmd)\n{\n  int i;\n  struct backcmd *bcmd;\n  struct execcmd *ecmd;\n  struct listcmd *lcmd;\n  struct pipecmd *pcmd;\n  struct redircmd *rcmd;\n\n  if(cmd == 0)\n    return 0;\n\n  switch(cmd->type){\n  case EXEC:\n    ecmd = (struct execcmd*)cmd;\n    for(i=0; ecmd->argv[i]; i++)\n      *ecmd->eargv[i] = 0;\n    break;\n\n  case REDIR:\n    rcmd = (struct redircmd*)cmd;\n    nulterminate(rcmd->cmd);\n    *rcmd->efile = 0;\n    break;\n\n  case PIPE:\n    pcmd = (struct pipecmd*)cmd;\n    nulterminate(pcmd->left);\n    nulterminate(pcmd->right);\n    break;\n\n  case LIST:\n    lcmd = (struct listcmd*)cmd;\n    nulterminate(lcmd->left);\n    nulterminate(lcmd->right);\n    break;\n\n  case BACK:\n    bcmd = (struct backcmd*)cmd;\n    nulterminate(bcmd->cmd);\n    break;\n  }\n  return cmd;\n}\n"
  },
  {
    "path": "user/stressfs.c",
    "content": "// Demonstrate that moving the \"acquire\" in iderw after the loop that\n// appends to the idequeue results in a race.\n\n// For this to work, you should also add a spin within iderw's\n// idequeue traversal loop.  Adding the following demonstrated a panic\n// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU:\n//    for (i = 0; i < 40000; i++)\n//      asm volatile(\"\");\n\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/fcntl.h\"\n\nint\nmain(int argc, char *argv[])\n{\n  int fd, i;\n  char path[] = \"stressfs0\";\n  char data[512];\n\n  printf(\"stressfs starting\\n\");\n  memset(data, 'a', sizeof(data));\n\n  for(i = 0; i < 4; i++)\n    if(fork() > 0)\n      break;\n\n  printf(\"write %d\\n\", i);\n\n  path[8] += i;\n  fd = open(path, O_CREATE | O_RDWR);\n  for(i = 0; i < 20; i++)\n//    printf(fd, \"%d\\n\", i);\n    write(fd, data, sizeof(data));\n  close(fd);\n\n  printf(\"read\\n\");\n\n  fd = open(path, O_RDONLY);\n  for (i = 0; i < 20; i++)\n    read(fd, data, sizeof(data));\n  close(fd);\n\n  wait(0);\n\n  exit(0);\n}\n"
  },
  {
    "path": "user/ulib.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"kernel/riscv.h\"\n#include \"kernel/vm.h\"\n#include \"user/user.h\"\n\n//\n// wrapper so that it's OK if main() does not call exit().\n//\nvoid\nstart(int argc, char **argv)\n{\n  int r;\n  extern int main(int argc, char **argv);\n  r = main(argc, argv);\n  exit(r);\n}\n\nchar*\nstrcpy(char *s, const char *t)\n{\n  char *os;\n\n  os = s;\n  while((*s++ = *t++) != 0)\n    ;\n  return os;\n}\n\nint\nstrcmp(const char *p, const char *q)\n{\n  while(*p && *p == *q)\n    p++, q++;\n  return (uchar)*p - (uchar)*q;\n}\n\nuint\nstrlen(const char *s)\n{\n  int n;\n\n  for(n = 0; s[n]; n++)\n    ;\n  return n;\n}\n\nvoid*\nmemset(void *dst, int c, uint n)\n{\n  char *cdst = (char *) dst;\n  int i;\n  for(i = 0; i < n; i++){\n    cdst[i] = c;\n  }\n  return dst;\n}\n\nchar*\nstrchr(const char *s, char c)\n{\n  for(; *s; s++)\n    if(*s == c)\n      return (char*)s;\n  return 0;\n}\n\nchar*\ngets(char *buf, int max)\n{\n  int i, cc;\n  char c;\n\n  for(i=0; i+1 < max; ){\n    cc = read(0, &c, 1);\n    if(cc < 1)\n      break;\n    buf[i++] = c;\n    if(c == '\\n' || c == '\\r')\n      break;\n  }\n  buf[i] = '\\0';\n  return buf;\n}\n\nint\nstat(const char *n, struct stat *st)\n{\n  int fd;\n  int r;\n\n  fd = open(n, O_RDONLY);\n  if(fd < 0)\n    return -1;\n  r = fstat(fd, st);\n  close(fd);\n  return r;\n}\n\nint\natoi(const char *s)\n{\n  int n;\n\n  n = 0;\n  while('0' <= *s && *s <= '9')\n    n = n*10 + *s++ - '0';\n  return n;\n}\n\nvoid*\nmemmove(void *vdst, const void *vsrc, int n)\n{\n  char *dst;\n  const char *src;\n\n  dst = vdst;\n  src = vsrc;\n  if (src > dst) {\n    while(n-- > 0)\n      *dst++ = *src++;\n  } else {\n    dst += n;\n    src += n;\n    while(n-- > 0)\n      *--dst = *--src;\n  }\n  return vdst;\n}\n\nint\nmemcmp(const void *s1, const void *s2, uint n)\n{\n  const char *p1 = s1, *p2 = s2;\n  while (n-- > 0) {\n    if (*p1 != *p2) {\n      return *p1 - *p2;\n    }\n    p1++;\n    p2++;\n  }\n  return 0;\n}\n\nvoid *\nmemcpy(void *dst, const void *src, uint n)\n{\n  return memmove(dst, src, n);\n}\n\nchar *\nsbrk(int n) {\n  return sys_sbrk(n, SBRK_EAGER);\n}\n\nchar *\nsbrklazy(int n) {\n  return sys_sbrk(n, SBRK_LAZY);\n}\n\n"
  },
  {
    "path": "user/umalloc.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n#include \"kernel/param.h\"\n\n// Memory allocator by Kernighan and Ritchie,\n// The C programming Language, 2nd ed.  Section 8.7.\n\ntypedef long Align;\n\nunion header {\n  struct {\n    union header *ptr;\n    uint size;\n  } s;\n  Align x;\n};\n\ntypedef union header Header;\n\nstatic Header base;\nstatic Header *freep;\n\nvoid\nfree(void *ap)\n{\n  Header *bp, *p;\n\n  bp = (Header*)ap - 1;\n  for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)\n    if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))\n      break;\n  if(bp + bp->s.size == p->s.ptr){\n    bp->s.size += p->s.ptr->s.size;\n    bp->s.ptr = p->s.ptr->s.ptr;\n  } else\n    bp->s.ptr = p->s.ptr;\n  if(p + p->s.size == bp){\n    p->s.size += bp->s.size;\n    p->s.ptr = bp->s.ptr;\n  } else\n    p->s.ptr = bp;\n  freep = p;\n}\n\nstatic Header*\nmorecore(uint nu)\n{\n  char *p;\n  Header *hp;\n\n  if(nu < 4096)\n    nu = 4096;\n  p = sbrk(nu * sizeof(Header));\n  if(p == SBRK_ERROR)\n    return 0;\n  hp = (Header*)p;\n  hp->s.size = nu;\n  free((void*)(hp + 1));\n  return freep;\n}\n\nvoid*\nmalloc(uint nbytes)\n{\n  Header *p, *prevp;\n  uint nunits;\n\n  nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1;\n  if((prevp = freep) == 0){\n    base.s.ptr = freep = prevp = &base;\n    base.s.size = 0;\n  }\n  for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){\n    if(p->s.size >= nunits){\n      if(p->s.size == nunits)\n        prevp->s.ptr = p->s.ptr;\n      else {\n        p->s.size -= nunits;\n        p += p->s.size;\n        p->s.size = nunits;\n      }\n      freep = prevp;\n      return (void*)(p + 1);\n    }\n    if(p == freep)\n      if((p = morecore(nunits)) == 0)\n        return 0;\n  }\n}\n"
  },
  {
    "path": "user/user.h",
    "content": "#define SBRK_ERROR ((char *)-1)\n\nstruct stat;\n\n// system calls\nint fork(void);\nint exit(int) __attribute__((noreturn));\nint wait(int*);\nint pipe(int*);\nint write(int, const void*, int);\nint read(int, void*, int);\nint close(int);\nint kill(int);\nint exec(const char*, char**);\nint open(const char*, int);\nint mknod(const char*, short, short);\nint unlink(const char*);\nint fstat(int fd, struct stat*);\nint link(const char*, const char*);\nint mkdir(const char*);\nint chdir(const char*);\nint dup(int);\nint getpid(void);\nchar* sys_sbrk(int,int);\nint pause(int);\nint uptime(void);\n\n// ulib.c\nint stat(const char*, struct stat*);\nchar* strcpy(char*, const char*);\nvoid *memmove(void*, const void*, int);\nchar* strchr(const char*, char c);\nint strcmp(const char*, const char*);\nchar* gets(char*, int max);\nuint strlen(const char*);\nvoid* memset(void*, int, uint);\nint atoi(const char*);\nint memcmp(const void *, const void *, uint);\nvoid *memcpy(void *, const void *, uint);\nchar* sbrk(int);\nchar* sbrklazy(int);\n\n// printf.c\nvoid fprintf(int, const char*, ...) __attribute__ ((format (printf, 2, 3)));\nvoid printf(const char*, ...) __attribute__ ((format (printf, 1, 2)));\n\n// umalloc.c\nvoid* malloc(uint);\nvoid free(void*);\n"
  },
  {
    "path": "user/user.ld",
    "content": "OUTPUT_ARCH( \"riscv\" )\n\nSECTIONS\n{\n . = 0x0;\n \n  .text : {\n    *(.text .text.*)\n  }\n\n  .rodata : {\n    . = ALIGN(16);\n    *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */\n    . = ALIGN(16);\n    *(.rodata .rodata.*)\n  }\n\n  .eh_frame : {\n       *(.eh_frame)\n       *(.eh_frame.*)\n   }\n\n  . = ALIGN(0x1000);\n  .data : {\n    . = ALIGN(16);\n    *(.sdata .sdata.*) /* do not need to distinguish this from .data */\n    . = ALIGN(16);\n    *(.data .data.*)\n  }\n\n  .bss : {\n    . = ALIGN(16);\n    *(.sbss .sbss.*) /* do not need to distinguish this from .bss */\n    . = ALIGN(16);\n    *(.bss .bss.*)\n  }\n\n  PROVIDE(end = .);\n}\n"
  },
  {
    "path": "user/usertests.c",
    "content": "#include \"kernel/param.h\"\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n#include \"kernel/fs.h\"\n#include \"kernel/fcntl.h\"\n#include \"kernel/syscall.h\"\n#include \"kernel/memlayout.h\"\n#include \"kernel/riscv.h\"\n\n//\n// Tests xv6 system calls.  usertests without arguments runs them all\n// and usertests <name> runs <name> test. The test runner creates for\n// each test a process and based on the exit status of the process,\n// the test runner reports \"OK\" or \"FAILED\".  Some tests result in\n// kernel printing usertrap messages, which can be ignored if test\n// prints \"OK\".\n//\n\n#define BUFSZ  ((MAXOPBLOCKS+2)*BSIZE)\n\nchar buf[BUFSZ];\n\n//\n// Section with tests that run fairly quickly.  Use -q if you want to\n// run just those.  Without -q usertests also runs the ones that take a\n// fair amount of time.\n//\n\n// what if you pass ridiculous pointers to system calls\n// that read user memory with copyin?\nvoid\ncopyin(char *s)\n{\n  uint64 addrs[] = { 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000,\n                     0xffffffffffffffff };\n\n  for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){\n    uint64 addr = addrs[ai];\n    \n    int fd = open(\"copyin1\", O_CREATE|O_WRONLY);\n    if(fd < 0){\n      printf(\"open(copyin1) failed\\n\");\n      exit(1);\n    }\n    int n = write(fd, (void*)addr, 8192);\n    if(n >= 0){\n      printf(\"write(fd, %p, 8192) returned %d, not -1\\n\", (void*)addr, n);\n      exit(1);\n    }\n    close(fd);\n    unlink(\"copyin1\");\n    \n    n = write(1, (char*)addr, 8192);\n    if(n > 0){\n      printf(\"write(1, %p, 8192) returned %d, not -1 or 0\\n\", (void*)addr, n);\n      exit(1);\n    }\n    \n    int fds[2];\n    if(pipe(fds) < 0){\n      printf(\"pipe() failed\\n\");\n      exit(1);\n    }\n    n = write(fds[1], (char*)addr, 8192);\n    if(n > 0){\n      printf(\"write(pipe, %p, 8192) returned %d, not -1 or 0\\n\", (void*)addr, n);\n      exit(1);\n    }\n    close(fds[0]);\n    close(fds[1]);\n  }\n}\n\n// what if you pass ridiculous pointers to system calls\n// that write user memory with copyout?\nvoid\ncopyout(char *s)\n{\n  uint64 addrs[] = { 0LL, 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000,\n                     0xffffffffffffffff };\n\n  for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){\n    uint64 addr = addrs[ai];\n\n    int fd = open(\"README\", 0);\n    if(fd < 0){\n      printf(\"open(README) failed\\n\");\n      exit(1);\n    }\n    int n = read(fd, (void*)addr, 8192);\n    if(n > 0){\n      printf(\"read(fd, %p, 8192) returned %d, not -1 or 0\\n\", (void*)addr, n);\n      exit(1);\n    }\n    close(fd);\n\n    int fds[2];\n    if(pipe(fds) < 0){\n      printf(\"pipe() failed\\n\");\n      exit(1);\n    }\n    n = write(fds[1], \"x\", 1);\n    if(n != 1){\n      printf(\"pipe write failed\\n\");\n      exit(1);\n    }\n    n = read(fds[0], (void*)addr, 8192);\n    if(n > 0){\n      printf(\"read(pipe, %p, 8192) returned %d, not -1 or 0\\n\", (void*)addr, n);\n      exit(1);\n    }\n    close(fds[0]);\n    close(fds[1]);\n  }\n}\n\n// what if you pass ridiculous string pointers to system calls?\nvoid\ncopyinstr1(char *s)\n{\n  uint64 addrs[] = { 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000,\n                     0xffffffffffffffff };\n\n  for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){\n    uint64 addr = addrs[ai];\n\n    int fd = open((char *)addr, O_CREATE|O_WRONLY);\n    if(fd >= 0){\n      printf(\"open(%p) returned %d, not -1\\n\", (void*)addr, fd);\n      exit(1);\n    }\n  }\n}\n\n// what if a string system call argument is exactly the size\n// of the kernel buffer it is copied into, so that the null\n// would fall just beyond the end of the kernel buffer?\nvoid\ncopyinstr2(char *s)\n{\n  char b[MAXPATH+1];\n\n  for(int i = 0; i < MAXPATH; i++)\n    b[i] = 'x';\n  b[MAXPATH] = '\\0';\n  \n  int ret = unlink(b);\n  if(ret != -1){\n    printf(\"unlink(%s) returned %d, not -1\\n\", b, ret);\n    exit(1);\n  }\n\n  int fd = open(b, O_CREATE | O_WRONLY);\n  if(fd != -1){\n    printf(\"open(%s) returned %d, not -1\\n\", b, fd);\n    exit(1);\n  }\n\n  ret = link(b, b);\n  if(ret != -1){\n    printf(\"link(%s, %s) returned %d, not -1\\n\", b, b, ret);\n    exit(1);\n  }\n\n  char *args[] = { \"xx\", 0 };\n  ret = exec(b, args);\n  if(ret != -1){\n    printf(\"exec(%s) returned %d, not -1\\n\", b, fd);\n    exit(1);\n  }\n\n  int pid = fork();\n  if(pid < 0){\n    printf(\"fork failed\\n\");\n    exit(1);\n  }\n  if(pid == 0){\n    static char big[PGSIZE+1];\n    for(int i = 0; i < PGSIZE; i++)\n      big[i] = 'x';\n    big[PGSIZE] = '\\0';\n    char *args2[] = { big, big, big, 0 };\n    ret = exec(\"echo\", args2);\n    if(ret != -1){\n      printf(\"exec(echo, BIG) returned %d, not -1\\n\", fd);\n      exit(1);\n    }\n    exit(747); // OK\n  }\n\n  int st = 0;\n  wait(&st);\n  if(st != 747){\n    printf(\"exec(echo, BIG) succeeded, should have failed\\n\");\n    exit(1);\n  }\n}\n\n// what if a string argument crosses over the end of last user page?\nvoid\ncopyinstr3(char *s)\n{\n  sbrk(8192);\n  uint64 top = (uint64) sbrk(0);\n  if((top % PGSIZE) != 0){\n    sbrk(PGSIZE - (top % PGSIZE));\n  }\n  top = (uint64) sbrk(0);\n  if(top % PGSIZE){\n    printf(\"oops\\n\");\n    exit(1);\n  }\n\n  char *b = (char *) (top - 1);\n  *b = 'x';\n\n  int ret = unlink(b);\n  if(ret != -1){\n    printf(\"unlink(%s) returned %d, not -1\\n\", b, ret);\n    exit(1);\n  }\n\n  int fd = open(b, O_CREATE | O_WRONLY);\n  if(fd != -1){\n    printf(\"open(%s) returned %d, not -1\\n\", b, fd);\n    exit(1);\n  }\n\n  ret = link(b, b);\n  if(ret != -1){\n    printf(\"link(%s, %s) returned %d, not -1\\n\", b, b, ret);\n    exit(1);\n  }\n\n  char *args[] = { \"xx\", 0 };\n  ret = exec(b, args);\n  if(ret != -1){\n    printf(\"exec(%s) returned %d, not -1\\n\", b, fd);\n    exit(1);\n  }\n}\n\n// See if the kernel refuses to read/write user memory that the\n// application doesn't have anymore, because it returned it.\nvoid\nrwsbrk(char *s)\n{\n  int fd, n;\n  \n  uint64 a = (uint64) sbrk(8192);\n\n  if(a == (uint64) SBRK_ERROR) {\n    printf(\"sbrk(rwsbrk) failed\\n\");\n    exit(1);\n  }\n  \n  if (sbrk(-8192) == SBRK_ERROR) {\n    printf(\"sbrk(rwsbrk) shrink failed\\n\");\n    exit(1);\n  }\n\n  fd = open(\"rwsbrk\", O_CREATE|O_WRONLY);\n  if(fd < 0){\n    printf(\"open(rwsbrk) failed\\n\");\n    exit(1);\n  }\n  n = write(fd, (void*)(a+PGSIZE), 1024);\n  if(n >= 0){\n    printf(\"write(fd, %p, 1024) returned %d, not -1\\n\", (void*)a+PGSIZE, n);\n    exit(1);\n  }\n  close(fd);\n  unlink(\"rwsbrk\");\n\n  fd = open(\"README\", O_RDONLY);\n  if(fd < 0){\n    printf(\"open(README) failed\\n\");\n    exit(1);\n  }\n  n = read(fd, (void*)(a+PGSIZE), 10);\n  if(n >= 0){\n    printf(\"read(fd, %p, 10) returned %d, not -1\\n\", (void*)a+PGSIZE, n);\n    exit(1);\n  }\n  close(fd);\n  \n  exit(0);\n}\n\n// test O_TRUNC.\nvoid\ntruncate1(char *s)\n{\n  char buf[32];\n  \n  unlink(\"truncfile\");\n  int fd1 = open(\"truncfile\", O_CREATE|O_WRONLY|O_TRUNC);\n  write(fd1, \"abcd\", 4);\n  close(fd1);\n\n  int fd2 = open(\"truncfile\", O_RDONLY);\n  int n = read(fd2, buf, sizeof(buf));\n  if(n != 4){\n    printf(\"%s: read %d bytes, wanted 4\\n\", s, n);\n    exit(1);\n  }\n\n  fd1 = open(\"truncfile\", O_WRONLY|O_TRUNC);\n\n  int fd3 = open(\"truncfile\", O_RDONLY);\n  n = read(fd3, buf, sizeof(buf));\n  if(n != 0){\n    printf(\"aaa fd3=%d\\n\", fd3);\n    printf(\"%s: read %d bytes, wanted 0\\n\", s, n);\n    exit(1);\n  }\n\n  n = read(fd2, buf, sizeof(buf));\n  if(n != 0){\n    printf(\"bbb fd2=%d\\n\", fd2);\n    printf(\"%s: read %d bytes, wanted 0\\n\", s, n);\n    exit(1);\n  }\n  \n  write(fd1, \"abcdef\", 6);\n\n  n = read(fd3, buf, sizeof(buf));\n  if(n != 6){\n    printf(\"%s: read %d bytes, wanted 6\\n\", s, n);\n    exit(1);\n  }\n\n  n = read(fd2, buf, sizeof(buf));\n  if(n != 2){\n    printf(\"%s: read %d bytes, wanted 2\\n\", s, n);\n    exit(1);\n  }\n\n  unlink(\"truncfile\");\n\n  close(fd1);\n  close(fd2);\n  close(fd3);\n}\n\n// write to an open FD whose file has just been truncated.\n// this causes a write at an offset beyond the end of the file.\n// such writes fail on xv6 (unlike POSIX) but at least\n// they don't crash.\nvoid\ntruncate2(char *s)\n{\n  unlink(\"truncfile\");\n\n  int fd1 = open(\"truncfile\", O_CREATE|O_TRUNC|O_WRONLY);\n  write(fd1, \"abcd\", 4);\n\n  int fd2 = open(\"truncfile\", O_TRUNC|O_WRONLY);\n\n  int n = write(fd1, \"x\", 1);\n  if(n != -1){\n    printf(\"%s: write returned %d, expected -1\\n\", s, n);\n    exit(1);\n  }\n\n  unlink(\"truncfile\");\n  close(fd1);\n  close(fd2);\n}\n\nvoid\ntruncate3(char *s)\n{\n  int pid, xstatus;\n\n  close(open(\"truncfile\", O_CREATE|O_TRUNC|O_WRONLY));\n  \n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n\n  if(pid == 0){\n    for(int i = 0; i < 100; i++){\n      char buf[32];\n      int fd = open(\"truncfile\", O_WRONLY);\n      if(fd < 0){\n        printf(\"%s: open failed\\n\", s);\n        exit(1);\n      }\n      int n = write(fd, \"1234567890\", 10);\n      if(n != 10){\n        printf(\"%s: write got %d, expected 10\\n\", s, n);\n        exit(1);\n      }\n      close(fd);\n      fd = open(\"truncfile\", O_RDONLY);\n      read(fd, buf, sizeof(buf));\n      close(fd);\n    }\n    exit(0);\n  }\n\n  for(int i = 0; i < 150; i++){\n    int fd = open(\"truncfile\", O_CREATE|O_WRONLY|O_TRUNC);\n    if(fd < 0){\n      printf(\"%s: open failed\\n\", s);\n      exit(1);\n    }\n    int n = write(fd, \"xxx\", 3);\n    if(n != 3){\n      printf(\"%s: write got %d, expected 3\\n\", s, n);\n      exit(1);\n    }\n    close(fd);\n  }\n\n  wait(&xstatus);\n  unlink(\"truncfile\");\n  exit(xstatus);\n}\n  \n\n// does chdir() call iput(p->cwd) in a transaction?\nvoid\niputtest(char *s)\n{\n  if(mkdir(\"iputdir\") < 0){\n    printf(\"%s: mkdir failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"iputdir\") < 0){\n    printf(\"%s: chdir iputdir failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"../iputdir\") < 0){\n    printf(\"%s: unlink ../iputdir failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"/\") < 0){\n    printf(\"%s: chdir / failed\\n\", s);\n    exit(1);\n  }\n}\n\n// does exit() call iput(p->cwd) in a transaction?\nvoid\nexitiputtest(char *s)\n{\n  int pid, xstatus;\n\n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n  if(pid == 0){\n    if(mkdir(\"iputdir\") < 0){\n      printf(\"%s: mkdir failed\\n\", s);\n      exit(1);\n    }\n    if(chdir(\"iputdir\") < 0){\n      printf(\"%s: child chdir failed\\n\", s);\n      exit(1);\n    }\n    if(unlink(\"../iputdir\") < 0){\n      printf(\"%s: unlink ../iputdir failed\\n\", s);\n      exit(1);\n    }\n    exit(0);\n  }\n  wait(&xstatus);\n  exit(xstatus);\n}\n\n// does the error path in open() for attempt to write a\n// directory call iput() in a transaction?\n// needs a hacked kernel that pauses just after the namei()\n// call in sys_open():\n//    if((ip = namei(path)) == 0)\n//      return -1;\n//    {\n//      int i;\n//      for(i = 0; i < 10000; i++)\n//        yield();\n//    }\nvoid\nopeniputtest(char *s)\n{\n  int pid, xstatus;\n\n  if(mkdir(\"oidir\") < 0){\n    printf(\"%s: mkdir oidir failed\\n\", s);\n    exit(1);\n  }\n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n  if(pid == 0){\n    int fd = open(\"oidir\", O_RDWR);\n    if(fd >= 0){\n      printf(\"%s: open directory for write succeeded\\n\", s);\n      exit(1);\n    }\n    exit(0);\n  }\n  pause(1);\n  if(unlink(\"oidir\") != 0){\n    printf(\"%s: unlink failed\\n\", s);\n    exit(1);\n  }\n  wait(&xstatus);\n  exit(xstatus);\n}\n\n// simple file system tests\n\nvoid\nopentest(char *s)\n{\n  int fd;\n\n  fd = open(\"echo\", 0);\n  if(fd < 0){\n    printf(\"%s: open echo failed!\\n\", s);\n    exit(1);\n  }\n  close(fd);\n  fd = open(\"doesnotexist\", 0);\n  if(fd >= 0){\n    printf(\"%s: open doesnotexist succeeded!\\n\", s);\n    exit(1);\n  }\n}\n\nvoid\nwritetest(char *s)\n{\n  int fd;\n  int i;\n  enum { N=100, SZ=10 };\n  \n  fd = open(\"small\", O_CREATE|O_RDWR);\n  if(fd < 0){\n    printf(\"%s: error: creat small failed!\\n\", s);\n    exit(1);\n  }\n  for(i = 0; i < N; i++){\n    if(write(fd, \"aaaaaaaaaa\", SZ) != SZ){\n      printf(\"%s: error: write aa %d new file failed\\n\", s, i);\n      exit(1);\n    }\n    if(write(fd, \"bbbbbbbbbb\", SZ) != SZ){\n      printf(\"%s: error: write bb %d new file failed\\n\", s, i);\n      exit(1);\n    }\n  }\n  close(fd);\n  fd = open(\"small\", O_RDONLY);\n  if(fd < 0){\n    printf(\"%s: error: open small failed!\\n\", s);\n    exit(1);\n  }\n  i = read(fd, buf, N*SZ*2);\n  if(i != N*SZ*2){\n    printf(\"%s: read failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(unlink(\"small\") < 0){\n    printf(\"%s: unlink small failed\\n\", s);\n    exit(1);\n  }\n}\n\nvoid\nwritebig(char *s)\n{\n  int i, fd, n;\n\n  fd = open(\"big\", O_CREATE|O_RDWR);\n  if(fd < 0){\n    printf(\"%s: error: creat big failed!\\n\", s);\n    exit(1);\n  }\n\n  for(i = 0; i < MAXFILE; i++){\n    ((int*)buf)[0] = i;\n    if(write(fd, buf, BSIZE) != BSIZE){\n      printf(\"%s: error: write big file failed i=%d\\n\", s, i);\n      exit(1);\n    }\n  }\n\n  close(fd);\n\n  fd = open(\"big\", O_RDONLY);\n  if(fd < 0){\n    printf(\"%s: error: open big failed!\\n\", s);\n    exit(1);\n  }\n\n  n = 0;\n  for(;;){\n    i = read(fd, buf, BSIZE);\n    if(i == 0){\n      if(n != MAXFILE){\n        printf(\"%s: read only %d blocks from big\", s, n);\n        exit(1);\n      }\n      break;\n    } else if(i != BSIZE){\n      printf(\"%s: read failed %d\\n\", s, i);\n      exit(1);\n    }\n    if(((int*)buf)[0] != n){\n      printf(\"%s: read content of block %d is %d\\n\", s,\n             n, ((int*)buf)[0]);\n      exit(1);\n    }\n    n++;\n  }\n  close(fd);\n  if(unlink(\"big\") < 0){\n    printf(\"%s: unlink big failed\\n\", s);\n    exit(1);\n  }\n}\n\n// many creates, followed by unlink test\nvoid\ncreatetest(char *s)\n{\n  int i, fd;\n  enum { N=52 };\n\n  char name[3];\n  name[0] = 'a';\n  name[2] = '\\0';\n  for(i = 0; i < N; i++){\n    name[1] = '0' + i;\n    fd = open(name, O_CREATE|O_RDWR);\n    close(fd);\n  }\n  name[0] = 'a';\n  name[2] = '\\0';\n  for(i = 0; i < N; i++){\n    name[1] = '0' + i;\n    unlink(name);\n  }\n}\n\nvoid dirtest(char *s)\n{\n  if(mkdir(\"dir0\") < 0){\n    printf(\"%s: mkdir failed\\n\", s);\n    exit(1);\n  }\n\n  if(chdir(\"dir0\") < 0){\n    printf(\"%s: chdir dir0 failed\\n\", s);\n    exit(1);\n  }\n\n  if(chdir(\"..\") < 0){\n    printf(\"%s: chdir .. failed\\n\", s);\n    exit(1);\n  }\n\n  if(unlink(\"dir0\") < 0){\n    printf(\"%s: unlink dir0 failed\\n\", s);\n    exit(1);\n  }\n}\n\nvoid\nexectest(char *s)\n{\n  int fd, xstatus, pid;\n  char *echoargv[] = { \"echo\", \"OK\", 0 };\n  char buf[3];\n\n  unlink(\"echo-ok\");\n  pid = fork();\n  if(pid < 0) {\n     printf(\"%s: fork failed\\n\", s);\n     exit(1);\n  }\n  if(pid == 0) {\n    close(1);\n    fd = open(\"echo-ok\", O_CREATE|O_WRONLY);\n    if(fd < 0) {\n      printf(\"%s: create failed\\n\", s);\n      exit(1);\n    }\n    if(fd != 1) {\n      printf(\"%s: wrong fd\\n\", s);\n      exit(1);\n    }\n    if(exec(\"echo\", echoargv) < 0){\n      printf(\"%s: exec echo failed\\n\", s);\n      exit(1);\n    }\n    // won't get to here\n  }\n  if (wait(&xstatus) != pid) {\n    printf(\"%s: wait failed!\\n\", s);\n  }\n  if(xstatus != 0)\n    exit(xstatus);\n\n  fd = open(\"echo-ok\", O_RDONLY);\n  if(fd < 0) {\n    printf(\"%s: open failed\\n\", s);\n    exit(1);\n  }\n  if (read(fd, buf, 2) != 2) {\n    printf(\"%s: read failed\\n\", s);\n    exit(1);\n  }\n  unlink(\"echo-ok\");\n  if(buf[0] == 'O' && buf[1] == 'K')\n    exit(0);\n  else {\n    printf(\"%s: wrong output\\n\", s);\n    exit(1);\n  }\n\n}\n\n// simple fork and pipe read/write\n\nvoid\npipe1(char *s)\n{\n  int fds[2], pid, xstatus;\n  int seq, i, n, cc, total;\n  enum { N=5, SZ=1033 };\n  \n  if(pipe(fds) != 0){\n    printf(\"%s: pipe() failed\\n\", s);\n    exit(1);\n  }\n  pid = fork();\n  seq = 0;\n  if(pid == 0){\n    close(fds[0]);\n    for(n = 0; n < N; n++){\n      for(i = 0; i < SZ; i++)\n        buf[i] = seq++;\n      if(write(fds[1], buf, SZ) != SZ){\n        printf(\"%s: pipe1 oops 1\\n\", s);\n        exit(1);\n      }\n    }\n    exit(0);\n  } else if(pid > 0){\n    close(fds[1]);\n    total = 0;\n    cc = 1;\n    while((n = read(fds[0], buf, cc)) > 0){\n      for(i = 0; i < n; i++){\n        if((buf[i] & 0xff) != (seq++ & 0xff)){\n          printf(\"%s: pipe1 oops 2\\n\", s);\n          return;\n        }\n      }\n      total += n;\n      cc = cc * 2;\n      if(cc > sizeof(buf))\n        cc = sizeof(buf);\n    }\n    if(total != N * SZ){\n      printf(\"%s: pipe1 oops 3 total %d\\n\", s, total);\n      exit(1);\n    }\n    close(fds[0]);\n    wait(&xstatus);\n    exit(xstatus);\n  } else {\n    printf(\"%s: fork() failed\\n\", s);\n    exit(1);\n  }\n}\n\n\n// test if child is killed (status = -1)\nvoid\nkillstatus(char *s)\n{\n  int xst;\n  \n  for(int i = 0; i < 100; i++){\n    int pid1 = fork();\n    if(pid1 < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid1 == 0){\n      while(1) {\n        getpid();\n      }\n      exit(0);\n    }\n    pause(1);\n    kill(pid1);\n    wait(&xst);\n    if(xst != -1) {\n       printf(\"%s: status should be -1\\n\", s);\n       exit(1);\n    }\n  }\n  exit(0);\n}\n\n// meant to be run w/ at most two CPUs\nvoid\npreempt(char *s)\n{\n  int pid1, pid2, pid3;\n  int pfds[2];\n\n  pid1 = fork();\n  if(pid1 < 0) {\n    printf(\"%s: fork failed\", s);\n    exit(1);\n  }\n  if(pid1 == 0)\n    for(;;)\n      ;\n\n  pid2 = fork();\n  if(pid2 < 0) {\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n  if(pid2 == 0)\n    for(;;)\n      ;\n\n  pipe(pfds);\n  pid3 = fork();\n  if(pid3 < 0) {\n     printf(\"%s: fork failed\\n\", s);\n     exit(1);\n  }\n  if(pid3 == 0){\n    close(pfds[0]);\n    if(write(pfds[1], \"x\", 1) != 1)\n      printf(\"%s: preempt write error\", s);\n    close(pfds[1]);\n    for(;;)\n      ;\n  }\n\n  close(pfds[1]);\n  if(read(pfds[0], buf, sizeof(buf)) != 1){\n    printf(\"%s: preempt read error\", s);\n    return;\n  }\n  close(pfds[0]);\n  printf(\"kill... \");\n  kill(pid1);\n  kill(pid2);\n  kill(pid3);\n  printf(\"wait... \");\n  wait(0);\n  wait(0);\n  wait(0);\n}\n\n// try to find any races between exit and wait\nvoid\nexitwait(char *s)\n{\n  int i, pid;\n\n  for(i = 0; i < 100; i++){\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid){\n      int xstate;\n      if(wait(&xstate) != pid){\n        printf(\"%s: wait wrong pid\\n\", s);\n        exit(1);\n      }\n      if(i != xstate) {\n        printf(\"%s: wait wrong exit status\\n\", s);\n        exit(1);\n      }\n    } else {\n      exit(i);\n    }\n  }\n}\n\n// try to find races in the reparenting\n// code that handles a parent exiting\n// when it still has live children.\nvoid\nreparent(char *s)\n{\n  int master_pid = getpid();\n  for(int i = 0; i < 200; i++){\n    int pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid){\n      if(wait(0) != pid){\n        printf(\"%s: wait wrong pid\\n\", s);\n        exit(1);\n      }\n    } else {\n      int pid2 = fork();\n      if(pid2 < 0){\n        kill(master_pid);\n        exit(1);\n      }\n      exit(0);\n    }\n  }\n  exit(0);\n}\n\n// what if two children exit() at the same time?\nvoid\ntwochildren(char *s)\n{\n  for(int i = 0; i < 1000; i++){\n    int pid1 = fork();\n    if(pid1 < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid1 == 0){\n      exit(0);\n    } else {\n      int pid2 = fork();\n      if(pid2 < 0){\n        printf(\"%s: fork failed\\n\", s);\n        exit(1);\n      }\n      if(pid2 == 0){\n        exit(0);\n      } else {\n        wait(0);\n        wait(0);\n      }\n    }\n  }\n}\n\n// concurrent forks to try to expose locking bugs.\nvoid\nforkfork(char *s)\n{\n  enum { N=2 };\n  \n  for(int i = 0; i < N; i++){\n    int pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\", s);\n      exit(1);\n    }\n    if(pid == 0){\n      for(int j = 0; j < 200; j++){\n        int pid1 = fork();\n        if(pid1 < 0){\n          exit(1);\n        }\n        if(pid1 == 0){\n          exit(0);\n        }\n        wait(0);\n      }\n      exit(0);\n    }\n  }\n\n  int xstatus;\n  for(int i = 0; i < N; i++){\n    wait(&xstatus);\n    if(xstatus != 0) {\n      printf(\"%s: fork in child failed\", s);\n      exit(1);\n    }\n  }\n}\n\nvoid\nforkforkfork(char *s)\n{\n  unlink(\"stopforking\");\n\n  int pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\", s);\n    exit(1);\n  }\n  if(pid == 0){\n    while(1){\n      int fd = open(\"stopforking\", 0);\n      if(fd >= 0){\n        exit(0);\n      }\n      if(fork() < 0){\n        close(open(\"stopforking\", O_CREATE|O_RDWR));\n      }\n    }\n\n    exit(0);\n  }\n\n  pause(20); // two seconds\n  close(open(\"stopforking\", O_CREATE|O_RDWR));\n  wait(0);\n  pause(10); // one second\n}\n\n// regression test. does reparent() violate the parent-then-child\n// locking order when giving away a child to init, so that exit()\n// deadlocks against init's wait()? also used to trigger a \"panic:\n// release\" due to exit() releasing a different p->parent->lock than\n// it acquired.\nvoid\nreparent2(char *s)\n{\n  for(int i = 0; i < 800; i++){\n    int pid1 = fork();\n    if(pid1 < 0){\n      printf(\"fork failed\\n\");\n      exit(1);\n    }\n    if(pid1 == 0){\n      fork();\n      fork();\n      exit(0);\n    }\n    wait(0);\n  }\n\n  exit(0);\n}\n\n// allocate all mem, free it, and allocate again\nvoid\nmem(char *s)\n{\n  void *m1, *m2;\n  int pid;\n\n  if((pid = fork()) == 0){\n    m1 = 0;\n    while((m2 = malloc(10001)) != 0){\n      *(char**)m2 = m1;\n      m1 = m2;\n    }\n    while(m1){\n      m2 = *(char**)m1;\n      free(m1);\n      m1 = m2;\n    }\n    m1 = malloc(1024*20);\n    if(m1 == 0){\n      printf(\"%s: couldn't allocate mem?!!\\n\", s);\n      exit(1);\n    }\n    free(m1);\n    exit(0);\n  } else {\n    int xstatus;\n    wait(&xstatus);\n    if(xstatus == -1){\n      // probably page fault, so might be lazy lab,\n      // so OK.\n      exit(0);\n    }\n    exit(xstatus);\n  }\n}\n\n// More file system tests\n\n// two processes write to the same file descriptor\n// is the offset shared? does inode locking work?\nvoid\nsharedfd(char *s)\n{\n  int fd, pid, i, n, nc, np;\n  enum { N = 1000, SZ=10};\n  char buf[SZ];\n\n  unlink(\"sharedfd\");\n  fd = open(\"sharedfd\", O_CREATE|O_RDWR);\n  if(fd < 0){\n    printf(\"%s: cannot open sharedfd for writing\", s);\n    exit(1);\n  }\n  pid = fork();\n  memset(buf, pid==0?'c':'p', sizeof(buf));\n  for(i = 0; i < N; i++){\n    if(write(fd, buf, sizeof(buf)) != sizeof(buf)){\n      printf(\"%s: write sharedfd failed\\n\", s);\n      exit(1);\n    }\n  }\n  if(pid == 0) {\n    exit(0);\n  } else {\n    int xstatus;\n    wait(&xstatus);\n    if(xstatus != 0)\n      exit(xstatus);\n  }\n  \n  close(fd);\n  fd = open(\"sharedfd\", 0);\n  if(fd < 0){\n    printf(\"%s: cannot open sharedfd for reading\\n\", s);\n    exit(1);\n  }\n  nc = np = 0;\n  while((n = read(fd, buf, sizeof(buf))) > 0){\n    for(i = 0; i < sizeof(buf); i++){\n      if(buf[i] == 'c')\n        nc++;\n      if(buf[i] == 'p')\n        np++;\n    }\n  }\n  close(fd);\n  unlink(\"sharedfd\");\n  if(nc == N*SZ && np == N*SZ){\n    exit(0);\n  } else {\n    printf(\"%s: nc/np test fails\\n\", s);\n    exit(1);\n  }\n}\n\n// four processes write different files at the same\n// time, to test block allocation.\nvoid\nfourfiles(char *s)\n{\n  int fd, pid, i, j, n, total, pi;\n  char *names[] = { \"f0\", \"f1\", \"f2\", \"f3\" };\n  char *fname;\n  enum { N=12, NCHILD=4, SZ=500 };\n  \n  for(pi = 0; pi < NCHILD; pi++){\n    fname = names[pi];\n    unlink(fname);\n\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n\n    if(pid == 0){\n      fd = open(fname, O_CREATE | O_RDWR);\n      if(fd < 0){\n        printf(\"%s: create failed\\n\", s);\n        exit(1);\n      }\n\n      memset(buf, '0'+pi, SZ);\n      for(i = 0; i < N; i++){\n        if((n = write(fd, buf, SZ)) != SZ){\n          printf(\"write failed %d\\n\", n);\n          exit(1);\n        }\n      }\n      exit(0);\n    }\n  }\n\n  int xstatus;\n  for(pi = 0; pi < NCHILD; pi++){\n    wait(&xstatus);\n    if(xstatus != 0)\n      exit(xstatus);\n  }\n\n  for(i = 0; i < NCHILD; i++){\n    fname = names[i];\n    fd = open(fname, 0);\n    total = 0;\n    while((n = read(fd, buf, sizeof(buf))) > 0){\n      for(j = 0; j < n; j++){\n        if(buf[j] != '0'+i){\n          printf(\"%s: wrong char\\n\", s);\n          exit(1);\n        }\n      }\n      total += n;\n    }\n    close(fd);\n    if(total != N*SZ){\n      printf(\"wrong length %d\\n\", total);\n      exit(1);\n    }\n    unlink(fname);\n  }\n}\n\n// four processes create and delete different files in same directory\nvoid\ncreatedelete(char *s)\n{\n  enum { N = 20, NCHILD=4 };\n  int pid, i, fd, pi;\n  char name[32];\n\n  for(pi = 0; pi < NCHILD; pi++){\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n\n    if(pid == 0){\n      name[0] = 'p' + pi;\n      name[2] = '\\0';\n      for(i = 0; i < N; i++){\n        name[1] = '0' + i;\n        fd = open(name, O_CREATE | O_RDWR);\n        if(fd < 0){\n          printf(\"%s: create failed\\n\", s);\n          exit(1);\n        }\n        close(fd);\n        if(i > 0 && (i % 2 ) == 0){\n          name[1] = '0' + (i / 2);\n          if(unlink(name) < 0){\n            printf(\"%s: unlink failed\\n\", s);\n            exit(1);\n          }\n        }\n      }\n      exit(0);\n    }\n  }\n\n  int xstatus;\n  for(pi = 0; pi < NCHILD; pi++){\n    wait(&xstatus);\n    if(xstatus != 0)\n      exit(1);\n  }\n\n  name[0] = name[1] = name[2] = 0;\n  for(i = 0; i < N; i++){\n    for(pi = 0; pi < NCHILD; pi++){\n      name[0] = 'p' + pi;\n      name[1] = '0' + i;\n      fd = open(name, 0);\n      if((i == 0 || i >= N/2) && fd < 0){\n        printf(\"%s: oops createdelete %s didn't exist\\n\", s, name);\n        exit(1);\n      } else if((i >= 1 && i < N/2) && fd >= 0){\n        printf(\"%s: oops createdelete %s did exist\\n\", s, name);\n        exit(1);\n      }\n      if(fd >= 0)\n        close(fd);\n    }\n  }\n\n  for(i = 0; i < N; i++){\n    for(pi = 0; pi < NCHILD; pi++){\n      name[0] = 'p' + pi;\n      name[1] = '0' + i;\n      unlink(name);\n    }\n  }\n}\n\n// can I unlink a file and still read it?\nvoid\nunlinkread(char *s)\n{\n  enum { SZ = 5 };\n  int fd, fd1;\n\n  fd = open(\"unlinkread\", O_CREATE | O_RDWR);\n  if(fd < 0){\n    printf(\"%s: create unlinkread failed\\n\", s);\n    exit(1);\n  }\n  write(fd, \"hello\", SZ);\n  close(fd);\n\n  fd = open(\"unlinkread\", O_RDWR);\n  if(fd < 0){\n    printf(\"%s: open unlinkread failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"unlinkread\") != 0){\n    printf(\"%s: unlink unlinkread failed\\n\", s);\n    exit(1);\n  }\n\n  fd1 = open(\"unlinkread\", O_CREATE | O_RDWR);\n  write(fd1, \"yyy\", 3);\n  close(fd1);\n\n  if(read(fd, buf, sizeof(buf)) != SZ){\n    printf(\"%s: unlinkread read failed\", s);\n    exit(1);\n  }\n  if(buf[0] != 'h'){\n    printf(\"%s: unlinkread wrong data\\n\", s);\n    exit(1);\n  }\n  if(write(fd, buf, 10) != 10){\n    printf(\"%s: unlinkread write failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n  unlink(\"unlinkread\");\n}\n\nvoid\nlinktest(char *s)\n{\n  enum { SZ = 5 };\n  int fd;\n\n  unlink(\"lf1\");\n  unlink(\"lf2\");\n\n  fd = open(\"lf1\", O_CREATE|O_RDWR);\n  if(fd < 0){\n    printf(\"%s: create lf1 failed\\n\", s);\n    exit(1);\n  }\n  if(write(fd, \"hello\", SZ) != SZ){\n    printf(\"%s: write lf1 failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(link(\"lf1\", \"lf2\") < 0){\n    printf(\"%s: link lf1 lf2 failed\\n\", s);\n    exit(1);\n  }\n  unlink(\"lf1\");\n\n  if(open(\"lf1\", 0) >= 0){\n    printf(\"%s: unlinked lf1 but it is still there!\\n\", s);\n    exit(1);\n  }\n\n  fd = open(\"lf2\", 0);\n  if(fd < 0){\n    printf(\"%s: open lf2 failed\\n\", s);\n    exit(1);\n  }\n  if(read(fd, buf, sizeof(buf)) != SZ){\n    printf(\"%s: read lf2 failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(link(\"lf2\", \"lf2\") >= 0){\n    printf(\"%s: link lf2 lf2 succeeded! oops\\n\", s);\n    exit(1);\n  }\n\n  unlink(\"lf2\");\n  if(link(\"lf2\", \"lf1\") >= 0){\n    printf(\"%s: link non-existent succeeded! oops\\n\", s);\n    exit(1);\n  }\n\n  if(link(\".\", \"lf1\") >= 0){\n    printf(\"%s: link . lf1 succeeded! oops\\n\", s);\n    exit(1);\n  }\n}\n\n// test concurrent create/link/unlink of the same file\nvoid\nconcreate(char *s)\n{\n  enum { N = 40 };\n  char file[3];\n  int i, pid, n, fd;\n  char fa[N];\n  struct {\n    ushort inum;\n    char name[DIRSIZ];\n  } de;\n\n  file[0] = 'C';\n  file[2] = '\\0';\n  for(i = 0; i < N; i++){\n    file[1] = '0' + i;\n    unlink(file);\n    pid = fork();\n    if(pid && (i % 3) == 1){\n      link(\"C0\", file);\n    } else if(pid == 0 && (i % 5) == 1){\n      link(\"C0\", file);\n    } else {\n      fd = open(file, O_CREATE | O_RDWR);\n      if(fd < 0){\n        printf(\"concreate create %s failed\\n\", file);\n        exit(1);\n      }\n      close(fd);\n    }\n    if(pid == 0) {\n      exit(0);\n    } else {\n      int xstatus;\n      wait(&xstatus);\n      if(xstatus != 0)\n        exit(1);\n    }\n  }\n\n  memset(fa, 0, sizeof(fa));\n  fd = open(\".\", 0);\n  n = 0;\n  while(read(fd, &de, sizeof(de)) > 0){\n    if(de.inum == 0)\n      continue;\n    if(de.name[0] == 'C' && de.name[2] == '\\0'){\n      i = de.name[1] - '0';\n      if(i < 0 || i >= sizeof(fa)){\n        printf(\"%s: concreate weird file %s\\n\", s, de.name);\n        exit(1);\n      }\n      if(fa[i]){\n        printf(\"%s: concreate duplicate file %s\\n\", s, de.name);\n        exit(1);\n      }\n      fa[i] = 1;\n      n++;\n    }\n  }\n  close(fd);\n\n  if(n != N){\n    printf(\"%s: concreate not enough files in directory listing\\n\", s);\n    exit(1);\n  }\n\n  for(i = 0; i < N; i++){\n    file[1] = '0' + i;\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(((i % 3) == 0 && pid == 0) ||\n       ((i % 3) == 1 && pid != 0)){\n      close(open(file, 0));\n      close(open(file, 0));\n      close(open(file, 0));\n      close(open(file, 0));\n      close(open(file, 0));\n      close(open(file, 0));\n    } else {\n      unlink(file);\n      unlink(file);\n      unlink(file);\n      unlink(file);\n      unlink(file);\n      unlink(file);\n    }\n    if(pid == 0)\n      exit(0);\n    else\n      wait(0);\n  }\n}\n\n// another concurrent link/unlink/create test,\n// to look for deadlocks.\nvoid\nlinkunlink(char *s)\n{\n  int pid, i;\n\n  unlink(\"x\");\n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n\n  unsigned int x = (pid ? 1 : 97);\n  for(i = 0; i < 100; i++){\n    x = x * 1103515245 + 12345;\n    if((x % 3) == 0){\n      close(open(\"x\", O_RDWR | O_CREATE));\n    } else if((x % 3) == 1){\n      link(\"cat\", \"x\");\n    } else {\n      unlink(\"x\");\n    }\n  }\n\n  if(pid)\n    wait(0);\n  else\n    exit(0);\n}\n\n\nvoid\nsubdir(char *s)\n{\n  int fd, cc;\n\n  unlink(\"ff\");\n  if(mkdir(\"dd\") != 0){\n    printf(\"%s: mkdir dd failed\\n\", s);\n    exit(1);\n  }\n\n  fd = open(\"dd/ff\", O_CREATE | O_RDWR);\n  if(fd < 0){\n    printf(\"%s: create dd/ff failed\\n\", s);\n    exit(1);\n  }\n  write(fd, \"ff\", 2);\n  close(fd);\n\n  if(unlink(\"dd\") >= 0){\n    printf(\"%s: unlink dd (non-empty dir) succeeded!\\n\", s);\n    exit(1);\n  }\n\n  if(mkdir(\"/dd/dd\") != 0){\n    printf(\"%s: subdir mkdir dd/dd failed\\n\", s);\n    exit(1);\n  }\n\n  fd = open(\"dd/dd/ff\", O_CREATE | O_RDWR);\n  if(fd < 0){\n    printf(\"%s: create dd/dd/ff failed\\n\", s);\n    exit(1);\n  }\n  write(fd, \"FF\", 2);\n  close(fd);\n\n  fd = open(\"dd/dd/../ff\", 0);\n  if(fd < 0){\n    printf(\"%s: open dd/dd/../ff failed\\n\", s);\n    exit(1);\n  }\n  cc = read(fd, buf, sizeof(buf));\n  if(cc != 2 || buf[0] != 'f'){\n    printf(\"%s: dd/dd/../ff wrong content\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(link(\"dd/dd/ff\", \"dd/dd/ffff\") != 0){\n    printf(\"%s: link dd/dd/ff dd/dd/ffff failed\\n\", s);\n    exit(1);\n  }\n\n  if(unlink(\"dd/dd/ff\") != 0){\n    printf(\"%s: unlink dd/dd/ff failed\\n\", s);\n    exit(1);\n  }\n  if(open(\"dd/dd/ff\", O_RDONLY) >= 0){\n    printf(\"%s: open (unlinked) dd/dd/ff succeeded\\n\", s);\n    exit(1);\n  }\n\n  if(chdir(\"dd\") != 0){\n    printf(\"%s: chdir dd failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"dd/../../dd\") != 0){\n    printf(\"%s: chdir dd/../../dd failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"dd/../../../dd\") != 0){\n    printf(\"%s: chdir dd/../../../dd failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"./..\") != 0){\n    printf(\"%s: chdir ./.. failed\\n\", s);\n    exit(1);\n  }\n\n  fd = open(\"dd/dd/ffff\", 0);\n  if(fd < 0){\n    printf(\"%s: open dd/dd/ffff failed\\n\", s);\n    exit(1);\n  }\n  if(read(fd, buf, sizeof(buf)) != 2){\n    printf(\"%s: read dd/dd/ffff wrong len\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(open(\"dd/dd/ff\", O_RDONLY) >= 0){\n    printf(\"%s: open (unlinked) dd/dd/ff succeeded!\\n\", s);\n    exit(1);\n  }\n\n  if(open(\"dd/ff/ff\", O_CREATE|O_RDWR) >= 0){\n    printf(\"%s: create dd/ff/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(open(\"dd/xx/ff\", O_CREATE|O_RDWR) >= 0){\n    printf(\"%s: create dd/xx/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(open(\"dd\", O_CREATE) >= 0){\n    printf(\"%s: create dd succeeded!\\n\", s);\n    exit(1);\n  }\n  if(open(\"dd\", O_RDWR) >= 0){\n    printf(\"%s: open dd rdwr succeeded!\\n\", s);\n    exit(1);\n  }\n  if(open(\"dd\", O_WRONLY) >= 0){\n    printf(\"%s: open dd wronly succeeded!\\n\", s);\n    exit(1);\n  }\n  if(link(\"dd/ff/ff\", \"dd/dd/xx\") == 0){\n    printf(\"%s: link dd/ff/ff dd/dd/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(link(\"dd/xx/ff\", \"dd/dd/xx\") == 0){\n    printf(\"%s: link dd/xx/ff dd/dd/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(link(\"dd/ff\", \"dd/dd/ffff\") == 0){\n    printf(\"%s: link dd/ff dd/dd/ffff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"dd/ff/ff\") == 0){\n    printf(\"%s: mkdir dd/ff/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"dd/xx/ff\") == 0){\n    printf(\"%s: mkdir dd/xx/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"dd/dd/ffff\") == 0){\n    printf(\"%s: mkdir dd/dd/ffff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd/xx/ff\") == 0){\n    printf(\"%s: unlink dd/xx/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd/ff/ff\") == 0){\n    printf(\"%s: unlink dd/ff/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"dd/ff\") == 0){\n    printf(\"%s: chdir dd/ff succeeded!\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"dd/xx\") == 0){\n    printf(\"%s: chdir dd/xx succeeded!\\n\", s);\n    exit(1);\n  }\n\n  if(unlink(\"dd/dd/ffff\") != 0){\n    printf(\"%s: unlink dd/dd/ff failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd/ff\") != 0){\n    printf(\"%s: unlink dd/ff failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd\") == 0){\n    printf(\"%s: unlink non-empty dd succeeded!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd/dd\") < 0){\n    printf(\"%s: unlink dd/dd failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dd\") < 0){\n    printf(\"%s: unlink dd failed\\n\", s);\n    exit(1);\n  }\n}\n\n// test writes that are larger than the log.\nvoid\nbigwrite(char *s)\n{\n  int fd, sz;\n\n  unlink(\"bigwrite\");\n  for(sz = 499; sz < (MAXOPBLOCKS+2)*BSIZE; sz += 471){\n    fd = open(\"bigwrite\", O_CREATE | O_RDWR);\n    if(fd < 0){\n      printf(\"%s: cannot create bigwrite\\n\", s);\n      exit(1);\n    }\n    int i;\n    for(i = 0; i < 2; i++){\n      int cc = write(fd, buf, sz);\n      if(cc != sz){\n        printf(\"%s: write(%d) ret %d\\n\", s, sz, cc);\n        exit(1);\n      }\n    }\n    close(fd);\n    unlink(\"bigwrite\");\n  }\n}\n\n\nvoid\nbigfile(char *s)\n{\n  enum { N = 20, SZ=600 };\n  int fd, i, total, cc;\n\n  unlink(\"bigfile.dat\");\n  fd = open(\"bigfile.dat\", O_CREATE | O_RDWR);\n  if(fd < 0){\n    printf(\"%s: cannot create bigfile\", s);\n    exit(1);\n  }\n  for(i = 0; i < N; i++){\n    memset(buf, i, SZ);\n    if(write(fd, buf, SZ) != SZ){\n      printf(\"%s: write bigfile failed\\n\", s);\n      exit(1);\n    }\n  }\n  close(fd);\n\n  fd = open(\"bigfile.dat\", 0);\n  if(fd < 0){\n    printf(\"%s: cannot open bigfile\\n\", s);\n    exit(1);\n  }\n  total = 0;\n  for(i = 0; ; i++){\n    cc = read(fd, buf, SZ/2);\n    if(cc < 0){\n      printf(\"%s: read bigfile failed\\n\", s);\n      exit(1);\n    }\n    if(cc == 0)\n      break;\n    if(cc != SZ/2){\n      printf(\"%s: short read bigfile\\n\", s);\n      exit(1);\n    }\n    if(buf[0] != i/2 || buf[SZ/2-1] != i/2){\n      printf(\"%s: read bigfile wrong data\\n\", s);\n      exit(1);\n    }\n    total += cc;\n  }\n  close(fd);\n  if(total != N*SZ){\n    printf(\"%s: read bigfile wrong total\\n\", s);\n    exit(1);\n  }\n  unlink(\"bigfile.dat\");\n}\n\nvoid\nfourteen(char *s)\n{\n  int fd;\n\n  // DIRSIZ is 14.\n\n  if(mkdir(\"12345678901234\") != 0){\n    printf(\"%s: mkdir 12345678901234 failed\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"12345678901234/123456789012345\") != 0){\n    printf(\"%s: mkdir 12345678901234/123456789012345 failed\\n\", s);\n    exit(1);\n  }\n  fd = open(\"123456789012345/123456789012345/123456789012345\", O_CREATE);\n  if(fd < 0){\n    printf(\"%s: create 123456789012345/123456789012345/123456789012345 failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n  fd = open(\"12345678901234/12345678901234/12345678901234\", 0);\n  if(fd < 0){\n    printf(\"%s: open 12345678901234/12345678901234/12345678901234 failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  if(mkdir(\"12345678901234/12345678901234\") == 0){\n    printf(\"%s: mkdir 12345678901234/12345678901234 succeeded!\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"123456789012345/12345678901234\") == 0){\n    printf(\"%s: mkdir 12345678901234/123456789012345 succeeded!\\n\", s);\n    exit(1);\n  }\n\n  // clean up\n  unlink(\"123456789012345/12345678901234\");\n  unlink(\"12345678901234/12345678901234\");\n  unlink(\"12345678901234/12345678901234/12345678901234\");\n  unlink(\"123456789012345/123456789012345/123456789012345\");\n  unlink(\"12345678901234/123456789012345\");\n  unlink(\"12345678901234\");\n}\n\nvoid\nrmdot(char *s)\n{\n  if(mkdir(\"dots\") != 0){\n    printf(\"%s: mkdir dots failed\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"dots\") != 0){\n    printf(\"%s: chdir dots failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\".\") == 0){\n    printf(\"%s: rm . worked!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"..\") == 0){\n    printf(\"%s: rm .. worked!\\n\", s);\n    exit(1);\n  }\n  if(chdir(\"/\") != 0){\n    printf(\"%s: chdir / failed\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dots/.\") == 0){\n    printf(\"%s: unlink dots/. worked!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dots/..\") == 0){\n    printf(\"%s: unlink dots/.. worked!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dots\") != 0){\n    printf(\"%s: unlink dots failed!\\n\", s);\n    exit(1);\n  }\n}\n\nvoid\ndirfile(char *s)\n{\n  int fd;\n\n  fd = open(\"dirfile\", O_CREATE);\n  if(fd < 0){\n    printf(\"%s: create dirfile failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n  if(chdir(\"dirfile\") == 0){\n    printf(\"%s: chdir dirfile succeeded!\\n\", s);\n    exit(1);\n  }\n  fd = open(\"dirfile/xx\", 0);\n  if(fd >= 0){\n    printf(\"%s: create dirfile/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  fd = open(\"dirfile/xx\", O_CREATE);\n  if(fd >= 0){\n    printf(\"%s: create dirfile/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(mkdir(\"dirfile/xx\") == 0){\n    printf(\"%s: mkdir dirfile/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dirfile/xx\") == 0){\n    printf(\"%s: unlink dirfile/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(link(\"README\", \"dirfile/xx\") == 0){\n    printf(\"%s: link to dirfile/xx succeeded!\\n\", s);\n    exit(1);\n  }\n  if(unlink(\"dirfile\") != 0){\n    printf(\"%s: unlink dirfile failed!\\n\", s);\n    exit(1);\n  }\n\n  fd = open(\".\", O_RDWR);\n  if(fd >= 0){\n    printf(\"%s: open . for writing succeeded!\\n\", s);\n    exit(1);\n  }\n  fd = open(\".\", 0);\n  if(write(fd, \"x\", 1) > 0){\n    printf(\"%s: write . succeeded!\\n\", s);\n    exit(1);\n  }\n  close(fd);\n}\n\n// test that iput() is called at the end of _namei().\n// also tests empty file names.\nvoid\niref(char *s)\n{\n  int i, fd;\n\n  for(i = 0; i < NINODE + 1; i++){\n    if(mkdir(\"irefd\") != 0){\n      printf(\"%s: mkdir irefd failed\\n\", s);\n      exit(1);\n    }\n    if(chdir(\"irefd\") != 0){\n      printf(\"%s: chdir irefd failed\\n\", s);\n      exit(1);\n    }\n\n    mkdir(\"\");\n    link(\"README\", \"\");\n    fd = open(\"\", O_CREATE);\n    if(fd >= 0)\n      close(fd);\n    fd = open(\"xx\", O_CREATE);\n    if(fd >= 0)\n      close(fd);\n    unlink(\"xx\");\n  }\n\n  // clean up\n  for(i = 0; i < NINODE + 1; i++){\n    chdir(\"..\");\n    unlink(\"irefd\");\n  }\n\n  chdir(\"/\");\n}\n\n// test that fork fails gracefully\n// the forktest binary also does this, but it runs out of proc entries first.\n// inside the bigger usertests binary, we run out of memory first.\nvoid\nforktest(char *s)\n{\n  enum{ N = 1000 };\n  int n, pid;\n\n  for(n=0; n<N; n++){\n    pid = fork();\n    if(pid < 0)\n      break;\n    if(pid == 0)\n      exit(0);\n  }\n\n  if (n == 0) {\n    printf(\"%s: no fork at all!\\n\", s);\n    exit(1);\n  }\n\n  if(n == N){\n    printf(\"%s: fork claimed to work 1000 times!\\n\", s);\n    exit(1);\n  }\n\n  for(; n > 0; n--){\n    if(wait(0) < 0){\n      printf(\"%s: wait stopped early\\n\", s);\n      exit(1);\n    }\n  }\n\n  if(wait(0) != -1){\n    printf(\"%s: wait got too many\\n\", s);\n    exit(1);\n  }\n}\n\nvoid\nsbrkbasic(char *s)\n{\n  enum { TOOMUCH=1024*1024*1024};\n  int i, pid, xstatus;\n  char *c, *a, *b;\n\n  // does sbrk() return the expected failure value?\n  pid = fork();\n  if(pid < 0){\n    printf(\"fork failed in sbrkbasic\\n\");\n    exit(1);\n  }\n  if(pid == 0){\n    a = sbrk(TOOMUCH);\n    if(a == (char*)SBRK_ERROR){\n      // it's OK if this fails.\n      exit(0);\n    }\n    \n    for(b = a; b < a+TOOMUCH; b += PGSIZE){\n      *b = 99;\n    }\n    \n    // we should not get here! either sbrk(TOOMUCH)\n    // should have failed, or (with lazy allocation)\n    // a pagefault should have killed this process.\n    exit(1);\n  }\n\n  wait(&xstatus);\n  if(xstatus == 1){\n    printf(\"%s: too much memory allocated!\\n\", s);\n    exit(1);\n  }\n\n  // can one sbrk() less than a page?\n  a = sbrk(0);\n  for(i = 0; i < 5000; i++){\n    b = sbrk(1);\n    if(b != a){\n      printf(\"%s: sbrk test failed %d %p %p\\n\", s, i, a, b);\n      exit(1);\n    }\n    *b = 1;\n    a = b + 1;\n  }\n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: sbrk test fork failed\\n\", s);\n    exit(1);\n  }\n  c = sbrk(1);\n  c = sbrk(1);\n  if(c != a + 1){\n    printf(\"%s: sbrk test failed post-fork\\n\", s);\n    exit(1);\n  }\n  if(pid == 0)\n    exit(0);\n  wait(&xstatus);\n  exit(xstatus);\n}\n\nvoid\nsbrkmuch(char *s)\n{\n  enum { BIG=100*1024*1024 };\n  char *c, *oldbrk, *a, *lastaddr, *p;\n  uint64 amt;\n\n  oldbrk = sbrk(0);\n\n  // can one grow address space to something big?\n  a = sbrk(0);\n  amt = BIG - (uint64)a;\n  p = sbrk(amt);\n  if (p != a) {\n    printf(\"%s: sbrk test failed to grow big address space; enough phys mem?\\n\", s);\n    exit(1);\n  }\n\n  lastaddr = (char*) (BIG-1);\n  *lastaddr = 99;\n\n  // can one de-allocate?\n  a = sbrk(0);\n  c = sbrk(-PGSIZE);\n  if(c == (char*)SBRK_ERROR){\n    printf(\"%s: sbrk could not deallocate\\n\", s);\n    exit(1);\n  }\n  c = sbrk(0);\n  if(c != a - PGSIZE){\n    printf(\"%s: sbrk deallocation produced wrong address, a %p c %p\\n\", s, a, c);\n    exit(1);\n  }\n\n  // can one re-allocate that page?\n  a = sbrk(0);\n  c = sbrk(PGSIZE);\n  if(c != a || sbrk(0) != a + PGSIZE){\n    printf(\"%s: sbrk re-allocation failed, a %p c %p\\n\", s, a, c);\n    exit(1);\n  }\n  if(*lastaddr == 99){\n    // should be zero\n    printf(\"%s: sbrk de-allocation didn't really deallocate\\n\", s);\n    exit(1);\n  }\n\n  a = sbrk(0);\n  c = sbrk(-(sbrk(0) - oldbrk));\n  if(c != a){\n    printf(\"%s: sbrk downsize failed, a %p c %p\\n\", s, a, c);\n    exit(1);\n  }\n}\n\n// can we read the kernel's memory?\nvoid\nkernmem(char *s)\n{\n  char *a;\n  int pid;\n\n  for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid == 0){\n      printf(\"%s: oops could read %p = %x\\n\", s, a, *a);\n      exit(1);\n    }\n    int xstatus;\n    wait(&xstatus);\n    if(xstatus != -1)  // did kernel kill child?\n      exit(1);\n  }\n}\n\n// user code should not be able to write to addresses above MAXVA.\nvoid\nMAXVAplus(char *s)\n{\n  volatile uint64 a = MAXVA;\n  for( ; a != 0; a <<= 1){\n    int pid;\n    pid = fork();\n    if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    if(pid == 0){\n      *(char*)a = 99;\n      printf(\"%s: oops wrote %p\\n\", s, (void*)a);\n      exit(1);\n    }\n    int xstatus;\n    wait(&xstatus);\n    if(xstatus != -1)  // did kernel kill child?\n      exit(1);\n  }\n}\n\n// if we run the system out of memory, does it clean up the last\n// failed allocation?\nvoid\nsbrkfail(char *s)\n{\n  enum { BIG=100*1024*1024 };\n  int i, xstatus;\n  int fds[2];\n  char scratch;\n  char *c, *a;\n  int pids[10];\n  int pid;\n  int failed;\n\n  failed = 0;\n  if(pipe(fds) != 0){\n    printf(\"%s: pipe() failed\\n\", s);\n    exit(1);\n  }\n  for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){\n    if((pids[i] = fork()) == 0){\n      // allocate a lot of memory\n      if (sbrk(BIG - (uint64)sbrk(0)) ==  (char*)SBRK_ERROR)\n        write(fds[1], \"0\", 1);\n      else\n        write(fds[1], \"1\", 1);\n      // sit around until killed\n      for(;;) pause(1000);\n    }\n    if(pids[i] != -1) {\n      read(fds[0], &scratch, 1);\n      if(scratch == '0')\n        failed = 1;\n    }\n  }\n  if(!failed) {\n    printf(\"%s: no allocation failed; allocate more?\\n\", s);\n  }\n  \n  // if those failed allocations freed up the pages they did allocate,\n  // we'll be able to allocate here\n  c = sbrk(PGSIZE);\n  for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){\n    if(pids[i] == -1)\n      continue;\n    kill(pids[i]);\n    wait(0);\n  }\n  if(c == (char*)SBRK_ERROR){\n    printf(\"%s: failed sbrk leaked memory\\n\", s);\n    exit(1);\n  }\n\n  // test running fork with the above allocated page \n  pid = fork();\n  if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n  if(pid == 0){\n    // allocate a lot of memory. this should produce an error\n    a = sbrk(10*BIG);\n    if(a == (char*)SBRK_ERROR){\n      exit(0);\n    }   \n    printf(\"%s: allocate a lot of memory succeeded %d\\n\", s, 10*BIG);\n    exit(1);\n  }\n  wait(&xstatus);\n  if(xstatus != 0)\n    exit(1);\n}\n\n  \n// test reads/writes from/to allocated memory\nvoid\nsbrkarg(char *s)\n{\n  char *a;\n  int fd, n;\n\n  a = sbrk(PGSIZE);\n  fd = open(\"sbrk\", O_CREATE|O_WRONLY);\n  unlink(\"sbrk\");\n  if(fd < 0)  {\n    printf(\"%s: open sbrk failed\\n\", s);\n    exit(1);\n  }\n  if ((n = write(fd, a, PGSIZE)) < 0) {\n    printf(\"%s: write sbrk failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  // test writes to allocated memory\n  a = sbrk(PGSIZE);\n  if(pipe((int *) a) != 0){\n    printf(\"%s: pipe() failed\\n\", s);\n    exit(1);\n  } \n}\n\nvoid\nvalidatetest(char *s)\n{\n  int hi;\n  uint64 p;\n\n  hi = 1100*1024;\n  for(p = 0; p <= (uint)hi; p += PGSIZE){\n    // try to crash the kernel by passing in a bad string pointer\n    if(link(\"nosuchfile\", (char*)p) != -1){\n      printf(\"%s: link should not succeed\\n\", s);\n      exit(1);\n    }\n  }\n}\n\n// does uninitialized data start out zero?\nchar uninit[10000];\nvoid\nbsstest(char *s)\n{\n  int i;\n\n  for(i = 0; i < sizeof(uninit); i++){\n    if(uninit[i] != '\\0'){\n      printf(\"%s: bss test failed\\n\", s);\n      exit(1);\n    }\n  }\n}\n\n// does exec return an error if the arguments\n// are larger than a page? or does it write\n// below the stack and wreck the instructions/data?\nvoid\nbigargtest(char *s)\n{\n  int pid, fd, xstatus;\n\n  unlink(\"bigarg-ok\");\n  pid = fork();\n  if(pid == 0){\n    static char *args[MAXARG];\n    int i;\n    char big[400];\n    memset(big, ' ', sizeof(big));\n    big[sizeof(big)-1] = '\\0';\n    for(i = 0; i < MAXARG-1; i++)\n      args[i] = big;\n    args[MAXARG-1] = 0;\n    // this exec() should fail (and return) because the\n    // arguments are too large.\n    exec(\"echo\", args);\n    fd = open(\"bigarg-ok\", O_CREATE);\n    close(fd);\n    exit(0);\n  } else if(pid < 0){\n    printf(\"%s: bigargtest: fork failed\\n\", s);\n    exit(1);\n  }\n  \n  wait(&xstatus);\n  if(xstatus != 0)\n    exit(xstatus);\n  fd = open(\"bigarg-ok\", 0);\n  if(fd < 0){\n    printf(\"%s: bigarg test failed!\\n\", s);\n    exit(1);\n  }\n  close(fd);\n}\n\n// what happens when the file system runs out of blocks?\n// answer: balloc panics, so this test is not useful.\nvoid\nfsfull()\n{\n  int nfiles;\n  int fsblocks = 0;\n\n  printf(\"fsfull test\\n\");\n\n  for(nfiles = 0; ; nfiles++){\n    char name[64];\n    name[0] = 'f';\n    name[1] = '0' + nfiles / 1000;\n    name[2] = '0' + (nfiles % 1000) / 100;\n    name[3] = '0' + (nfiles % 100) / 10;\n    name[4] = '0' + (nfiles % 10);\n    name[5] = '\\0';\n    printf(\"writing %s\\n\", name);\n    int fd = open(name, O_CREATE|O_RDWR);\n    if(fd < 0){\n      printf(\"open %s failed\\n\", name);\n      break;\n    }\n    int total = 0;\n    while(1){\n      int cc = write(fd, buf, BSIZE);\n      if(cc < BSIZE)\n        break;\n      total += cc;\n      fsblocks++;\n    }\n    printf(\"wrote %d bytes\\n\", total);\n    close(fd);\n    if(total == 0)\n      break;\n  }\n\n  while(nfiles >= 0){\n    char name[64];\n    name[0] = 'f';\n    name[1] = '0' + nfiles / 1000;\n    name[2] = '0' + (nfiles % 1000) / 100;\n    name[3] = '0' + (nfiles % 100) / 10;\n    name[4] = '0' + (nfiles % 10);\n    name[5] = '\\0';\n    unlink(name);\n    nfiles--;\n  }\n\n  printf(\"fsfull test finished\\n\");\n}\n\nvoid argptest(char *s)\n{\n  int fd;\n  fd = open(\"init\", O_RDONLY);\n  if (fd < 0) {\n    printf(\"%s: open failed\\n\", s);\n    exit(1);\n  }\n  read(fd, sbrk(0) - 1, -1);\n  close(fd);\n}\n\n// check that there's an invalid page beneath\n// the user stack, to catch stack overflow.\nvoid\nstacktest(char *s)\n{\n  int pid;\n  int xstatus;\n  \n  pid = fork();\n  if(pid == 0) {\n    char *sp = (char *) r_sp();\n    sp -= USERSTACK*PGSIZE;\n    // the *sp should cause a trap.\n    printf(\"%s: stacktest: read below stack %d\\n\", s, *sp);\n    exit(1);\n  } else if(pid < 0){\n    printf(\"%s: fork failed\\n\", s);\n    exit(1);\n  }\n  wait(&xstatus);\n  if(xstatus == -1)  // kernel killed child?\n    exit(0);\n  else\n    exit(xstatus);\n}\n\n// check that writes to a few forbidden addresses\n// cause a fault, e.g. process's text and TRAMPOLINE.\nvoid\nnowrite(char *s)\n{\n  int pid;\n  int xstatus;\n  uint64 addrs[] = { 0, 0x80000000LL, 0x3fffffe000, 0x3ffffff000, 0x4000000000,\n                     0xffffffffffffffff };\n  \n  for(int ai = 0; ai < sizeof(addrs)/sizeof(addrs[0]); ai++){\n    pid = fork();\n    if(pid == 0) {\n      volatile int *addr = (int *) addrs[ai];\n      *addr = 10;\n      printf(\"%s: write to %p did not fail!\\n\", s, addr);\n      exit(0);\n    } else if(pid < 0){\n      printf(\"%s: fork failed\\n\", s);\n      exit(1);\n    }\n    wait(&xstatus);\n    if(xstatus == 0){\n      // kernel did not kill child!\n      exit(1);\n    }\n  }\n  exit(0);\n}\n\n// regression test. copyin(), copyout(), and copyinstr() used to cast\n// the virtual page address to uint, which (with certain wild system\n// call arguments) resulted in a kernel page faults.\nvoid *big = (void*) 0xeaeb0b5b00002f5e;\nvoid\npgbug(char *s)\n{\n  char *argv[1];\n  argv[0] = 0;\n  exec(big, argv);\n  pipe(big);\n\n  exit(0);\n}\n\n// regression test. does the kernel panic if a process sbrk()s its\n// size to be less than a page, or zero, or reduces the break by an\n// amount too small to cause a page to be freed?\nvoid\nsbrkbugs(char *s)\n{\n  int pid = fork();\n  if(pid < 0){\n    printf(\"fork failed\\n\");\n    exit(1);\n  }\n  if(pid == 0){\n    int sz = (uint64) sbrk(0);\n    // free all user memory; there used to be a bug that\n    // would not adjust p->sz correctly in this case,\n    // causing exit() to panic.\n    sbrk(-sz);\n    // user page fault here.\n    exit(0);\n  }\n  wait(0);\n\n  pid = fork();\n  if(pid < 0){\n    printf(\"fork failed\\n\");\n    exit(1);\n  }\n  if(pid == 0){\n    int sz = (uint64) sbrk(0);\n    // set the break to somewhere in the very first\n    // page; there used to be a bug that would incorrectly\n    // free the first page.\n    sbrk(-(sz - 3500));\n    exit(0);\n  }\n  wait(0);\n\n  pid = fork();\n  if(pid < 0){\n    printf(\"fork failed\\n\");\n    exit(1);\n  }\n  if(pid == 0){\n    // set the break in the middle of a page.\n    sbrk((10*PGSIZE + 2048) - (uint64)sbrk(0));\n\n    // reduce the break a bit, but not enough to\n    // cause a page to be freed. this used to cause\n    // a panic.\n    sbrk(-10);\n\n    exit(0);\n  }\n  wait(0);\n\n  exit(0);\n}\n\n// if process size was somewhat more than a page boundary, and then\n// shrunk to be somewhat less than that page boundary, can the kernel\n// still copyin() from addresses in the last page?\nvoid\nsbrklast(char *s)\n{\n  uint64 top = (uint64) sbrk(0);\n  if((top % PGSIZE) != 0)\n    sbrk(PGSIZE - (top % PGSIZE));\n  sbrk(PGSIZE);\n  sbrk(10);\n  sbrk(-20);\n  top = (uint64) sbrk(0);\n  char *p = (char *) (top - 64);\n  p[0] = 'x';\n  p[1] = '\\0';\n  int fd = open(p, O_RDWR|O_CREATE);\n  write(fd, p, 1);\n  close(fd);\n  fd = open(p, O_RDWR);\n  p[0] = '\\0';\n  read(fd, p, 1);\n  if(p[0] != 'x')\n    exit(1);\n}\n\n\n// does sbrk handle signed int32 wrap-around with\n// negative arguments?\nvoid\nsbrk8000(char *s)\n{\n  sbrk(0x80000004);\n  volatile char *top = sbrk(0);\n  *(top-1) = *(top-1) + 1;\n}\n\n\n\n// regression test. test whether exec() leaks memory if one of the\n// arguments is invalid. the test passes if the kernel doesn't panic.\nvoid\nbadarg(char *s)\n{\n  for(int i = 0; i < 50000; i++){\n    char *argv[2];\n    argv[0] = (char*)0xffffffff;\n    argv[1] = 0;\n    exec(\"echo\", argv);\n  }\n  \n  exit(0);\n}\n\n#define REGION_SZ (1024 * 1024 * 1024)\n\n// Touch a page every 64 pages, which with lazy allocation\n// causes one page to be allocated.\nvoid\nlazy_alloc(char *s)\n{\n  char *i, *prev_end, *new_end;\n  \n  prev_end = sbrklazy(REGION_SZ);\n  if (prev_end == (char *) SBRK_ERROR) {\n    printf(\"sbrklazy() failed\\n\");\n    exit(1);\n  }\n  new_end = prev_end + REGION_SZ;\n\n  for (i = prev_end + PGSIZE; i < new_end; i += 64 * PGSIZE)\n    *(char **)i = i;\n\n  for (i = prev_end + PGSIZE; i < new_end; i += 64 * PGSIZE) {\n    if (*(char **)i != i) {\n      printf(\"failed to read value from memory\\n\");\n      exit(1);\n    }\n  }\n\n  exit(0);\n}\n\n// Touch a page every 64 pages in region, which with lazy allocation\n// causes one page to be allocated. Check that freeing the region\n// frees the allocated pages.\nvoid\nlazy_unmap(char *s)\n{\n  int pid;\n  char *i, *prev_end, *new_end;\n\n  prev_end = sbrklazy(REGION_SZ);\n  if (prev_end == (char*)SBRK_ERROR) {\n    printf(\"sbrklazy() failed\\n\");\n    exit(1);\n  }\n  new_end = prev_end + REGION_SZ;\n\n  for (i = prev_end + PGSIZE; i < new_end; i += PGSIZE * PGSIZE)\n    *(char **)i = i;\n\n  for (i = prev_end + PGSIZE; i < new_end; i += PGSIZE * PGSIZE) {\n    pid = fork();\n    if (pid < 0) {\n      printf(\"error forking\\n\");\n      exit(1);\n    } else if (pid == 0) {\n      sbrklazy(-1L * REGION_SZ);\n      *(char **)i = i;\n      exit(0);\n    } else {\n      int status;\n      wait(&status);\n      if (status == 0) {\n        printf(\"memory not unmapped\\n\");\n        exit(1);\n      }\n    }\n  }\n\n  exit(0);\n}\n\nvoid\nlazy_copy(char *s)\n{\n  // copyinstr on lazy page\n  {\n    char *p = sbrk(0);\n    sbrklazy(4*PGSIZE);\n    open(p + 8192, 0);\n  }\n  \n  {\n    void *xx = sbrk(0);\n    void *ret = sbrk(-(((uint64) xx)+1));\n    if(ret != xx){\n      printf(\"sbrk(sbrk(0)+1) returned %p, not old sz\\n\", ret);\n      exit(1);\n    }\n  }\n\n  \n  // read() and write() to these addresses should fail.\n  unsigned long bad[] = {\n    0x3fffffc000,\n    0x3fffffd000,\n    0x3fffffe000,\n    0x3ffffff000,\n    0x4000000000,\n    0x8000000000,\n  };\n  for(int i = 0; i < sizeof(bad)/sizeof(bad[0]); i++){\n    int fd = open(\"README\", 0);\n    if(fd < 0) { printf(\"cannot open README\\n\"); exit(1); }\n    if(read(fd, (char*)bad[i], 512) >= 0) { printf(\"read succeeded\\n\");  exit(1); }\n    close(fd);\n    fd = open(\"junk\", O_CREATE|O_RDWR|O_TRUNC);\n    if(fd < 0) { printf(\"cannot open junk\\n\"); exit(1); }\n    if(write(fd, (char*)bad[i], 512) >= 0) { printf(\"write succeeded\\n\"); exit(1); }\n    close(fd);\n  }\n\n  exit(0);\n}\n\nvoid\nlazy_sbrk(char *s)\n{\n  // sbrk() takes just int, so take 2^30-sized steps towards MAXVA\n  char *p = sbrk(0);\n  while ((uint64)p < MAXVA-(1<<30)) {\n    p = sbrklazy(1<<30);\n    if (p < 0) {\n      printf(\"sbrklazy(%d) returned %p\\n\", 1<<30, p);\n      exit(1);\n    }\n\n    p = sbrklazy(0);\n  }\n\n  int n = TRAPFRAME-PGSIZE-(uint64)p;\n\n  char *p1 = sbrklazy(n);\n  if (p1 < 0 || p1 != p) {\n    printf(\"sbrklazy(%d) returned %p, not expected %p\\n\", n, p1, p);\n    exit(1);\n  }\n\n  p = sbrk(PGSIZE);\n  if (p < 0 || (uint64)p != TRAPFRAME-PGSIZE) {\n    printf(\"sbrk(%d) returned %p, not expected TRAPFRAME-PGSIZE\\n\", PGSIZE, p);\n    exit(1);\n  }\n\n  p[0] = 1;\n  if (p[1] != 0) {\n    printf(\"sbrk() returned non-zero-filled memory\\n\");\n    exit(1);\n  }\n\n  p = sbrk(1);\n  if ((uint64)p != -1) {\n    printf(\"sbrk(1) returned %p, expected error\\n\", p);\n    exit(1);\n  }\n\n  p = sbrklazy(1);\n  if ((uint64)p != -1) {\n    printf(\"sbrklazy(1) returned %p, expected error\\n\", p);\n    exit(1);\n  }\n\n  exit(0);\n}\n\nstruct test {\n  void (*f)(char *);\n  char *s;\n} quicktests[] = {\n  {copyin, \"copyin\"},\n  {copyout, \"copyout\"},\n  {copyinstr1, \"copyinstr1\"},\n  {copyinstr2, \"copyinstr2\"},\n  {copyinstr3, \"copyinstr3\"},\n  {rwsbrk, \"rwsbrk\" },\n  {truncate1, \"truncate1\"},\n  {truncate2, \"truncate2\"},\n  {truncate3, \"truncate3\"},\n  {openiputtest, \"openiput\"},\n  {exitiputtest, \"exitiput\"},\n  {iputtest, \"iput\"},\n  {opentest, \"opentest\"},\n  {writetest, \"writetest\"},\n  {writebig, \"writebig\"},\n  {createtest, \"createtest\"},\n  {dirtest, \"dirtest\"},\n  {exectest, \"exectest\"},\n  {pipe1, \"pipe1\"},\n  {killstatus, \"killstatus\"},\n  {preempt, \"preempt\"},\n  {exitwait, \"exitwait\"},\n  {reparent, \"reparent\" },\n  {twochildren, \"twochildren\"},\n  {forkfork, \"forkfork\"},\n  {forkforkfork, \"forkforkfork\"},\n  {reparent2, \"reparent2\"},\n  {mem, \"mem\"},\n  {sharedfd, \"sharedfd\"},\n  {fourfiles, \"fourfiles\"},\n  {createdelete, \"createdelete\"},\n  {unlinkread, \"unlinkread\"},\n  {linktest, \"linktest\"},\n  {concreate, \"concreate\"},\n  {linkunlink, \"linkunlink\"},\n  {subdir, \"subdir\"},\n  {bigwrite, \"bigwrite\"},\n  {bigfile, \"bigfile\"},\n  {fourteen, \"fourteen\"},\n  {rmdot, \"rmdot\"},\n  {dirfile, \"dirfile\"},\n  {iref, \"iref\"},\n  {forktest, \"forktest\"},\n  {sbrkbasic, \"sbrkbasic\"},\n  {sbrkmuch, \"sbrkmuch\"},\n  {kernmem, \"kernmem\"},\n  {MAXVAplus, \"MAXVAplus\"},\n  {sbrkfail, \"sbrkfail\"},\n  {sbrkarg, \"sbrkarg\"},\n  {validatetest, \"validatetest\"},\n  {bsstest, \"bsstest\"},\n  {bigargtest, \"bigargtest\"},\n  {argptest, \"argptest\"},\n  {stacktest, \"stacktest\"},\n  {nowrite, \"nowrite\"},\n  {pgbug, \"pgbug\" },\n  {sbrkbugs, \"sbrkbugs\" },\n  {sbrklast, \"sbrklast\"},\n  {sbrk8000, \"sbrk8000\"},\n  {badarg, \"badarg\" },\n  {lazy_alloc, \"lazy_alloc\"},\n  {lazy_unmap, \"lazy_unmap\"},\n  {lazy_copy, \"lazy_copy\"},\n  {lazy_sbrk, \"lazy_sbrk\"},\n  { 0, 0},\n};\n\n//\n// Section with tests that take a fair bit of time\n//\n\n// directory that uses indirect blocks\nvoid\nbigdir(char *s)\n{\n  enum { N = 500 };\n  int i, fd;\n  char name[10];\n\n  unlink(\"bd\");\n\n  fd = open(\"bd\", O_CREATE);\n  if(fd < 0){\n    printf(\"%s: bigdir create failed\\n\", s);\n    exit(1);\n  }\n  close(fd);\n\n  for(i = 0; i < N; i++){\n    name[0] = 'x';\n    name[1] = '0' + (i / 64);\n    name[2] = '0' + (i % 64);\n    name[3] = '\\0';\n    if(link(\"bd\", name) != 0){\n      printf(\"%s: bigdir i=%d link(bd, %s) failed\\n\", s, i, name);\n      exit(1);\n    }\n  }\n\n  unlink(\"bd\");\n  for(i = 0; i < N; i++){\n    name[0] = 'x';\n    name[1] = '0' + (i / 64);\n    name[2] = '0' + (i % 64);\n    name[3] = '\\0';\n    if(unlink(name) != 0){\n      printf(\"%s: bigdir unlink failed\", s);\n      exit(1);\n    }\n  }\n}\n\n// concurrent writes to try to provoke deadlock in the virtio disk\n// driver.\nvoid\nmanywrites(char *s)\n{\n  int nchildren = 4;\n  int howmany = 30; // increase to look for deadlock\n  \n  for(int ci = 0; ci < nchildren; ci++){\n    int pid = fork();\n    if(pid < 0){\n      printf(\"fork failed\\n\");\n      exit(1);\n    }\n\n    if(pid == 0){\n      char name[3];\n      name[0] = 'b';\n      name[1] = 'a' + ci;\n      name[2] = '\\0';\n      unlink(name);\n      \n      for(int iters = 0; iters < howmany; iters++){\n        for(int i = 0; i < ci+1; i++){\n          int fd = open(name, O_CREATE | O_RDWR);\n          if(fd < 0){\n            printf(\"%s: cannot create %s\\n\", s, name);\n            exit(1);\n          }\n          int sz = sizeof(buf);\n          int cc = write(fd, buf, sz);\n          if(cc != sz){\n            printf(\"%s: write(%d) ret %d\\n\", s, sz, cc);\n            exit(1);\n          }\n          close(fd);\n        }\n        unlink(name);\n      }\n\n      unlink(name);\n      exit(0);\n    }\n  }\n\n  for(int ci = 0; ci < nchildren; ci++){\n    int st = 0;\n    wait(&st);\n    if(st != 0)\n      exit(st);\n  }\n  exit(0);\n}\n\n// regression test. does write() with an invalid buffer pointer cause\n// a block to be allocated for a file that is then not freed when the\n// file is deleted? if the kernel has this bug, it will panic: balloc:\n// out of blocks. assumed_free may need to be raised to be more than\n// the number of free blocks. this test takes a long time.\nvoid\nbadwrite(char *s)\n{\n  int assumed_free = 600;\n  \n  unlink(\"junk\");\n  for(int i = 0; i < assumed_free; i++){\n    int fd = open(\"junk\", O_CREATE|O_WRONLY);\n    if(fd < 0){\n      printf(\"open junk failed\\n\");\n      exit(1);\n    }\n    write(fd, (char*)0xffffffffffL, 1);\n    close(fd);\n    unlink(\"junk\");\n  }\n\n  int fd = open(\"junk\", O_CREATE|O_WRONLY);\n  if(fd < 0){\n    printf(\"open junk failed\\n\");\n    exit(1);\n  }\n  if(write(fd, \"x\", 1) != 1){\n    printf(\"write failed\\n\");\n    exit(1);\n  }\n  close(fd);\n  unlink(\"junk\");\n\n  exit(0);\n}\n\n// test the exec() code that cleans up if it runs out\n// of memory. it's really a test that such a condition\n// doesn't cause a panic.\nvoid\nexecout(char *s)\n{\n  for(int avail = 0; avail < 15; avail++){\n    int pid = fork();\n    if(pid < 0){\n      printf(\"fork failed\\n\");\n      exit(1);\n    } else if(pid == 0){\n      // allocate all of memory.\n      while(1){\n        char *a = sbrk(PGSIZE);\n        if(a == SBRK_ERROR)\n          break;\n        *(a + PGSIZE - 1) = 1;\n      }\n\n      // free a few pages, in order to let exec() make some\n      // progress.\n      for(int i = 0; i < avail; i++)\n        sbrk(-PGSIZE);\n      \n      close(1);\n      char *args[] = { \"echo\", \"x\", 0 };\n      exec(\"echo\", args);\n      exit(0);\n    } else {\n      wait((int*)0);\n    }\n  }\n\n  exit(0);\n}\n\n// can the kernel tolerate running out of disk space?\nvoid\ndiskfull(char *s)\n{\n  int fi;\n  int done = 0;\n\n  unlink(\"diskfulldir\");\n  \n  for(fi = 0; done == 0 && '0' + fi < 0177; fi++){\n    char name[32];\n    name[0] = 'b';\n    name[1] = 'i';\n    name[2] = 'g';\n    name[3] = '0' + fi;\n    name[4] = '\\0';\n    unlink(name);\n    int fd = open(name, O_CREATE|O_RDWR|O_TRUNC);\n    if(fd < 0){\n      // oops, ran out of inodes before running out of blocks.\n      printf(\"%s: could not create file %s\\n\", s, name);\n      done = 1;\n      break;\n    }\n    for(int i = 0; i < MAXFILE; i++){\n      char buf[BSIZE];\n      if(write(fd, buf, BSIZE) != BSIZE){\n        done = 1;\n        close(fd);\n        break;\n      }\n    }\n    close(fd);\n  }\n\n  // now that there are no free blocks, test that dirlink()\n  // merely fails (doesn't panic) if it can't extend\n  // directory content. one of these file creations\n  // is expected to fail.\n  int nzz = 128;\n  for(int i = 0; i < nzz; i++){\n    char name[32];\n    name[0] = 'z';\n    name[1] = 'z';\n    name[2] = '0' + (i / 32);\n    name[3] = '0' + (i % 32);\n    name[4] = '\\0';\n    unlink(name);\n    int fd = open(name, O_CREATE|O_RDWR|O_TRUNC);\n    if(fd < 0)\n      break;\n    close(fd);\n  }\n\n  // this mkdir() is expected to fail.\n  if(mkdir(\"diskfulldir\") == 0)\n    printf(\"%s: mkdir(diskfulldir) unexpectedly succeeded!\\n\", s);\n\n  unlink(\"diskfulldir\");\n\n  for(int i = 0; i < nzz; i++){\n    char name[32];\n    name[0] = 'z';\n    name[1] = 'z';\n    name[2] = '0' + (i / 32);\n    name[3] = '0' + (i % 32);\n    name[4] = '\\0';\n    unlink(name);\n  }\n\n  for(int i = 0; '0' + i < 0177; i++){\n    char name[32];\n    name[0] = 'b';\n    name[1] = 'i';\n    name[2] = 'g';\n    name[3] = '0' + i;\n    name[4] = '\\0';\n    unlink(name);\n  }\n}\n\nvoid\noutofinodes(char *s)\n{\n  int nzz = 32*32;\n  for(int i = 0; i < nzz; i++){\n    char name[32];\n    name[0] = 'z';\n    name[1] = 'z';\n    name[2] = '0' + (i / 32);\n    name[3] = '0' + (i % 32);\n    name[4] = '\\0';\n    unlink(name);\n    int fd = open(name, O_CREATE|O_RDWR|O_TRUNC);\n    if(fd < 0){\n      // failure is eventually expected.\n      break;\n    }\n    close(fd);\n  }\n\n  for(int i = 0; i < nzz; i++){\n    char name[32];\n    name[0] = 'z';\n    name[1] = 'z';\n    name[2] = '0' + (i / 32);\n    name[3] = '0' + (i % 32);\n    name[4] = '\\0';\n    unlink(name);\n  }\n}\n\nstruct test slowtests[] = {\n  {bigdir, \"bigdir\"},\n  {manywrites, \"manywrites\"},\n  {badwrite, \"badwrite\" },\n  {execout, \"execout\"},\n  {diskfull, \"diskfull\"},\n  {outofinodes, \"outofinodes\"},\n    \n  { 0, 0},\n};\n\n//\n// drive tests\n//\n\n// run each test in its own process. run returns 1 if child's exit()\n// indicates success.\nint\nrun(void f(char *), char *s) {\n  int pid;\n  int xstatus;\n\n  printf(\"test %s: \", s);\n  if((pid = fork()) < 0) {\n    printf(\"runtest: fork error\\n\");\n    exit(1);\n  }\n  if(pid == 0) {\n    f(s);\n    exit(0);\n  } else {\n    wait(&xstatus);\n    if(xstatus != 0) \n      printf(\"FAILED\\n\");\n    else\n      printf(\"OK\\n\");\n    return xstatus == 0;\n  }\n}\n\nint\nruntests(struct test *tests, char *justone, int continuous) {\n  int ntests = 0;\n  for (struct test *t = tests; t->s != 0; t++) {\n    if((justone == 0) || strcmp(t->s, justone) == 0) {\n      ntests++;\n      if(!run(t->f, t->s)){\n        if(continuous != 2){\n          printf(\"SOME TESTS FAILED\\n\");\n          return -1;\n        }\n      }\n    }\n  }\n  return ntests;\n}\n\n\n// use sbrk() to count how many free physical memory pages there are.\nint\ncountfree()\n{\n  int n = 0;\n  uint64 sz0 = (uint64)sbrk(0);\n  while(1){\n    char *a = sbrk(PGSIZE);\n    if(a == SBRK_ERROR){\n      break;\n    }\n    n += 1;\n  }\n  sbrk(-((uint64)sbrk(0) - sz0));  \n  return n;\n}\n\nint\ndrivetests(int quick, int continuous, char *justone) {\n  do {\n    printf(\"usertests starting\\n\");\n    int free0 = countfree();\n    int free1 = 0;\n    int ntests = 0;\n    int n;\n    n = runtests(quicktests, justone, continuous);\n    if (n < 0) {\n      if(continuous != 2) {\n        return 1;\n      }\n    } else {\n      ntests += n;\n    }\n    if(!quick) {\n      if (justone == 0)\n        printf(\"usertests slow tests starting\\n\");\n      n = runtests(slowtests, justone, continuous);\n      if (n < 0) {\n        if(continuous != 2) {\n          return 1;\n        }\n      } else {\n        ntests += n;\n      }\n    }\n    if((free1 = countfree()) < free0) {\n      printf(\"FAILED -- lost some free pages %d (out of %d)\\n\", free1, free0);\n      if(continuous != 2) {\n        return 1;\n      }\n    }\n    if (justone != 0 && ntests == 0) {\n      printf(\"NO TESTS EXECUTED\\n\");\n      return 1;\n    }\n  } while(continuous);\n  return 0;\n}\n\nint\nmain(int argc, char *argv[])\n{\n  int continuous = 0;\n  int quick = 0;\n  char *justone = 0;\n\n  if(argc == 2 && strcmp(argv[1], \"-q\") == 0){\n    quick = 1;\n  } else if(argc == 2 && strcmp(argv[1], \"-c\") == 0){\n    continuous = 1;\n  } else if(argc == 2 && strcmp(argv[1], \"-C\") == 0){\n    continuous = 2;\n  } else if(argc == 2 && argv[1][0] != '-'){\n    justone = argv[1];\n  } else if(argc > 1){\n    printf(\"Usage: usertests [-c] [-C] [-q] [testname]\\n\");\n    exit(1);\n  }\n  if (drivetests(quick, continuous, justone)) {\n    exit(1);\n  }\n  printf(\"ALL TESTS PASSED\\n\");\n  exit(0);\n}\n"
  },
  {
    "path": "user/usys.pl",
    "content": "#!/usr/bin/perl -w\n\n# Generate usys.S, the stubs for syscalls.\n\nprint \"# generated by usys.pl - do not edit\\n\";\n\nprint \"#include \\\"kernel/syscall.h\\\"\\n\";\n\nsub entry {\n    my $prefix = \"sys_\";\n    my $name = shift;\n    if ($name eq \"sbrk\") {\n\tprint \".global $prefix$name\\n\";\n\tprint \"$prefix$name:\\n\";\n    } else {\n\tprint \".global $name\\n\";\n\tprint \"$name:\\n\";\n    }\n    print \" li a7, SYS_${name}\\n\";\n    print \" ecall\\n\";\n    print \" ret\\n\";\n}\n\t\nentry(\"fork\");\nentry(\"exit\");\nentry(\"wait\");\nentry(\"pipe\");\nentry(\"read\");\nentry(\"write\");\nentry(\"close\");\nentry(\"kill\");\nentry(\"exec\");\nentry(\"open\");\nentry(\"mknod\");\nentry(\"unlink\");\nentry(\"fstat\");\nentry(\"link\");\nentry(\"mkdir\");\nentry(\"chdir\");\nentry(\"dup\");\nentry(\"getpid\");\nentry(\"sbrk\");\nentry(\"pause\");\nentry(\"uptime\");\n"
  },
  {
    "path": "user/wc.c",
    "content": "#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"kernel/fcntl.h\"\n#include \"user/user.h\"\n\nchar buf[512];\n\nvoid\nwc(int fd, char *name)\n{\n  int i, n;\n  int l, w, c, inword;\n\n  l = w = c = 0;\n  inword = 0;\n  while((n = read(fd, buf, sizeof(buf))) > 0){\n    for(i=0; i<n; i++){\n      c++;\n      if(buf[i] == '\\n')\n        l++;\n      if(strchr(\" \\r\\t\\n\\v\", buf[i]))\n        inword = 0;\n      else if(!inword){\n        w++;\n        inword = 1;\n      }\n    }\n  }\n  if(n < 0){\n    printf(\"wc: read error\\n\");\n    exit(1);\n  }\n  printf(\"%d %d %d %s\\n\", l, w, c, name);\n}\n\nint\nmain(int argc, char *argv[])\n{\n  int fd, i;\n\n  if(argc <= 1){\n    wc(0, \"\");\n    exit(0);\n  }\n\n  for(i = 1; i < argc; i++){\n    if((fd = open(argv[i], O_RDONLY)) < 0){\n      printf(\"wc: cannot open %s\\n\", argv[i]);\n      exit(1);\n    }\n    wc(fd, argv[i]);\n    close(fd);\n  }\n  exit(0);\n}\n"
  },
  {
    "path": "user/zombie.c",
    "content": "// Create a zombie process that\n// must be reparented at exit.\n\n#include \"kernel/types.h\"\n#include \"kernel/stat.h\"\n#include \"user/user.h\"\n\nint\nmain(void)\n{\n  if(fork() > 0)\n    pause(5);  // Let child exit before parent.\n  exit(0);\n}\n"
  }
]