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