kernel/trap.c

219 lines
1#include "types.h"2#include "param.h"3#include "memlayout.h"4#include "riscv.h"5#include "spinlock.h"6#include "proc.h"7#include "defs.h"89struct spinlock tickslock;10uint ticks;1112extern char trampoline[], uservec[];1314// in kernelvec.S, calls kerneltrap().15void kernelvec();1617extern int devintr();1819void20trapinit(void)21{22  initlock(&tickslock, "time");23}2425// set up to take exceptions and traps while in the kernel.26void27trapinithart(void)28{29  w_stvec((uint64)kernelvec);30}3132//33// handle an interrupt, exception, or system call from user space.34// called from, and returns to, trampoline.S35// return value is user satp for trampoline.S to switch to.36//37uint6438usertrap(void)39{40  int which_dev = 0;4142  if((r_sstatus() & SSTATUS_SPP) != 0)43    panic("usertrap: not from user mode");4445  // send interrupts and exceptions to kerneltrap(),46  // since we're now in the kernel.47  w_stvec((uint64)kernelvec);  //DOC: kernelvec4849  struct proc *p = myproc();50  51  // save user program counter.52  p->trapframe->epc = r_sepc();53  54  if(r_scause() == 8){55    // system call5657    if(killed(p))58      kexit(-1);5960    // sepc points to the ecall instruction,61    // but we want to return to the next instruction.62    p->trapframe->epc += 4;6364    // an interrupt will change sepc, scause, and sstatus,65    // so enable only now that we're done with those registers.66    intr_on();6768    syscall();69  } else if((which_dev = devintr()) != 0){70    // ok71  } else if((r_scause() == 15 || r_scause() == 13) &&72            vmfault(p->pagetable, r_stval(), (r_scause() == 13)? 1 : 0) != 0) {73    // page fault on lazily-allocated page74  } else {75    printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);76    printf("            sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());77    setkilled(p);78  }7980  if(killed(p))81    kexit(-1);8283  // give up the CPU if this is a timer interrupt.84  if(which_dev == 2)85    yield();8687  prepare_return();8889  // the user page table to switch to, for trampoline.S90  uint64 satp = MAKE_SATP(p->pagetable);9192  // return to trampoline.S; satp value in a0.93  return satp;94}9596//97// set up trapframe and control registers for a return to user space98//99void100prepare_return(void)101{102  struct proc *p = myproc();103104  // we're about to switch the destination of traps from105  // kerneltrap() to usertrap(). because a trap from kernel106  // code to usertrap would be a disaster, turn off interrupts.107  intr_off();108109  // send syscalls, interrupts, and exceptions to uservec in trampoline.S110  uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);111  w_stvec(trampoline_uservec);112113  // set up trapframe values that uservec will need when114  // the process next traps into the kernel.115  p->trapframe->kernel_satp = r_satp();         // kernel page table116  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack117  p->trapframe->kernel_trap = (uint64)usertrap;118  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()119120  // set up the registers that trampoline.S's sret will use121  // to get to user space.122  123  // set S Previous Privilege mode to User.124  unsigned long x = r_sstatus();125  x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode126  x |= SSTATUS_SPIE; // enable interrupts in user mode127  w_sstatus(x);128129  // set S Exception Program Counter to the saved user pc.130  w_sepc(p->trapframe->epc);131}132133// interrupts and exceptions from kernel code go here via kernelvec,134// on whatever the current kernel stack is.135void 136kerneltrap()137{138  int which_dev = 0;139  uint64 sepc = r_sepc();140  uint64 sstatus = r_sstatus();141  uint64 scause = r_scause();142  143  if((sstatus & SSTATUS_SPP) == 0)144    panic("kerneltrap: not from supervisor mode");145  if(intr_get() != 0)146    panic("kerneltrap: interrupts enabled");147148  if((which_dev = devintr()) == 0){149    // interrupt or trap from an unknown source150    printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval());151    panic("kerneltrap");152  }153154  // give up the CPU if this is a timer interrupt.155  if(which_dev == 2 && myproc() != 0)156    yield();157158  // the yield() may have caused some traps to occur,159  // so restore trap registers for use by kernelvec.S's sepc instruction.160  w_sepc(sepc);161  w_sstatus(sstatus);162}163164void165clockintr()166{167  if(cpuid() == 0){168    acquire(&tickslock);169    ticks++;170    wakeup(&ticks);171    release(&tickslock);172  }173174  // ask for the next timer interrupt. this also clears175  // the interrupt request. 1000000 is about a tenth176  // of a second.177  w_stimecmp(r_time() + 1000000);178}179180// check if it's an external interrupt or software interrupt,181// and handle it.182// returns 2 if timer interrupt,183// 1 if other device,184// 0 if not recognized.185int186devintr()187{188  uint64 scause = r_scause();189190  if(scause == 0x8000000000000009L){191    // this is a supervisor external interrupt, via PLIC.192193    // irq indicates which device interrupted.194    int irq = plic_claim();195196    if(irq == UART0_IRQ){197      uartintr();198    } else if(irq == VIRTIO0_IRQ){199      virtio_disk_intr();200    } else if(irq){201      printf("unexpected interrupt irq=%d\n", irq);202    }203204    // the PLIC allows each device to raise at most one205    // interrupt at a time; tell the PLIC the device is206    // now allowed to interrupt again.207    if(irq)208      plic_complete(irq);209210    return 1;211  } else if(scause == 0x8000000000000005L){212    // timer interrupt.213    clockintr();214    return 2;215  } else {216    return 0;217  }218}219220