diff options
| -rw-r--r-- | .editorconfig | 22 | ||||
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | kernel/bio.c | 8 | ||||
| -rw-r--r-- | kernel/buf.h | 1 | ||||
| -rw-r--r-- | kernel/console.c | 10 | ||||
| -rw-r--r-- | kernel/defs.h | 3 | ||||
| -rw-r--r-- | kernel/exec.c | 7 | ||||
| -rw-r--r-- | kernel/fcntl.h | 1 | ||||
| -rw-r--r-- | kernel/fs.c | 12 | ||||
| -rw-r--r-- | kernel/kernel.ld | 36 | ||||
| -rw-r--r-- | kernel/memlayout.h | 2 | ||||
| -rw-r--r-- | kernel/pipe.c | 6 | ||||
| -rw-r--r-- | kernel/plic.c | 16 | ||||
| -rw-r--r-- | kernel/proc.c | 32 | ||||
| -rw-r--r-- | kernel/proc.h | 8 | ||||
| -rw-r--r-- | kernel/riscv.h | 1 | ||||
| -rw-r--r-- | kernel/spinlock.c | 16 | ||||
| -rw-r--r-- | kernel/start.c | 1 | ||||
| -rw-r--r-- | kernel/syscall.c | 18 | ||||
| -rw-r--r-- | kernel/sysfile.c | 4 | ||||
| -rw-r--r-- | kernel/trampoline.S | 12 | ||||
| -rw-r--r-- | kernel/trap.c | 29 | ||||
| -rw-r--r-- | kernel/uart.c | 121 | ||||
| -rw-r--r-- | kernel/vm.c | 11 | ||||
| -rw-r--r-- | user/alarmtest.c | 88 | ||||
| -rw-r--r-- | user/cat.c | 8 | ||||
| -rw-r--r-- | user/grind.c | 333 | ||||
| -rw-r--r-- | user/sh.c | 2 | ||||
| -rw-r--r-- | user/ulib.c | 31 | ||||
| -rw-r--r-- | user/user.h | 2 | ||||
| -rw-r--r-- | user/usertests.c | 471 | 
31 files changed, 1074 insertions, 251 deletions
| diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c47611e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +; https://editorconfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.{c,h}] +indent_size = 2 + +[*.S] +indent_size = 8 + +[*.ld] +indent_size = 2 + +[Makefile] +indent_style = tab +indent_size = 8 @@ -40,8 +40,10 @@ TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big'  	then echo 'riscv64-unknown-elf-'; \  	elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \  	then echo 'riscv64-linux-gnu-'; \ +	elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ +	then echo 'riscv64-unknown-linux-gnu-'; \  	else echo "***" 1>&2; \ -	echo "*** Error: Couldn't find an riscv64 version of GCC/binutils." 1>&2; \ +	echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \  	echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \  	echo "***" 1>&2; exit 1; fi)  endif @@ -104,7 +106,7 @@ $U/_forktest: $U/forktest.o $(ULIB)  	$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o  	$(OBJDUMP) -S $U/_forktest > $U/forktest.asm -mkfs/mkfs: mkfs/mkfs.c $K/fs.h +mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h  	gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c  # Prevent deletion of intermediate files, e.g. cat.o, after first build, so @@ -127,6 +129,7 @@ UPROGS=\  	$U/_sh\  	$U/_stressfs\  	$U/_usertests\ +	$U/_grind\  	$U/_wc\  	$U/_zombie\ @@ -153,9 +156,9 @@ ifndef CPUS  CPUS := 3  endif -QEMUEXTRA = -drive file=fs1.img,if=none,format=raw,id=x1 -device virtio-blk-device,drive=x1,bus=virtio-mmio-bus.1 -QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 3G -smp $(CPUS) -nographic -QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 +QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic +QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 +QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0  qemu: $K/kernel fs.img  	$(QEMU) $(QEMUOPTS) diff --git a/kernel/bio.c b/kernel/bio.c index a1074f2..60d91a6 100644 --- a/kernel/bio.c +++ b/kernel/bio.c @@ -28,7 +28,8 @@ struct {    struct buf buf[NBUF];    // Linked list of all buffers, through prev/next. -  // head.next is most recently used. +  // Sorted by how recently the buffer was used. +  // head.next is most recent, head.prev is least.    struct buf head;  } bcache; @@ -71,7 +72,8 @@ bget(uint dev, uint blockno)      }    } -  // Not cached; recycle an unused buffer. +  // Not cached. +  // Recycle the least recently used (LRU) unused buffer.    for(b = bcache.head.prev; b != &bcache.head; b = b->prev){      if(b->refcnt == 0) {        b->dev = dev; @@ -110,7 +112,7 @@ bwrite(struct buf *b)  }  // Release a locked buffer. -// Move to the head of the MRU list. +// Move to the head of the most-recently-used list.  void  brelse(struct buf *b)  { diff --git a/kernel/buf.h b/kernel/buf.h index 4a3a39d..4616e9e 100644 --- a/kernel/buf.h +++ b/kernel/buf.h @@ -7,7 +7,6 @@ struct buf {    uint refcnt;    struct buf *prev; // LRU cache list    struct buf *next; -  struct buf *qnext; // disk queue    uchar data[BSIZE];  }; diff --git a/kernel/console.c b/kernel/console.c index 87a83ff..2b1ed3c 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -27,6 +27,8 @@  //  // send one character to the uart. +// called by printf, and to echo input characters, +// but not from write().  //  void  consputc(int c) @@ -40,9 +42,9 @@ consputc(int c)    if(c == BACKSPACE){      // if the user typed backspace, overwrite with a space. -    uartputc('\b'); uartputc(' '); uartputc('\b'); +    uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');    } else { -    uartputc(c); +    uartputc_sync(c);    }  } @@ -70,11 +72,11 @@ consolewrite(int user_src, uint64 src, int n)      char c;      if(either_copyin(&c, user_src, src+i, 1) == -1)        break; -    consputc(c); +    uartputc(c);    }    release(&cons.lock); -  return n; +  return i;  }  // diff --git a/kernel/defs.h b/kernel/defs.h index f893d28..4eedd89 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -52,6 +52,7 @@ struct inode*   nameiparent(char*, char*);  int             readi(struct inode*, int, uint64, uint, uint);  void            stati(struct inode*, struct stat*);  int             writei(struct inode*, int, uint64, uint, uint); +void            itrunc(struct inode*);  // ramdisk.c  void            ramdiskinit(void); @@ -149,6 +150,7 @@ void            usertrapret(void);  void            uartinit(void);  void            uartintr(void);  void            uartputc(int); +void            uartputc_sync(int);  int             uartgetc(void);  // vm.c @@ -173,7 +175,6 @@ int             copyinstr(pagetable_t, char *, uint64, uint64);  // plic.c  void            plicinit(void);  void            plicinithart(void); -uint64          plic_pending(void);  int             plic_claim(void);  void            plic_complete(int); diff --git a/kernel/exec.c b/kernel/exec.c index 74ef654..1077ac0 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -97,7 +97,7 @@ exec(char *path, char **argv)    // arguments to user main(argc, argv)    // argc is returned via the system call return    // value, which goes in a0. -  p->tf->a1 = sp; +  p->trapframe->a1 = sp;    // Save program name for debugging.    for(last=s=path; *s; s++) @@ -109,9 +109,10 @@ exec(char *path, char **argv)    oldpagetable = p->pagetable;    p->pagetable = pagetable;    p->sz = sz; -  p->tf->epc = elf.entry;  // initial program counter = main -  p->tf->sp = sp; // initial stack pointer +  p->trapframe->epc = elf.entry;  // initial program counter = main +  p->trapframe->sp = sp; // initial stack pointer    proc_freepagetable(oldpagetable, oldsz); +    return argc; // this ends up in a0, the first argument to main(argc, argv)   bad: diff --git a/kernel/fcntl.h b/kernel/fcntl.h index d565483..44861b9 100644 --- a/kernel/fcntl.h +++ b/kernel/fcntl.h @@ -2,3 +2,4 @@  #define O_WRONLY  0x001  #define O_RDWR    0x002  #define O_CREATE  0x200 +#define O_TRUNC   0x400 diff --git a/kernel/fs.c b/kernel/fs.c index 53586d5..ec68cd7 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -22,7 +22,6 @@  #include "file.h"  #define min(a, b) ((a) < (b) ? (a) : (b)) -static void itrunc(struct inode*);  // there should be one superblock per disk device, but we run with  // only one device  struct superblock sb;  @@ -406,11 +405,8 @@ bmap(struct inode *ip, uint bn)  }  // Truncate inode (discard contents). -// Only called when the inode has no links -// to it (no directory entries referring to it) -// and has no in-memory reference to it (is -// not an open file or current directory). -static void +// Caller must hold ip->lock. +void  itrunc(struct inode *ip)  {    int i, j; @@ -463,7 +459,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)    struct buf *bp;    if(off > ip->size || off + n < off) -    return -1; +    return 0;    if(off + n > ip->size)      n = ip->size - off; @@ -476,7 +472,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)      }      brelse(bp);    } -  return n; +  return tot;  }  // Write data to inode. diff --git a/kernel/kernel.ld b/kernel/kernel.ld index acc3c8e..ee04f22 100644 --- a/kernel/kernel.ld +++ b/kernel/kernel.ld @@ -8,25 +8,37 @@ SECTIONS     * where qemu's -kernel jumps.     */    . = 0x80000000; -  .text : -  { -    *(.text) + +  .text : { +    *(.text .text.*)      . = ALIGN(0x1000); +    _trampoline = .;      *(trampsec) +    . = ALIGN(0x1000); +    ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page"); +    PROVIDE(etext = .);    } -  . = ALIGN(0x1000); -  PROVIDE(etext = .); +  .rodata : { +    . = ALIGN(16); +    *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ +    . = ALIGN(16); +    *(.rodata .rodata.*) +  } -  /* -   * make sure end is after data and bss. -   */    .data : { -    *(.data) +    . = ALIGN(16); +    *(.sdata .sdata.*) /* do not need to distinguish this from .data */ +    . = ALIGN(16); +    *(.data .data.*)    } +    .bss : { -    *(.bss) -    *(.sbss*) -     PROVIDE(end = .); +    . = ALIGN(16); +    *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ +    . = ALIGN(16); +    *(.bss .bss.*)    } + +  PROVIDE(end = .);  } diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 8ffd538..b6e595d 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -62,6 +62,6 @@  //   fixed-size stack  //   expandable heap  //   ... -//   TRAPFRAME (p->tf, used by the trampoline) +//   TRAPFRAME (p->trapframe, used by the trampoline)  //   TRAMPOLINE (the same page as in the kernel)  #define TRAPFRAME (TRAMPOLINE - PGSIZE) diff --git a/kernel/pipe.c b/kernel/pipe.c index e358283..7ed402d 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -83,7 +83,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)    acquire(&pi->lock);    for(i = 0; i < n; i++){      while(pi->nwrite == pi->nread + PIPESIZE){  //DOC: pipewrite-full -      if(pi->readopen == 0 || myproc()->killed){ +      if(pi->readopen == 0 || pr->killed){          release(&pi->lock);          return -1;        } @@ -96,7 +96,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)    }    wakeup(&pi->nread);    release(&pi->lock); -  return n; +  return i;  }  int @@ -108,7 +108,7 @@ piperead(struct pipe *pi, uint64 addr, int n)    acquire(&pi->lock);    while(pi->nread == pi->nwrite && pi->writeopen){  //DOC: pipe-empty -    if(myproc()->killed){ +    if(pr->killed){        release(&pi->lock);        return -1;      } diff --git a/kernel/plic.c b/kernel/plic.c index b569492..5acba39 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -28,26 +28,11 @@ plicinithart(void)    *(uint32*)PLIC_SPRIORITY(hart) = 0;  } -// return a bitmap of which IRQs are waiting -// to be served. -uint64 -plic_pending(void) -{ -  uint64 mask; - -  //mask = *(uint32*)(PLIC + 0x1000); -  //mask |= (uint64)*(uint32*)(PLIC + 0x1004) << 32; -  mask = *(uint64*)PLIC_PENDING; - -  return mask; -} -  // ask the PLIC what interrupt we should serve.  int  plic_claim(void)  {    int hart = cpuid(); -  //int irq = *(uint32*)(PLIC + 0x201004);    int irq = *(uint32*)PLIC_SCLAIM(hart);    return irq;  } @@ -57,6 +42,5 @@ void  plic_complete(int irq)  {    int hart = cpuid(); -  //*(uint32*)(PLIC + 0x201004) = irq;    *(uint32*)PLIC_SCLAIM(hart) = irq;  } diff --git a/kernel/proc.c b/kernel/proc.c index bbf3af0..cf7f260 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -20,6 +20,7 @@ static void wakeup1(struct proc *chan);  extern char trampoline[]; // trampoline.S +// initialize the proc table at boot time.  void  procinit(void)  { @@ -106,7 +107,7 @@ found:    p->pid = allocpid();    // Allocate a trapframe page. -  if((p->tf = (struct trapframe *)kalloc()) == 0){ +  if((p->trapframe = (struct trapframe *)kalloc()) == 0){      release(&p->lock);      return 0;    } @@ -116,7 +117,7 @@ found:    // Set up new context to start executing at forkret,    // which returns to user space. -  memset(&p->context, 0, sizeof p->context); +  memset(&p->context, 0, sizeof(p->context));    p->context.ra = (uint64)forkret;    p->context.sp = p->kstack + PGSIZE; @@ -129,9 +130,9 @@ found:  static void  freeproc(struct proc *p)  { -  if(p->tf) -    kfree((void*)p->tf); -  p->tf = 0; +  if(p->trapframe) +    kfree((void*)p->trapframe); +  p->trapframe = 0;    if(p->pagetable)      proc_freepagetable(p->pagetable, p->sz);    p->pagetable = 0; @@ -145,8 +146,8 @@ freeproc(struct proc *p)    p->state = UNUSED;  } -// Create a page table for a given process, -// with no user pages, but with trampoline pages. +// Create a user page table for a given process, +// with no user memory, but with trampoline pages.  pagetable_t  proc_pagetable(struct proc *p)  { @@ -164,7 +165,7 @@ proc_pagetable(struct proc *p)    // map the trapframe just below TRAMPOLINE, for trampoline.S.    mappages(pagetable, TRAPFRAME, PGSIZE, -           (uint64)(p->tf), PTE_R | PTE_W); +           (uint64)(p->trapframe), PTE_R | PTE_W);    return pagetable;  } @@ -176,8 +177,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)  {    uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0);    uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0); -  if(sz > 0) -    uvmfree(pagetable, sz); +  uvmfree(pagetable, sz);  }  // a user program that calls exec("/init") @@ -207,8 +207,8 @@ userinit(void)    p->sz = PGSIZE;    // prepare for the very first "return" from kernel to user. -  p->tf->epc = 0;      // user program counter -  p->tf->sp = PGSIZE;  // user stack pointer +  p->trapframe->epc = 0;      // user program counter +  p->trapframe->sp = PGSIZE;  // user stack pointer    safestrcpy(p->name, "initcode", sizeof(p->name));    p->cwd = namei("/"); @@ -263,10 +263,10 @@ fork(void)    np->parent = p;    // copy saved user registers. -  *(np->tf) = *(p->tf); +  *(np->trapframe) = *(p->trapframe);    // Cause fork to return 0 in the child. -  np->tf->a0 = 0; +  np->trapframe->a0 = 0;    // increment reference counts on open file descriptors.    for(i = 0; i < NOFILE; i++) @@ -457,7 +457,7 @@ scheduler(void)          // before jumping back to us.          p->state = RUNNING;          c->proc = p; -        swtch(&c->scheduler, &p->context); +        swtch(&c->context, &p->context);          // Process is done running for now.          // It should have changed its p->state before coming back. @@ -491,7 +491,7 @@ sched(void)      panic("sched interruptible");    intena = mycpu()->intena; -  swtch(&p->context, &mycpu()->scheduler); +  swtch(&p->context, &mycpu()->context);    mycpu()->intena = intena;  } diff --git a/kernel/proc.h b/kernel/proc.h index 538b48a..9c16ea7 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -21,7 +21,7 @@ struct context {  // Per-CPU state.  struct cpu {    struct proc *proc;          // The process running on this cpu, or null. -  struct context scheduler;   // swtch() here to enter scheduler(). +  struct context context;     // swtch() here to enter scheduler().    int noff;                   // Depth of push_off() nesting.    int intena;                 // Were interrupts enabled before push_off()?  }; @@ -95,10 +95,10 @@ struct proc {    int pid;                     // Process ID    // these are private to the process, so p->lock need not be held. -  uint64 kstack;               // Bottom of kernel stack for this process +  uint64 kstack;               // Virtual address of kernel stack    uint64 sz;                   // Size of process memory (bytes) -  pagetable_t pagetable;       // Page table -  struct trapframe *tf;        // data page for trampoline.S +  pagetable_t pagetable;       // User page table +  struct trapframe *trapframe; // data page for trampoline.S    struct context context;      // swtch() here to run process    struct file *ofile[NOFILE];  // Open files    struct inode *cwd;           // Current directory diff --git a/kernel/riscv.h b/kernel/riscv.h index f46ba59..0aec003 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -261,7 +261,6 @@ r_time()  static inline void  intr_on()  { -  w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);    w_sstatus(r_sstatus() | SSTATUS_SIE);  } diff --git a/kernel/spinlock.c b/kernel/spinlock.c index 563532e..9840302 100644 --- a/kernel/spinlock.c +++ b/kernel/spinlock.c @@ -34,7 +34,8 @@ acquire(struct spinlock *lk)    // Tell the C compiler and the processor to not move loads or stores    // past this point, to ensure that the critical section's memory -  // references happen after the lock is acquired. +  // references happen strictly after the lock is acquired. +  // On RISC-V, this emits a fence instruction.    __sync_synchronize();    // Record info about lock acquisition for holding() and debugging. @@ -52,8 +53,10 @@ release(struct spinlock *lk)    // Tell the C compiler and the CPU to not move loads or stores    // past this point, to ensure that all the stores in the critical -  // section are visible to other CPUs before the lock is released. -  // On RISC-V, this turns into a fence instruction. +  // section are visible to other CPUs before the lock is released, +  // and that loads in the critical section occur strictly before +  // the lock is released. +  // On RISC-V, this emits a fence instruction.    __sync_synchronize();    // Release the lock, equivalent to lk->locked = 0. @@ -69,13 +72,12 @@ release(struct spinlock *lk)  }  // Check whether this cpu is holding the lock. +// Interrupts must be off.  int  holding(struct spinlock *lk)  {    int r; -  push_off();    r = (lk->locked && lk->cpu == mycpu()); -  pop_off();    return r;  } @@ -100,9 +102,9 @@ pop_off(void)    struct cpu *c = mycpu();    if(intr_get())      panic("pop_off - interruptible"); -  c->noff -= 1; -  if(c->noff < 0) +  if(c->noff < 1)      panic("pop_off"); +  c->noff -= 1;    if(c->noff == 0 && c->intena)      intr_on();  } diff --git a/kernel/start.c b/kernel/start.c index 203c5e6..4eb6c2d 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -36,6 +36,7 @@ start()    // delegate all interrupts and exceptions to supervisor mode.    w_medeleg(0xffff);    w_mideleg(0xffff); +  w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);    // ask for clock interrupts.    timerinit(); diff --git a/kernel/syscall.c b/kernel/syscall.c index 2817e44..c1b3670 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -37,17 +37,17 @@ argraw(int n)    struct proc *p = myproc();    switch (n) {    case 0: -    return p->tf->a0; +    return p->trapframe->a0;    case 1: -    return p->tf->a1; +    return p->trapframe->a1;    case 2: -    return p->tf->a2; +    return p->trapframe->a2;    case 3: -    return p->tf->a3; +    return p->trapframe->a3;    case 4: -    return p->tf->a4; +    return p->trapframe->a4;    case 5: -    return p->tf->a5; +    return p->trapframe->a5;    }    panic("argraw");    return -1; @@ -135,12 +135,12 @@ syscall(void)    int num;    struct proc *p = myproc(); -  num = p->tf->a7; +  num = p->trapframe->a7;    if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { -    p->tf->a0 = syscalls[num](); +    p->trapframe->a0 = syscalls[num]();    } else {      printf("%d %s: unknown sys call %d\n",              p->pid, p->name, num); -    p->tf->a0 = -1; +    p->trapframe->a0 = -1;    }  } diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 7768d20..015c942 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -341,6 +341,10 @@ sys_open(void)    f->readable = !(omode & O_WRONLY);    f->writable = (omode & O_WRONLY) || (omode & O_RDWR); +  if((omode & O_TRUNC) && ip->type == T_FILE){ +    itrunc(ip); +  } +    iunlock(ip);    end_op(); diff --git a/kernel/trampoline.S b/kernel/trampoline.S index b113bf6..fabaaf9 100644 --- a/kernel/trampoline.S +++ b/kernel/trampoline.S @@ -20,7 +20,7 @@ uservec:          # in supervisor mode, but with a          # user page table.          # -        # sscratch points to where the process's p->tf is +        # sscratch points to where the process's p->trapframe is          # mapped into user space, at TRAPFRAME.          # @@ -60,20 +60,20 @@ uservec:          sd t5, 272(a0)          sd t6, 280(a0) -	# save the user a0 in p->tf->a0 +	# save the user a0 in p->trapframe->a0          csrr t0, sscratch          sd t0, 112(a0) -        # restore kernel stack pointer from p->tf->kernel_sp +        # restore kernel stack pointer from p->trapframe->kernel_sp          ld sp, 8(a0) -        # make tp hold the current hartid, from p->tf->kernel_hartid +        # make tp hold the current hartid, from p->trapframe->kernel_hartid          ld tp, 32(a0) -        # load the address of usertrap(), p->tf->kernel_trap +        # load the address of usertrap(), p->trapframe->kernel_trap          ld t0, 16(a0) -        # restore kernel page table from p->tf->kernel_satp +        # restore kernel page table from p->trapframe->kernel_satp          ld t1, 0(a0)          csrw satp, t1          sfence.vma zero, zero diff --git a/kernel/trap.c b/kernel/trap.c index ca732f2..a63249e 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -48,7 +48,7 @@ usertrap(void)    struct proc *p = myproc();    // save user program counter. -  p->tf->epc = r_sepc(); +  p->trapframe->epc = r_sepc();    if(r_scause() == 8){      // system call @@ -58,7 +58,7 @@ usertrap(void)      // sepc points to the ecall instruction,      // but we want to return to the next instruction. -    p->tf->epc += 4; +    p->trapframe->epc += 4;      // an interrupt will change sstatus &c registers,      // so don't enable until done with those registers. @@ -91,8 +91,9 @@ usertrapret(void)  {    struct proc *p = myproc(); -  // turn off interrupts, since we're switching -  // now from kerneltrap() to usertrap(). +  // we're about to switch the destination of traps from +  // kerneltrap() to usertrap(), so turn off interrupts until +  // we're back in user space, where usertrap() is correct.    intr_off();    // send syscalls, interrupts, and exceptions to trampoline.S @@ -100,10 +101,10 @@ usertrapret(void)    // set up trapframe values that uservec will need when    // the process next re-enters the kernel. -  p->tf->kernel_satp = r_satp();         // kernel page table -  p->tf->kernel_sp = p->kstack + PGSIZE; // process's kernel stack -  p->tf->kernel_trap = (uint64)usertrap; -  p->tf->kernel_hartid = r_tp();         // hartid for cpuid() +  p->trapframe->kernel_satp = r_satp();         // kernel page table +  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack +  p->trapframe->kernel_trap = (uint64)usertrap; +  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()    // set up the registers that trampoline.S's sret will use    // to get to user space. @@ -115,7 +116,7 @@ usertrapret(void)    w_sstatus(x);    // set S Exception Program Counter to the saved user pc. -  w_sepc(p->tf->epc); +  w_sepc(p->trapframe->epc);    // tell trampoline.S the user page table to switch to.    uint64 satp = MAKE_SATP(p->pagetable); @@ -129,7 +130,6 @@ usertrapret(void)  // interrupts and exceptions from kernel code go here via kernelvec,  // on whatever the current kernel stack is. -// must be 4-byte aligned to fit in stvec.  void   kerneltrap()  { @@ -189,9 +189,16 @@ devintr()        uartintr();      } else if(irq == VIRTIO0_IRQ){        virtio_disk_intr(); +    } else if(irq){ +      printf("unexpected interrupt irq=%d\n", irq);      } -    plic_complete(irq); +    // the PLIC allows each device to raise at most one +    // interrupt at a time; tell the PLIC the device is +    // now allowed to interrupt again. +    if(irq) +      plic_complete(irq); +      return 1;    } else if(scause == 0x8000000000000001L){      // software interrupt from a machine-mode timer interrupt, diff --git a/kernel/uart.c b/kernel/uart.c index 3a5cdc4..daf9f04 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -18,18 +18,35 @@  // the UART control registers.  // some have different meanings for  // read vs write. -// http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define FCR 2 // FIFO control register -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LSR 5 // line status register +// see http://byterunner.com/16550.html +#define RHR 0                 // receive holding register (for input bytes) +#define THR 0                 // transmit holding register (for output bytes) +#define IER 1                 // interrupt enable register +#define IER_TX_ENABLE (1<<0) +#define IER_RX_ENABLE (1<<1) +#define FCR 2                 // FIFO control register +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs +#define ISR 2                 // interrupt status register +#define LCR 3                 // line control register +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate +#define LSR 5                 // line status register +#define LSR_RX_READY (1<<0)   // input is waiting to be read from RHR +#define LSR_TX_IDLE (1<<5)    // THR can accept another character to send  #define ReadReg(reg) (*(Reg(reg)))  #define WriteReg(reg, v) (*(Reg(reg)) = (v)) +// the transmit output buffer. +struct spinlock uart_tx_lock; +#define UART_TX_BUF_SIZE 32 +char uart_tx_buf[UART_TX_BUF_SIZE]; +int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++] +int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++] + +void uartstart(); +  void  uartinit(void)  { @@ -37,7 +54,7 @@ uartinit(void)    WriteReg(IER, 0x00);    // special mode to set baud rate. -  WriteReg(LCR, 0x80); +  WriteReg(LCR, LCR_BAUD_LATCH);    // LSB for baud rate of 38.4K.    WriteReg(0, 0x03); @@ -47,23 +64,87 @@ uartinit(void)    // leave set-baud mode,    // and set word length to 8 bits, no parity. -  WriteReg(LCR, 0x03); +  WriteReg(LCR, LCR_EIGHT_BITS);    // reset and enable FIFOs. -  WriteReg(FCR, 0x07); +  WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + +  // enable transmit and receive interrupts. +  WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); -  // enable receive interrupts. -  WriteReg(IER, 0x01); +  initlock(&uart_tx_lock, "uart");  } -// write one output character to the UART. +// add a character to the output buffer and tell the +// UART to start sending if it isn't already. +// blocks if the output buffer is full. +// because it may block, it can't be called +// from interrupts; it's only suitable for use +// by write().  void  uartputc(int c)  { +  acquire(&uart_tx_lock); +  while(1){ +    if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ +      // buffer is full. +      // wait for uartstart() to open up space in the buffer. +      sleep(&uart_tx_r, &uart_tx_lock); +    } else { +      uart_tx_buf[uart_tx_w] = c; +      uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE; +      uartstart(); +      release(&uart_tx_lock); +      return; +    } +  } +} + +// alternate version of uartputc() that doesn't  +// use interrupts, for use by kernel printf() and +// to echo characters. it spins waiting for the uart's +// output register to be empty. +void +uartputc_sync(int c) +{ +  push_off(); +    // wait for Transmit Holding Empty to be set in LSR. -  while((ReadReg(LSR) & (1 << 5)) == 0) +  while((ReadReg(LSR) & LSR_TX_IDLE) == 0)      ;    WriteReg(THR, c); + +  pop_off(); +} + +// if the UART is idle, and a character is waiting +// in the transmit buffer, send it. +// caller must hold uart_tx_lock. +// called from both the top- and bottom-half. +void +uartstart() +{ +  while(1){ +    if(uart_tx_w == uart_tx_r){ +      // transmit buffer is empty. +      return; +    } +     +    if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ +      // the UART transmit holding register is full, +      // so we cannot give it another byte. +      // it will interrupt when it's ready for a new byte. +      return; +    } +     +    int c = uart_tx_buf[uart_tx_r]; +    uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE; +     +    // maybe uartputc() is waiting for space in the buffer. +    wakeup(&uart_tx_r); +     +    WriteReg(THR, c); +  }  }  // read one input character from the UART. @@ -79,14 +160,22 @@ uartgetc(void)    }  } -// trap.c calls here when the uart interrupts. +// handle a uart interrupt, raised because input has +// arrived, or the uart is ready for more output, or +// both. called from trap.c.  void  uartintr(void)  { +  // read and process incoming characters.    while(1){      int c = uartgetc();      if(c == -1)        break;      consoleintr(c);    } + +  // send buffered characters. +  acquire(&uart_tx_lock); +  uartstart(); +  release(&uart_tx_lock);  } diff --git a/kernel/vm.c b/kernel/vm.c index 5e0b2aa..636f11a 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -16,9 +16,7 @@ extern char etext[];  // kernel.ld sets this to end of kernel code.  extern char trampoline[]; // trampoline.S  /* - * create a direct-map page table for the kernel and - * turn on paging. called early, in supervisor mode. - * the page allocator is already initialized. + * create a direct-map page table for the kernel.   */  void  kvminit() @@ -70,7 +68,7 @@ kvminithart()  //   21..29 -- 9 bits of level-1 index.  //   12..20 -- 9 bits of level-0 index.  //    0..11 -- 12 bits of byte offset within the page. -static pte_t * +pte_t *  walk(pagetable_t pagetable, uint64 va, int alloc)  {    if(va >= MAXVA) @@ -278,7 +276,7 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)  // Recursively free page-table pages.  // All leaf mappings must already have been removed. -static void +void  freewalk(pagetable_t pagetable)  {    // there are 2^9 = 512 PTEs in a page table. @@ -301,7 +299,8 @@ freewalk(pagetable_t pagetable)  void  uvmfree(pagetable_t pagetable, uint64 sz)  { -  uvmunmap(pagetable, 0, sz, 1); +  if(sz > 0) +    uvmunmap(pagetable, 0, sz, 1);    freewalk(pagetable);  } diff --git a/user/alarmtest.c b/user/alarmtest.c deleted file mode 100644 index ca3db23..0000000 --- a/user/alarmtest.c +++ /dev/null @@ -1,88 +0,0 @@ -// -// test program for the alarm lab. -// you can modify this file for testing, -// but please make sure your kernel -// modifications pass the original -// versions of these tests. -// - -#include "kernel/param.h" -#include "kernel/types.h" -#include "kernel/stat.h" -#include "kernel/riscv.h" -#include "user/user.h" - -void test0(); -void test1(); -void periodic(); - -int -main(int argc, char *argv[]) -{ -  test0(); -  test1(); -  exit(); -} - -volatile static int count; - -void -periodic() -{ -  count = count + 1; -  printf("alarm!\n"); -  sigreturn(); -} - -// tests whether the kernel calls -// the alarm handler even a single time. -void -test0() -{ -  int i; -  printf("test0 start\n"); -  count = 0; -  sigalarm(2, periodic); -  for(i = 0; i < 1000*500000; i++){ -    if((i % 250000) == 0) -      write(2, ".", 1); -    if(count > 0) -      break; -  } -  sigalarm(0, 0); -  if(count > 0){ -    printf("test0 passed\n"); -  } else { -    printf("test0 failed\n"); -  } -} - -void __attribute__ ((noinline)) foo(int i, int *j) { -  if((i % 2500000) == 0) { -    write(2, ".", 1); -  } -  *j += 1; -} - -void -test1() -{ -  int i; -  int j; - -  printf("test1 start\n"); -  count = 0; -  j = 0; -  sigalarm(2, periodic); -  for(i = 0; i < 500000000; i++){ -    if(count >= 10) -      break; -    foo(i, &j); -  } -  if(i != j || count < 10){ -    // i should equal j -    printf("test1 failed\n"); -  } else { -    printf("test1 passed\n"); -  } -} @@ -11,12 +11,12 @@ cat(int fd)    while((n = read(fd, buf, sizeof(buf))) > 0) {      if (write(1, buf, n) != n) { -      printf("cat: write error\n"); +      fprintf(2, "cat: write error\n");        exit(1);      }    }    if(n < 0){ -    printf("cat: read error\n"); +    fprintf(2, "cat: read error\n");      exit(1);    }  } @@ -28,12 +28,12 @@ main(int argc, char *argv[])    if(argc <= 1){      cat(0); -    exit(1); +    exit(0);    }    for(i = 1; i < argc; i++){      if((fd = open(argv[i], 0)) < 0){ -      printf("cat: cannot open %s\n", argv[i]); +      fprintf(2, "cat: cannot open %s\n", argv[i]);        exit(1);      }      cat(fd); diff --git a/user/grind.c b/user/grind.c new file mode 100644 index 0000000..14e2aae --- /dev/null +++ b/user/grind.c @@ -0,0 +1,333 @@ +// +// run random system calls in parallel forever. +// + +#include "kernel/param.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" +#include "kernel/syscall.h" +#include "kernel/memlayout.h" +#include "kernel/riscv.h" + +// from FreeBSD. +int +do_rand(unsigned long *ctx) +{ +/* + * Compute x = (7^5 * x) mod (2^31 - 1) + * without overflowing 31 bits: + *      (2^31 - 1) = 127773 * (7^5) + 2836 + * From "Random number generators: good ones are hard to find", + * Park and Miller, Communications of the ACM, vol. 31, no. 10, + * October 1988, p. 1195. + */ +    long hi, lo, x; + +    /* Transform to [1, 0x7ffffffe] range. */ +    x = (*ctx % 0x7ffffffe) + 1; +    hi = x / 127773; +    lo = x % 127773; +    x = 16807 * lo - 2836 * hi; +    if (x < 0) +        x += 0x7fffffff; +    /* Transform to [0, 0x7ffffffd] range. */ +    x--; +    *ctx = x; +    return (x); +} + +unsigned long rand_next = 1; + +int +rand(void) +{ +    return (do_rand(&rand_next)); +} + +void +go(int which_child) +{ +  int fd = -1; +  static char buf[999]; +  char *break0 = sbrk(0); +  uint64 iters = 0; + +  mkdir("grindir"); +  if(chdir("grindir") != 0){ +    printf("chdir grindir failed\n"); +    exit(1); +  } +  chdir("/"); +   +  while(1){ +    iters++; +    if((iters % 500) == 0) +      write(1, which_child?"B":"A", 1); +    int what = rand() % 23; +    if(what == 1){ +      close(open("grindir/../a", O_CREATE|O_RDWR)); +    } else if(what == 2){ +      close(open("grindir/../grindir/../b", O_CREATE|O_RDWR)); +    } else if(what == 3){ +      unlink("grindir/../a"); +    } else if(what == 4){ +      if(chdir("grindir") != 0){ +        printf("chdir grindir failed\n"); +        exit(1); +      } +      unlink("../b"); +      chdir("/"); +    } else if(what == 5){ +      close(fd); +      fd = open("/grindir/../a", O_CREATE|O_RDWR); +    } else if(what == 6){ +      close(fd); +      fd = open("/./grindir/./../b", O_CREATE|O_RDWR); +    } else if(what == 7){ +      write(fd, buf, sizeof(buf)); +    } else if(what == 8){ +      read(fd, buf, sizeof(buf)); +    } else if(what == 9){ +      mkdir("grindir/../a"); +      close(open("a/../a/./a", O_CREATE|O_RDWR)); +      unlink("a/a"); +    } else if(what == 10){ +      mkdir("/../b"); +      close(open("grindir/../b/b", O_CREATE|O_RDWR)); +      unlink("b/b"); +    } else if(what == 11){ +      unlink("b"); +      link("../grindir/./../a", "../b"); +    } else if(what == 12){ +      unlink("../grindir/../a"); +      link(".././b", "/grindir/../a"); +    } else if(what == 13){ +      int pid = fork(); +      if(pid == 0){ +        exit(0); +      } else if(pid < 0){ +        printf("grind: fork failed\n"); +        exit(1); +      } +      wait(0); +    } else if(what == 14){ +      int pid = fork(); +      if(pid == 0){ +        fork(); +        fork(); +        exit(0); +      } else if(pid < 0){ +        printf("grind: fork failed\n"); +        exit(1); +      } +      wait(0); +    } else if(what == 15){ +      sbrk(6011); +    } else if(what == 16){ +      if(sbrk(0) > break0) +        sbrk(-(sbrk(0) - break0)); +    } else if(what == 17){ +      int pid = fork(); +      if(pid == 0){ +        close(open("a", O_CREATE|O_RDWR)); +        exit(0); +      } else if(pid < 0){ +        printf("grind: fork failed\n"); +        exit(1); +      } +      if(chdir("../grindir/..") != 0){ +        printf("chdir failed\n"); +        exit(1); +      } +      kill(pid); +      wait(0); +    } else if(what == 18){ +      int pid = fork(); +      if(pid == 0){ +        kill(getpid()); +        exit(0); +      } else if(pid < 0){ +        printf("grind: fork failed\n"); +        exit(1); +      } +      wait(0); +    } else if(what == 19){ +      int fds[2]; +      if(pipe(fds) < 0){ +        printf("grind: pipe failed\n"); +        exit(1); +      } +      int pid = fork(); +      if(pid == 0){ +        fork(); +        fork(); +        if(write(fds[1], "x", 1) != 1) +          printf("grind: pipe write failed\n"); +        char c; +        if(read(fds[0], &c, 1) != 1) +          printf("grind: pipe read failed\n"); +        exit(0); +      } else if(pid < 0){ +        printf("grind: fork failed\n"); +        exit(1); +      } +      close(fds[0]); +      close(fds[1]); +      wait(0); +    } else if(what == 20){ +      int pid = fork(); +      if(pid == 0){ +        unlink("a"); +        mkdir("a"); +        chdir("a"); +        unlink("../a"); +        fd = open("x", O_CREATE|O_RDWR); +        unlink("x"); +        exit(0); +      } else if(pid < 0){ +        printf("fork failed\n"); +        exit(1); +      } +      wait(0); +    } else if(what == 21){ +      unlink("c"); +      // should always succeed. check that there are free i-nodes, +      // file descriptors, blocks. +      int fd1 = open("c", O_CREATE|O_RDWR); +      if(fd1 < 0){ +        printf("create c failed\n"); +        exit(1); +      } +      if(write(fd1, "x", 1) != 1){ +        printf("write c failed\n"); +        exit(1); +      } +      struct stat st; +      if(fstat(fd1, &st) != 0){ +        printf("fstat failed\n"); +        exit(1); +      } +      if(st.size != 1){ +        printf("fstat reports wrong size %d\n", (int)st.size); +        exit(1); +      } +      if(st.ino > 200){ +        printf("fstat reports crazy i-number %d\n", st.ino); +        exit(1); +      } +      close(fd1); +      unlink("c"); +    } else if(what == 22){ +      // echo hi | cat +      int aa[2], bb[2]; +      if(pipe(aa) < 0){ +        fprintf(2, "pipe failed\n"); +        exit(1); +      } +      if(pipe(bb) < 0){ +        fprintf(2, "pipe failed\n"); +        exit(1); +      } +      int pid1 = fork(); +      if(pid1 == 0){ +        close(bb[0]); +        close(bb[1]); +        close(aa[0]); +        close(1); +        if(dup(aa[1]) != 1){ +          fprintf(2, "dup failed\n"); +          exit(1); +        } +        close(aa[1]); +        char *args[3] = { "echo", "hi", 0 }; +        exec("grindir/../echo", args); +        fprintf(2, "echo: not found\n"); +        exit(2); +      } else if(pid1 < 0){ +        fprintf(2, "fork failed\n"); +        exit(3); +      } +      int pid2 = fork(); +      if(pid2 == 0){ +        close(aa[1]); +        close(bb[0]); +        close(0); +        if(dup(aa[0]) != 0){ +          fprintf(2, "dup failed\n"); +          exit(4); +        } +        close(aa[0]); +        close(1); +        if(dup(bb[1]) != 1){ +          fprintf(2, "dup failed\n"); +          exit(5); +        } +        close(bb[1]); +        char *args[2] = { "cat", 0 }; +        exec("/cat", args); +        fprintf(2, "cat: not found\n"); +        exit(6); +      } else if(pid2 < 0){ +        fprintf(2, "fork failed\n"); +        exit(7); +      } +      close(aa[0]); +      close(aa[1]); +      close(bb[1]); +      char buf[3] = { 0, 0, 0 }; +      read(bb[0], buf+0, 1); +      read(bb[0], buf+1, 1); +      close(bb[0]); +      int st1, st2; +      wait(&st1); +      wait(&st2); +      if(st1 != 0 || st2 != 0 || strcmp(buf, "hi") != 0){ +        printf("exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); +        exit(1); +      } +    } +  } +} + +int +main() +{ +  unlink("a"); +  unlink("b"); +   +  int pid1 = fork(); +  if(pid1 < 0){ +    printf("grind: fork failed\n"); +    exit(1); +  } +  if(pid1 == 0){ +    rand_next = 31; +    go(0); +    exit(0); +  } + +  int pid2 = fork(); +  if(pid2 < 0){ +    printf("grind: fork failed\n"); +    exit(1); +  } +  if(pid2 == 0){ +    rand_next = 7177; +    go(1); +    exit(0); +  } + +  int st1 = -1; +  wait(&st1); +  if(st1 != 0){ +    kill(pid1); +    kill(pid2); +  } +  int st2 = -1; +  wait(&st2); + +  exit(0); +} @@ -386,7 +386,7 @@ parseredirs(struct cmd *cmd, char **ps, char *es)        cmd = redircmd(cmd, q, eq, O_RDONLY, 0);        break;      case '>': -      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); +      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);        break;      case '+':  // >>        cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); diff --git a/user/ulib.c b/user/ulib.c index ddda0f5..4775939 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -103,7 +103,34 @@ memmove(void *vdst, const void *vsrc, int n)    dst = vdst;    src = vsrc; -  while(n-- > 0) -    *dst++ = *src++; +  if (src > dst) { +    while(n-- > 0) +      *dst++ = *src++; +  } else { +    dst += n; +    src += n; +    while(n-- > 0) +      *--dst = *--src; +  }    return vdst;  } + +int +memcmp(const void *s1, const void *s2, uint n) +{ +  const char *p1 = s1, *p2 = s2; +  while (n-- > 0) { +    if (*p1 != *p2) { +      return *p1 - *p2; +    } +    p1++; +    p2++; +  } +  return 0; +} + +void * +memcpy(void *dst, const void *src, uint n) +{ +  return memmove(dst, src, n); +} diff --git a/user/user.h b/user/user.h index 03af731..b71ecda 100644 --- a/user/user.h +++ b/user/user.h @@ -38,3 +38,5 @@ void* memset(void*, int, uint);  void* malloc(uint);  void free(void*);  int atoi(const char*); +int memcmp(const void *, const void *, uint); +void *memcpy(void *, const void *, uint); diff --git a/user/usertests.c b/user/usertests.c index db9f680..dfe0039 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -22,6 +22,352 @@  char buf[BUFSZ];  char name[3]; +// what if you pass ridiculous pointers to system calls +// that read user memory with copyin? +void +copyin(char *s) +{ +  uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; + +  for(int ai = 0; ai < 2; ai++){ +    uint64 addr = addrs[ai]; +     +    int fd = open("copyin1", O_CREATE|O_WRONLY); +    if(fd < 0){ +      printf("open(copyin1) failed\n"); +      exit(1); +    } +    int n = write(fd, (void*)addr, 8192); +    if(n >= 0){ +      printf("write(fd, %p, 8192) returned %d, not -1\n", addr, n); +      exit(1); +    } +    close(fd); +    unlink("copyin1"); +     +    n = write(1, (char*)addr, 8192); +    if(n > 0){ +      printf("write(1, %p, 8192) returned %d, not -1 or 0\n", addr, n); +      exit(1); +    } +     +    int fds[2]; +    if(pipe(fds) < 0){ +      printf("pipe() failed\n"); +      exit(1); +    } +    n = write(fds[1], (char*)addr, 8192); +    if(n > 0){ +      printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); +      exit(1); +    } +    close(fds[0]); +    close(fds[1]); +  } +} + +// what if you pass ridiculous pointers to system calls +// that write user memory with copyout? +void +copyout(char *s) +{ +  uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; + +  for(int ai = 0; ai < 2; ai++){ +    uint64 addr = addrs[ai]; + +    int fd = open("README", 0); +    if(fd < 0){ +      printf("open(README) failed\n"); +      exit(1); +    } +    int n = read(fd, (void*)addr, 8192); +    if(n > 0){ +      printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", addr, n); +      exit(1); +    } +    close(fd); + +    int fds[2]; +    if(pipe(fds) < 0){ +      printf("pipe() failed\n"); +      exit(1); +    } +    n = write(fds[1], "x", 1); +    if(n != 1){ +      printf("pipe write failed\n"); +      exit(1); +    } +    n = read(fds[0], (void*)addr, 8192); +    if(n > 0){ +      printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); +      exit(1); +    } +    close(fds[0]); +    close(fds[1]); +  } +} + +// what if you pass ridiculous string pointers to system calls? +void +copyinstr1(char *s) +{ +  uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; + +  for(int ai = 0; ai < 2; ai++){ +    uint64 addr = addrs[ai]; + +    int fd = open((char *)addr, O_CREATE|O_WRONLY); +    if(fd >= 0){ +      printf("open(%p) returned %d, not -1\n", addr, fd); +      exit(1); +    } +  } +} + +// what if a string system call argument is exactly the size +// of the kernel buffer it is copied into, so that the null +// would fall just beyond the end of the kernel buffer? +void +copyinstr2(char *s) +{ +  char b[MAXPATH+1]; + +  for(int i = 0; i < MAXPATH; i++) +    b[i] = 'x'; +  b[MAXPATH] = '\0'; +   +  int ret = unlink(b); +  if(ret != -1){ +    printf("unlink(%s) returned %d, not -1\n", b, ret); +    exit(1); +  } + +  int fd = open(b, O_CREATE | O_WRONLY); +  if(fd != -1){ +    printf("open(%s) returned %d, not -1\n", b, fd); +    exit(1); +  } + +  ret = link(b, b); +  if(ret != -1){ +    printf("link(%s, %s) returned %d, not -1\n", b, b, ret); +    exit(1); +  } + +  char *args[] = { "xx", 0 }; +  ret = exec(b, args); +  if(ret != -1){ +    printf("exec(%s) returned %d, not -1\n", b, fd); +    exit(1); +  } + +  int pid = fork(); +  if(pid < 0){ +    printf("fork failed\n"); +    exit(1); +  } +  if(pid == 0){ +    static char big[PGSIZE+1]; +    for(int i = 0; i < PGSIZE; i++) +      big[i] = 'x'; +    big[PGSIZE] = '\0'; +    char *args2[] = { big, big, big, 0 }; +    ret = exec("echo", args2); +    if(ret != -1){ +      printf("exec(echo, BIG) returned %d, not -1\n", fd); +      exit(1); +    } +    exit(747); // OK +  } + +  int st = 0; +  wait(&st); +  if(st != 747){ +    printf("exec(echo, BIG) succeeded, should have failed\n"); +    exit(1); +  } +} + +// what if a string argument crosses over the end of last user page? +void +copyinstr3(char *s) +{ +  sbrk(8192); +  uint64 top = (uint64) sbrk(0); +  if((top % PGSIZE) != 0){ +    sbrk(PGSIZE - (top % PGSIZE)); +  } +  top = (uint64) sbrk(0); +  if(top % PGSIZE){ +    printf("oops\n"); +    exit(1); +  } + +  char *b = (char *) (top - 1); +  *b = 'x'; + +  int ret = unlink(b); +  if(ret != -1){ +    printf("unlink(%s) returned %d, not -1\n", b, ret); +    exit(1); +  } + +  int fd = open(b, O_CREATE | O_WRONLY); +  if(fd != -1){ +    printf("open(%s) returned %d, not -1\n", b, fd); +    exit(1); +  } + +  ret = link(b, b); +  if(ret != -1){ +    printf("link(%s, %s) returned %d, not -1\n", b, b, ret); +    exit(1); +  } + +  char *args[] = { "xx", 0 }; +  ret = exec(b, args); +  if(ret != -1){ +    printf("exec(%s) returned %d, not -1\n", b, fd); +    exit(1); +  } +} + +// test O_TRUNC. +void +truncate1(char *s) +{ +  char buf[32]; +   +  unlink("truncfile"); +  int fd1 = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC); +  write(fd1, "abcd", 4); +  close(fd1); + +  int fd2 = open("truncfile", O_RDONLY); +  int n = read(fd2, buf, sizeof(buf)); +  if(n != 4){ +    printf("%s: read %d bytes, wanted 4\n", s, n); +    exit(1); +  } + +  fd1 = open("truncfile", O_WRONLY|O_TRUNC); + +  int fd3 = open("truncfile", O_RDONLY); +  n = read(fd3, buf, sizeof(buf)); +  if(n != 0){ +    printf("aaa fd3=%d\n", fd3); +    printf("%s: read %d bytes, wanted 0\n", s, n); +    exit(1); +  } + +  n = read(fd2, buf, sizeof(buf)); +  if(n != 0){ +    printf("bbb fd2=%d\n", fd2); +    printf("%s: read %d bytes, wanted 0\n", s, n); +    exit(1); +  } +   +  write(fd1, "abcdef", 6); + +  n = read(fd3, buf, sizeof(buf)); +  if(n != 6){ +    printf("%s: read %d bytes, wanted 6\n", s, n); +    exit(1); +  } + +  n = read(fd2, buf, sizeof(buf)); +  if(n != 2){ +    printf("%s: read %d bytes, wanted 2\n", s, n); +    exit(1); +  } + +  unlink("truncfile"); + +  close(fd1); +  close(fd2); +  close(fd3); +} + +// write to an open FD whose file has just been truncated. +// this causes a write at an offset beyond the end of the file. +// such writes fail on xv6 (unlike POSIX) but at least +// they don't crash. +void +truncate2(char *s) +{ +  unlink("truncfile"); + +  int fd1 = open("truncfile", O_CREATE|O_TRUNC|O_WRONLY); +  write(fd1, "abcd", 4); + +  int fd2 = open("truncfile", O_TRUNC|O_WRONLY); + +  int n = write(fd1, "x", 1); +  if(n != -1){ +    printf("%s: write returned %d, expected -1\n", s, n); +    exit(1); +  } + +  unlink("truncfile"); +  close(fd1); +  close(fd2); +} + +void +truncate3(char *s) +{ +  int pid, xstatus; + +  close(open("truncfile", O_CREATE|O_TRUNC|O_WRONLY)); +   +  pid = fork(); +  if(pid < 0){ +    printf("%s: fork failed\n", s); +    exit(1); +  } + +  if(pid == 0){ +    for(int i = 0; i < 100; i++){ +      char buf[32]; +      int fd = open("truncfile", O_WRONLY); +      if(fd < 0){ +        printf("%s: open failed\n", s); +        exit(1); +      } +      int n = write(fd, "1234567890", 10); +      if(n != 10){ +        printf("%s: write got %d, expected 10\n", s, n); +        exit(1); +      } +      close(fd); +      fd = open("truncfile", O_RDONLY); +      read(fd, buf, sizeof(buf)); +      close(fd); +    } +    exit(0); +  } + +  for(int i = 0; i < 150; i++){ +    int fd = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC); +    if(fd < 0){ +      printf("%s: open failed\n", s); +      exit(1); +    } +    int n = write(fd, "xxx", 3); +    if(n != 3){ +      printf("%s: write got %d, expected 3\n", s, n); +      exit(1); +    } +    close(fd); +  } + +  wait(&xstatus); +  unlink("truncfile"); +  exit(xstatus); +} +   +  // does chdir() call iput(p->cwd) in a transaction?  void  iputtest(char *s) @@ -1038,11 +1384,15 @@ concreate(char *s)        close(open(file, 0));        close(open(file, 0));        close(open(file, 0)); +      close(open(file, 0)); +      close(open(file, 0));      } else {        unlink(file);        unlink(file);        unlink(file);        unlink(file); +      unlink(file); +      unlink(file);      }      if(pid == 0)        exit(0); @@ -1106,7 +1456,7 @@ bigdir(char *s)      name[2] = '0' + (i % 64);      name[3] = '\0';      if(link("bd", name) != 0){ -      printf("%s: bigdir link failed\n", s); +      printf("%s: bigdir link(bd, %s) failed\n", s, name);        exit(1);      }    } @@ -1335,8 +1685,8 @@ bigfile(char *s)    enum { N = 20, SZ=600 };    int fd, i, total, cc; -  unlink("bigfile"); -  fd = open("bigfile", O_CREATE | O_RDWR); +  unlink("bigfile.dat"); +  fd = open("bigfile.dat", O_CREATE | O_RDWR);    if(fd < 0){      printf("%s: cannot create bigfile", s);      exit(1); @@ -1350,7 +1700,7 @@ bigfile(char *s)    }    close(fd); -  fd = open("bigfile", 0); +  fd = open("bigfile.dat", 0);    if(fd < 0){      printf("%s: cannot open bigfile\n", s);      exit(1); @@ -1379,7 +1729,7 @@ bigfile(char *s)      printf("%s: read bigfile wrong total\n", s);      exit(1);    } -  unlink("bigfile"); +  unlink("bigfile.dat");  }  void @@ -1418,6 +1768,14 @@ fourteen(char *s)      printf("%s: mkdir 12345678901234/123456789012345 succeeded!\n", s);      exit(1);    } + +  // clean up +  unlink("123456789012345/12345678901234"); +  unlink("12345678901234/12345678901234"); +  unlink("12345678901234/12345678901234/12345678901234"); +  unlink("123456789012345/123456789012345/123456789012345"); +  unlink("12345678901234/123456789012345"); +  unlink("12345678901234");  }  void @@ -1512,7 +1870,8 @@ dirfile(char *s)    close(fd);  } -// test that iput() is called at the end of _namei() +// test that iput() is called at the end of _namei(). +// also tests empty file names.  void  iref(char *s)  { @@ -1539,6 +1898,12 @@ iref(char *s)      unlink("xx");    } +  // clean up +  for(i = 0; i < NINODE + 1; i++){ +    chdir(".."); +    unlink("irefd"); +  } +    chdir("/");  } @@ -2087,13 +2452,35 @@ badarg(char *s)    exit(0);  } +// +// use sbrk() to count how many free physical memory pages there are. +// +int +countfree() +{ +  uint64 sz0 = (uint64)sbrk(0); +  int n = 0; + +  while(1){ +    uint64 a = (uint64) sbrk(4096); +    if(a == 0xffffffffffffffff){ +      break; +    } +    // modify the memory to make sure it's really allocated. +    *(char *)(a - 1) = 1; +    n += 1; +  } +  sbrk(-((uint64)sbrk(0) - sz0)); +  return n; +} +  // run each test in its own process. run returns 1 if child's exit()  // indicates success.  int  run(void f(char *), char *s) {    int pid;    int xstatus; -   +    printf("test %s: ", s);    if((pid = fork()) < 0) {      printf("runtest: fork error\n"); @@ -2105,9 +2492,9 @@ run(void f(char *), char *s) {    } else {      wait(&xstatus);      if(xstatus != 0)  -      printf("FAILED\n", s); +      printf("FAILED\n");      else -      printf("OK\n", s); +      printf("OK\n");      return xstatus == 0;    }  } @@ -2115,15 +2502,30 @@ run(void f(char *), char *s) {  int  main(int argc, char *argv[])  { -  char *n = 0; -  if(argc > 1) { -    n = argv[1]; +  int continuous = 0; +  char *justone = 0; + +  if(argc == 2 && strcmp(argv[1], "-c") == 0){ +    continuous = 1; +  } else if(argc == 2 && argv[1][0] != '-'){ +    justone = argv[1]; +  } else if(argc > 1){ +    printf("Usage: usertests [-c] [testname]\n"); +    exit(1);    }    struct test {      void (*f)(char *);      char *s;    } tests[] = { +    {copyin, "copyin"}, +    {copyout, "copyout"}, +    {copyinstr1, "copyinstr1"}, +    {copyinstr2, "copyinstr2"}, +    {copyinstr3, "copyinstr3"}, +    {truncate1, "truncate1"}, +    {truncate2, "truncate2"}, +    {truncate3, "truncate3"},      {reparent2, "reparent2"},      {pgbug, "pgbug" },      {sbrkbugs, "sbrkbugs" }, @@ -2173,25 +2575,48 @@ main(int argc, char *argv[])      {bigdir, "bigdir"}, // slow      { 0, 0},    }; -     -  printf("usertests starting\n"); -  if(open("usertests.ran", 0) >= 0){ -    printf("already ran user tests -- rebuild fs.img (rm fs.img; make fs.img)\n"); -    exit(1); +  if(continuous){ +    printf("continuous usertests starting\n"); +    while(1){ +      int fail = 0; +      int free0 = countfree(); +      for (struct test *t = tests; t->s != 0; t++) { +        if(!run(t->f, t->s)){ +          fail = 1; +          break; +        } +      } +      if(fail){ +        printf("SOME TESTS FAILED\n"); +        exit(1); +      } +      int free1 = countfree(); +      if(free1 < free0){ +        printf("FAILED -- lost some free pages\n"); +        exit(1); +      } +    }    } -  close(open("usertests.ran", O_CREATE)); +  printf("usertests starting\n"); +  int free0 = countfree();    int fail = 0;    for (struct test *t = tests; t->s != 0; t++) { -    if((n == 0) || strcmp(t->s, n) == 0) { +    if((justone == 0) || strcmp(t->s, justone) == 0) {        if(!run(t->f, t->s))          fail = 1;      }    } -  if(!fail) -    printf("ALL TESTS PASSED\n"); -  else + +  if(fail){      printf("SOME TESTS FAILED\n"); -  exit(1);   // not reached. +    exit(1); +  } else if(countfree() < free0){ +    printf("FAILED -- lost some free pages\n"); +    exit(1); +  } else { +    printf("ALL TESTS PASSED\n"); +    exit(0); +  }  } | 
