xv6, line by line
kernel/trap.c

kernel/trap.c

C · 220 lines · annotated 100% · kernel · upstream

About this file

The C half of trap handling. Every trap ends up in one of two functions here:

Both use devintr to recognize and dispatch interrupts, and clockintr to count time and schedule the next timer interrupt. On a timer interrupt, both give up the CPU with yield: this is how xv6 shares a CPU among processes even if a program never makes a system call.

The complete round trip of a system call is: user stub (li a7, n; ecall, from user/usys.pl) → uservec → usertrap → syscall → sys_* function → back in usertrap → prepare_return → userret → sret → the instruction after ecall, with the result in a0.

Read before: kernel/trampoline.S. Read next: kernel/syscall.c, kernel/plic.c.

1#include "types.h"
2#include "param.h"
4#include "riscv.h"
5#include "spinlock.h"
6#include "proc.h"
7#include "defs.h"
12extern char trampoline[], uservec[];
14// in kernelvec.S, calls kerneltrap().
15void kernelvec();
17extern int devintr();
19void
22 initlock(&tickslock, "time");
25// set up to take exceptions and traps while in the kernel.
26void
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//
40 int which_dev = 0;
42 if ((r_sstatus() & SSTATUS_SPP) != 0)
43 panic("usertrap: not from user mode");
45 // send interrupts and exceptions to kerneltrap(),
46 // since we're now in the kernel.
47 w_stvec((uint64)kernelvec); //DOC: kernelvec
49 struct proc *p = myproc();
51 // save user program counter.
54 if (r_scause() == 8) {
55 // system call
57 if (killed(p))
58 kexit(-1);
60 // sepc points to the ecall instruction,
61 // but we want to return to the next instruction.
62 p->trapframe->epc += 4;
64 // an interrupt will change sepc, scause, and sstatus,
65 // so enable only now that we're done with those registers.
69 } else if ((which_dev = devintr()) != 0) {
70 // ok
71 } else if ((r_scause() == 15 || r_scause() == 13) &&
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());
79 }
81 if (killed(p))
82 kexit(-1);
84 // give up the CPU if this is a timer interrupt.
85 if (which_dev == 2)
90 // the user page table to switch to, for trampoline.S
93 // return to trampoline.S; satp value in a0.
94 return satp;
97//
98// set up trapframe and control registers for a return to user space
99//
100void
103 struct proc *p = myproc();
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.
110 // send syscalls, interrupts, and exceptions to uservec in trampoline.S
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
119 p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
121 // set up the registers that trampoline.S's sret will use
122 // to get to user space.
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
130 // set S Exception Program Counter to the saved user pc.
134// interrupts and exceptions from kernel code go here via kernelvec,
135// on whatever the current kernel stack is.
136void
139 int which_dev = 0;
144 if ((sstatus & SSTATUS_SPP) == 0)
145 panic("kerneltrap: not from supervisor mode");
146 if (intr_get() != 0)
147 panic("kerneltrap: interrupts enabled");
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(),
153 panic("kerneltrap");
154 }
156 // give up the CPU if this is a timer interrupt.
157 if (which_dev == 2 && myproc() != 0)
160 // the yield() may have caused some traps to occur,
161 // so restore trap registers for use by kernelvec.S's sepc instruction.
166void
169 if (cpuid() == 0) {
174 }
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);
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.
187int
192 if (scause == 0x8000000000000009L) {
193 // this is a supervisor external interrupt, via PLIC.
195 // irq indicates which device interrupted.
196 int irq = plic_claim();
198 if (irq == UART0_IRQ) {
200 } else if (irq == VIRTIO0_IRQ) {
202 } else if (irq) {
203 printk("unexpected interrupt irq=%d\n", irq);
204 }
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)
212 return 1;
213 } else if (scause == 0x8000000000000005L) {
214 // timer interrupt.
216 return 2;
217 } else {
218 return 0;
219 }