root/kernel/trap.c

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

DEFINITIONS

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

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