root/kernel/trap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trapinit
  2. trapinithart
  3. usertrap
  4. prepare_return
  5. kerneltrap
  6. clockintr
  7. devintr

   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"
   8 
   9 struct spinlock tickslock;
  10 uint ticks;
  11 
  12 extern char trampoline[], uservec[];
  13 
  14 // in kernelvec.S, calls kerneltrap().
  15 void kernelvec();
  16 
  17 extern int devintr();
  18 
  19 void
  20 trapinit(void)
  21 {
  22   initlock(&tickslock, "time");
  23 }
  24 
  25 // set up to take exceptions and traps while in the kernel.
  26 void
  27 trapinithart(void)
  28 {
  29   w_stvec((uint64)kernelvec);
  30 }
  31 
  32 //
  33 // handle an interrupt, exception, or system call from user space.
  34 // called from, and returns to, trampoline.S
  35 // return value is user satp for trampoline.S to switch to.
  36 //
  37 uint64
  38 usertrap(void)
  39 {
  40   int which_dev = 0;
  41 
  42   if ((r_sstatus() & SSTATUS_SPP) != 0)
  43     panic("usertrap: not from user mode");
  44 
  45   // send interrupts and exceptions to kerneltrap(),
  46   // since we're now in the kernel.
  47   w_stvec((uint64)kernelvec); //DOC: kernelvec
  48 
  49   struct proc *p = myproc();
  50 
  51   // save user program counter.
  52   p->trapframe->epc = r_sepc();
  53 
  54   if (r_scause() == 8) {
  55     // system call
  56 
  57     if (killed(p))
  58       kexit(-1);
  59 
  60     // sepc points to the ecall instruction,
  61     // but we want to return to the next instruction.
  62     p->trapframe->epc += 4;
  63 
  64     // an interrupt will change sepc, scause, and sstatus,
  65     // so enable only now that we're done with those registers.
  66     intr_on();
  67 
  68     syscall();
  69   } else if ((which_dev = devintr()) != 0) {
  70     // ok
  71   } else if ((r_scause() == 15 || r_scause() == 13) &&
  72              vmfault(p->pagetable, p->sz, r_stval(),
  73                      (r_scause() == 13) ? 1 : 0) != 0) {
  74     // page fault on lazily-allocated page
  75   } else {
  76     printk("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
  77     printk("            sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
  78     setkilled(p);
  79   }
  80 
  81   if (killed(p))
  82     kexit(-1);
  83 
  84   // give up the CPU if this is a timer interrupt.
  85   if (which_dev == 2)
  86     yield();
  87 
  88   prepare_return();
  89 
  90   // the user page table to switch to, for trampoline.S
  91   uint64 satp = MAKE_SATP(p->pagetable);
  92 
  93   // return to trampoline.S; satp value in a0.
  94   return satp;
  95 }
  96 
  97 //
  98 // set up trapframe and control registers for a return to user space
  99 //
 100 void
 101 prepare_return(void)
 102 {
 103   struct proc *p = myproc();
 104 
 105   // we're about to switch the destination of traps from
 106   // kerneltrap() to usertrap(). because a trap from kernel
 107   // code to usertrap would be a disaster, turn off interrupts.
 108   intr_off();
 109 
 110   // send syscalls, interrupts, and exceptions to uservec in trampoline.S
 111   uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
 112   w_stvec(trampoline_uservec);
 113 
 114   // set up trapframe values that uservec will need when
 115   // the process next traps into the kernel.
 116   p->trapframe->kernel_satp = r_satp();         // kernel page table
 117   p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
 118   p->trapframe->kernel_trap = (uint64)usertrap;
 119   p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
 120 
 121   // set up the registers that trampoline.S's sret will use
 122   // to get to user space.
 123 
 124   // set S Previous Privilege mode to User.
 125   unsigned long x = r_sstatus();
 126   x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
 127   x |= SSTATUS_SPIE; // enable interrupts in user mode
 128   w_sstatus(x);
 129 
 130   // set S Exception Program Counter to the saved user pc.
 131   w_sepc(p->trapframe->epc);
 132 }
 133 
 134 // interrupts and exceptions from kernel code go here via kernelvec,
 135 // on whatever the current kernel stack is.
 136 void
 137 kerneltrap()
 138 {
 139   int which_dev = 0;
 140   uint64 sepc = r_sepc();
 141   uint64 sstatus = r_sstatus();
 142   uint64 scause = r_scause();
 143 
 144   if ((sstatus & SSTATUS_SPP) == 0)
 145     panic("kerneltrap: not from supervisor mode");
 146   if (intr_get() != 0)
 147     panic("kerneltrap: interrupts enabled");
 148 
 149   if ((which_dev = devintr()) == 0) {
 150     // interrupt or trap from an unknown source
 151     printk("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(),
 152            r_stval());
 153     panic("kerneltrap");
 154   }
 155 
 156   // give up the CPU if this is a timer interrupt.
 157   if (which_dev == 2 && myproc() != 0)
 158     yield();
 159 
 160   // the yield() may have caused some traps to occur,
 161   // so restore trap registers for use by kernelvec.S's sepc instruction.
 162   w_sepc(sepc);
 163   w_sstatus(sstatus);
 164 }
 165 
 166 void
 167 clockintr()
 168 {
 169   if (cpuid() == 0) {
 170     acquire(&tickslock);
 171     ticks++;
 172     wakeup(&ticks);
 173     release(&tickslock);
 174   }
 175 
 176   // ask for the next timer interrupt. this also clears
 177   // the interrupt request. 1000000 is about a tenth
 178   // of a second.
 179   w_stimecmp(r_time() + 1000000);
 180 }
 181 
 182 // check if it's an external interrupt or software interrupt,
 183 // and handle it.
 184 // returns 2 if timer interrupt,
 185 // 1 if other device,
 186 // 0 if not recognized.
 187 int
 188 devintr()
 189 {
 190   uint64 scause = r_scause();
 191 
 192   if (scause == 0x8000000000000009L) {
 193     // this is a supervisor external interrupt, via PLIC.
 194 
 195     // irq indicates which device interrupted.
 196     int irq = plic_claim();
 197 
 198     if (irq == UART0_IRQ) {
 199       uartintr();
 200     } else if (irq == VIRTIO0_IRQ) {
 201       virtio_disk_intr();
 202     } else if (irq) {
 203       printk("unexpected interrupt irq=%d\n", irq);
 204     }
 205 
 206     // the PLIC allows each device to raise at most one
 207     // interrupt at a time; tell the PLIC the device is
 208     // now allowed to interrupt again.
 209     if (irq)
 210       plic_complete(irq);
 211 
 212     return 1;
 213   } else if (scause == 0x8000000000000005L) {
 214     // timer interrupt.
 215     clockintr();
 216     return 2;
 217   } else {
 218     return 0;
 219   }
 220 }

/* [<][>][^][v][top][bottom][index][help] */