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);
  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, r_stval(), (r_scause() == 13)? 1 : 0) != 0) {
  73     // page fault on lazily-allocated page
  74   } 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   }
  79 
  80   if(killed(p))
  81     kexit(-1);
  82 
  83   // give up the CPU if this is a timer interrupt.
  84   if(which_dev == 2)
  85     yield();
  86 
  87   prepare_return();
  88 
  89   // the user page table to switch to, for trampoline.S
  90   uint64 satp = MAKE_SATP(p->pagetable);
  91 
  92   // return to trampoline.S; satp value in a0.
  93   return satp;
  94 }
  95 
  96 //
  97 // set up trapframe and control registers for a return to user space
  98 //
  99 void
 100 prepare_return(void)
 101 {
 102   struct proc *p = myproc();
 103 
 104   // we're about to switch the destination of traps from
 105   // kerneltrap() to usertrap(). because a trap from kernel
 106   // code to usertrap would be a disaster, turn off interrupts.
 107   intr_off();
 108 
 109   // send syscalls, interrupts, and exceptions to uservec in trampoline.S
 110   uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
 111   w_stvec(trampoline_uservec);
 112 
 113   // set up trapframe values that uservec will need when
 114   // the process next traps into the kernel.
 115   p->trapframe->kernel_satp = r_satp();         // kernel page table
 116   p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
 117   p->trapframe->kernel_trap = (uint64)usertrap;
 118   p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()
 119 
 120   // set up the registers that trampoline.S's sret will use
 121   // 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 mode
 126   x |= SSTATUS_SPIE; // enable interrupts in user mode
 127   w_sstatus(x);
 128 
 129   // set S Exception Program Counter to the saved user pc.
 130   w_sepc(p->trapframe->epc);
 131 }
 132 
 133 // interrupts and exceptions from kernel code go here via kernelvec,
 134 // on whatever the current kernel stack is.
 135 void 
 136 kerneltrap()
 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");
 147 
 148   if((which_dev = devintr()) == 0){
 149     // interrupt or trap from an unknown source
 150     printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval());
 151     panic("kerneltrap");
 152   }
 153 
 154   // give up the CPU if this is a timer interrupt.
 155   if(which_dev == 2 && myproc() != 0)
 156     yield();
 157 
 158   // 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 }
 163 
 164 void
 165 clockintr()
 166 {
 167   if(cpuid() == 0){
 168     acquire(&tickslock);
 169     ticks++;
 170     wakeup(&ticks);
 171     release(&tickslock);
 172   }
 173 
 174   // ask for the next timer interrupt. this also clears
 175   // the interrupt request. 1000000 is about a tenth
 176   // of a second.
 177   w_stimecmp(r_time() + 1000000);
 178 }
 179 
 180 // 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.
 185 int
 186 devintr()
 187 {
 188   uint64 scause = r_scause();
 189 
 190   if(scause == 0x8000000000000009L){
 191     // this is a supervisor external interrupt, via PLIC.
 192 
 193     // irq indicates which device interrupted.
 194     int irq = plic_claim();
 195 
 196     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     }
 203 
 204     // the PLIC allows each device to raise at most one
 205     // interrupt at a time; tell the PLIC the device is
 206     // now allowed to interrupt again.
 207     if(irq)
 208       plic_complete(irq);
 209 
 210     return 1;
 211   } else if(scause == 0x8000000000000005L){
 212     // timer interrupt.
 213     clockintr();
 214     return 2;
 215   } else {
 216     return 0;
 217   }
 218 }
 219 

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