Kernel uses user_contexts, instead of TFs
[akaros.git] / kern / arch / riscv / trap.c
1 #include <arch/arch.h>
2 #include <assert.h>
3 #include <trap.h>
4 #include <arch/console.h>
5 #include <console.h>
6 #include <string.h>
7 #include <process.h>
8 #include <syscall.h>
9 #include <monitor.h>
10 #include <manager.h>
11 #include <stdio.h>
12 #include <smp.h>
13 #include <slab.h>
14 #include <mm.h>
15 #include <umem.h>
16 #include <pmap.h>
17
18 /* These are the stacks the kernel will load when it receives a trap from user
19  * space.  The deal is that they get set right away in entry.S, and can always
20  * be used for finding the top of the stack (from which you should subtract the
21  * sizeof the trapframe.  Note, we need to have a junk value in the array so
22  * that this is NOT part of the BSS.  If it is in the BSS, it will get 0'd in
23  * kernel_init(), which is after these values get set.
24  *
25  * TODO: if these end up becoming contended cache lines, move this to
26  * per_cpu_info. */
27 uintptr_t core_stacktops[MAX_NUM_CPUS] = {0xcafebabe, 0};
28
29 void
30 advance_pc(struct hw_trapframe *state)
31 {
32         state->epc += 4;
33 }
34
35 /* Set stacktop for the current core to be the stack the kernel will start on
36  * when trapping/interrupting from userspace */
37 void set_stack_top(uintptr_t stacktop)
38 {
39         core_stacktops[core_id()] = stacktop;
40 }
41
42 /* Note the assertion assumes we are in the top page of the stack. */
43 uintptr_t get_stack_top(void)
44 {
45         register uintptr_t sp asm ("sp");
46         uintptr_t stacktop = core_stacktops[core_id()];
47         assert(ROUNDUP(sp, PGSIZE) == stacktop);
48         return stacktop;
49 }
50
51 void
52 idt_init(void)
53 {
54 }
55
56 void
57 sysenter_init(void)
58 {
59 }
60
61 /* Helper.  For now, this copies out the TF to pcpui, and sets cur_ctx to point
62  * to it. */
63 static void set_current_ctx_hw(struct per_cpu_info *pcpui,
64                                struct hw_trapframe *hw_tf)
65 {
66         if (irq_is_enabled())
67                 warn("Turn off IRQs until cur_ctx is set!");
68         assert(!pcpui->cur_ctx);
69         pcpui->actual_ctx.type = ROS_HW_CTX;
70         pcpui->actual_ctx.hw_tf = *hw_tf;
71         pcpui->cur_ctx = &pcpui->actual_ctx;
72 }
73
74 static void set_current_ctx_sw(struct per_cpu_info *pcpui,
75                                struct sw_trapframe *sw_tf)
76 {
77         if (irq_is_enabled())
78                 warn("Turn off IRQs until cur_ctx is set!");
79         assert(!pcpui->cur_ctx);
80         pcpui->actual_ctx.type = ROS_SW_CTX;
81         pcpui->actual_ctx.sw_tf = *sw_tf;
82         pcpui->cur_ctx = &pcpui->actual_ctx;
83 }
84
85 static int
86 format_trapframe(struct hw_trapframe *hw_tf, char* buf, int bufsz)
87 {
88         // slightly hackish way to read out the instruction that faulted.
89         // not guaranteed to be right 100% of the time
90         uint32_t insn;
91         if(!(current && !memcpy_from_user(current,&insn,(void*)hw_tf->epc,4)))
92                 insn = -1;
93
94         int len = snprintf(buf,bufsz,"TRAP frame at %p on core %d\n",
95                            hw_tf, core_id());
96         static const char* regnames[] = {
97           "z ", "ra", "v0", "v1", "a0", "a1", "a2", "a3",
98           "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
99           "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3",
100           "s4", "s5", "s6", "s7", "s8", "fp", "sp", "tp"
101         };
102         
103         hw_tf->gpr[0] = 0;
104         
105         for(int i = 0; i < 32; i+=4)
106         {
107                 for(int j = 0; j < 4; j++)
108                         len += snprintf(buf+len, bufsz-len,
109                                         "%s %016lx%c", regnames[i+j], hw_tf->gpr[i+j], 
110                                         j < 3 ? ' ' : '\n');
111         }
112         len += snprintf(buf+len, bufsz-len,
113                         "sr %016lx pc %016lx va %016lx insn       %08x\n",
114                                         hw_tf->sr, hw_tf->epc, hw_tf->badvaddr, insn);
115
116         buf[bufsz-1] = 0;
117         return len;
118 }
119
120 void
121 print_trapframe(struct hw_trapframe *hw_tf)
122 {
123         char buf[1024];
124         int len = format_trapframe(hw_tf, buf, sizeof(buf));
125         cputbuf(buf,len);
126 }
127
128 static void exit_halt_loop(struct hw_trapframe *hw_tf)
129 {
130         extern char after_cpu_halt;
131         if ((char*)hw_tf->epc >= (char*)&cpu_halt &&
132             (char*)hw_tf->epc < &after_cpu_halt)
133                 hw_tf->epc = hw_tf->gpr[1];
134 }
135
136 static void handle_keypress(char c)
137 {
138         amr_t handler = c == 'G' ? __run_mon : __cons_add_char;
139         send_kernel_message(core_id(), handler, (long)&cons_buf, (long)c, 0,
140                             KMSG_ROUTINE);
141         cons_init();
142 }
143
144 static void handle_host_interrupt(struct hw_trapframe *hw_tf)
145 {
146         uintptr_t fh = mtpcr(PCR_FROMHOST, 0);
147         switch (fh >> 56)
148         {
149           case 0x00: return;
150           case 0x01: handle_keypress(fh); return;
151           default: assert(0);
152         }
153 }
154
155 static void handle_timer_interrupt(struct hw_trapframe *hw_tf)
156 {
157         timer_interrupt(hw_tf, NULL);
158 }
159
160 /* Assumes that any IPI you get is really a kernel message */
161 static void handle_interprocessor_interrupt(struct hw_trapframe *hw_tf)
162 {
163         clear_ipi();
164         handle_kmsg_ipi(hw_tf, 0);
165 }
166
167 static void
168 unhandled_trap(struct hw_trapframe *state, const char* name)
169 {
170         static spinlock_t screwup_lock = SPINLOCK_INITIALIZER;
171         spin_lock(&screwup_lock);
172
173         if(in_kernel(state))
174         {
175                 print_trapframe(state);
176                 panic("Unhandled trap in kernel!\nTrap type: %s", name);
177         }
178         else
179         {
180                 char tf_buf[1024];
181                 format_trapframe(state, tf_buf, sizeof(tf_buf));
182
183                 warn("Unhandled trap in user!\nTrap type: %s\n%s", name, tf_buf);
184                 backtrace();
185                 spin_unlock(&screwup_lock);
186
187                 assert(current);
188                 enable_irq();
189                 proc_destroy(current);
190         }
191 }
192
193 static void
194 handle_misaligned_fetch(struct hw_trapframe *state)
195 {
196         unhandled_trap(state, "Misaligned Fetch");
197 }
198
199 static void
200 handle_misaligned_load(struct hw_trapframe *state)
201 {
202         unhandled_trap(state, "Misaligned Load");
203 }
204
205 static void
206 handle_misaligned_store(struct hw_trapframe *state)
207 {
208         unhandled_trap(state, "Misaligned Store");
209 }
210
211 static void
212 handle_fault_fetch(struct hw_trapframe *state)
213 {
214         if(in_kernel(state))
215         {
216                 print_trapframe(state);
217                 panic("Instruction Page Fault in the Kernel at %p!", state->epc);
218         }
219
220         set_current_ctx_hw(&per_cpu_info[core_id()], state);
221
222         if(handle_page_fault(current, state->epc, PROT_EXEC))
223                 unhandled_trap(state, "Instruction Page Fault");
224 }
225
226 static void
227 handle_fault_load(struct hw_trapframe *state)
228 {
229         if(in_kernel(state))
230         {
231                 print_trapframe(state);
232                 panic("Load Page Fault in the Kernel at %p!", state->badvaddr);
233         }
234
235         set_current_ctx_hw(&per_cpu_info[core_id()], state);
236
237         if(handle_page_fault(current, state->badvaddr, PROT_READ))
238                 unhandled_trap(state, "Load Page Fault");
239 }
240
241 static void
242 handle_fault_store(struct hw_trapframe *state)
243 {
244         if(in_kernel(state))
245         {
246                 print_trapframe(state);
247                 panic("Store Page Fault in the Kernel at %p!", state->badvaddr);
248         }
249
250         set_current_ctx_hw(&per_cpu_info[core_id()], state);
251
252         if(handle_page_fault(current, state->badvaddr, PROT_WRITE))
253                 unhandled_trap(state, "Store Page Fault");
254 }
255
256 static void
257 handle_illegal_instruction(struct hw_trapframe *state)
258 {
259         assert(!in_kernel(state));
260
261         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
262         set_current_ctx_hw(pcpui, state);
263         if (emulate_fpu(state) == 0)
264         {
265                 advance_pc(&pcpui->cur_ctx->hw_tf);
266                 return;
267         }
268
269         unhandled_trap(state, "Illegal Instruction");
270 }
271
272 static void
273 handle_fp_disabled(struct hw_trapframe *hw_tf)
274 {
275         if (in_kernel(hw_tf))
276                 panic("kernel executed an FP instruction!");
277
278         hw_tf->sr |= SR_EF;
279         env_pop_tf(hw_tf); /* We didn't save our TF, so don't proc_restartcore */
280 }
281
282 static void
283 handle_syscall(struct hw_trapframe *state)
284 {
285         uintptr_t a0 = state->gpr[4];
286         uintptr_t a1 = state->gpr[5];
287
288         advance_pc(state);
289         set_current_ctx_hw(&per_cpu_info[core_id()], state);
290         enable_irq();
291         prep_syscalls(current, (struct syscall*)a0, a1);
292 }
293
294 static void
295 handle_breakpoint(struct hw_trapframe *state)
296 {
297         advance_pc(state);
298         monitor(state);
299 }
300
301 void
302 handle_trap(struct hw_trapframe *hw_tf)
303 {
304         static void (*const trap_handlers[])(struct hw_trapframe *) = {
305           [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
306           [CAUSE_FAULT_FETCH] = handle_fault_fetch,
307           [CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
308           [CAUSE_PRIVILEGED_INSTRUCTION] = handle_illegal_instruction,
309           [CAUSE_FP_DISABLED] = handle_fp_disabled,
310           [CAUSE_SYSCALL] = handle_syscall,
311           [CAUSE_BREAKPOINT] = handle_breakpoint,
312           [CAUSE_MISALIGNED_LOAD] = handle_misaligned_load,
313           [CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
314           [CAUSE_FAULT_LOAD] = handle_fault_load,
315           [CAUSE_FAULT_STORE] = handle_fault_store,
316         };
317
318         static void (*const irq_handlers[])(struct hw_trapframe *) = {
319           [IRQ_TIMER] = handle_timer_interrupt,
320           [IRQ_HOST] = handle_host_interrupt,
321           [IRQ_IPI] = handle_interprocessor_interrupt,
322         };
323         
324         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
325         if (hw_tf->cause < 0)
326         {
327                 uint8_t irq = hw_tf->cause;
328                 assert(irq < sizeof(irq_handlers)/sizeof(irq_handlers[0]) &&
329                        irq_handlers[irq]);
330
331                 if (in_kernel(hw_tf))
332                         exit_halt_loop(hw_tf);
333                 else
334                         set_current_ctx_hw(&per_cpu_info[core_id()], hw_tf);
335
336                 inc_irq_depth(pcpui);
337                 irq_handlers[irq](hw_tf);
338                 dec_irq_depth(pcpui);
339         }
340         else
341         {
342                 assert(hw_tf->cause < sizeof(trap_handlers)/sizeof(trap_handlers[0]) &&
343                        trap_handlers[hw_tf->cause]);
344                 if (in_kernel(hw_tf)) {
345                         inc_ktrap_depth(pcpui);
346                         trap_handlers[hw_tf->cause](hw_tf);
347                         dec_ktrap_depth(pcpui);
348                 } else {
349                         trap_handlers[hw_tf->cause](hw_tf);
350                 }
351         }
352         
353         /* Return to the current process, which should be runnable.  If we're the
354          * kernel, we should just return naturally.  Note that current and tf need
355          * to still be okay (might not be after blocking) */
356         if (in_kernel(hw_tf))
357                 env_pop_tf(hw_tf);      /* TODO: for a kernel tf?  change names? */
358         else
359                 proc_restartcore();
360 }
361
362 /* We don't have NMIs now. */
363 void send_nmi(uint32_t os_coreid)
364 {
365 }