diff options
| author | Frans Kaashoek <kaashoek@mit.edu> | 2020-08-10 13:05:17 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-10 13:05:17 -0400 | 
| commit | c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9 (patch) | |
| tree | a6c903e1c61c08f4cb87700c320752a737081dcb /kernel | |
| parent | 90eb90b5e203299427c3fde8c996a48835fc93cf (diff) | |
| parent | d8fe1773b26758c7c7b8f36724cd822555b33612 (diff) | |
| download | xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.gz xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.bz2 xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.zip | |
Merge branch 'riscv' into riscv
Diffstat (limited to 'kernel')
| -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 | 
22 files changed, 227 insertions, 128 deletions
| 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);  } | 
