RISC-V architecture bugfix potpourri
[akaros.git] / kern / arch / riscv / trap.c
1 #include <arch/arch.h>
2 #include <assert.h>
3 #include <arch/trap.h>
4 #include <string.h>
5 #include <process.h>
6 #include <syscall.h>
7 #include <monitor.h>
8 #include <manager.h>
9 #include <stdio.h>
10 #include <smp.h>
11 #include <slab.h>
12 #include <mm.h>
13 #include <umem.h>
14 #include <pmap.h>
15
16 /* These are the stacks the kernel will load when it receives a trap from user
17  * space.  The deal is that they get set right away in entry.S, and can always
18  * be used for finding the top of the stack (from which you should subtract the
19  * sizeof the trapframe.  Note, we need to have a junk value in the array so
20  * that this is NOT part of the BSS.  If it is in the BSS, it will get 0'd in
21  * kernel_init(), which is after these values get set.
22  *
23  * TODO: if these end up becoming contended cache lines, move this to
24  * per_cpu_info. */
25 uintptr_t core_stacktops[MAX_NUM_CPUS] = {0xcafebabe, 0};
26
27 struct kmem_cache *kernel_msg_cache;
28 void kernel_msg_init(void)
29 {
30         kernel_msg_cache = kmem_cache_create("kernel_msgs",
31                            sizeof(struct kernel_message), HW_CACHE_ALIGN, 0, 0, 0);
32 }
33
34 spinlock_t kernel_message_buf_busy[MAX_NUM_CPUS] = {SPINLOCK_INITIALIZER};
35 kernel_message_t kernel_message_buf[MAX_NUM_CPUS];
36
37 /* This is mostly identical to x86's, minus the different send_ipi call. */
38 uint32_t send_kernel_message(uint32_t dst, amr_t pc, long arg0, long arg1,
39                              long arg2, int type)
40 {
41         kernel_message_t *k_msg;
42         assert(pc);
43         // note this will be freed on the destination core
44         k_msg = (kernel_message_t *CT(1))TC(kmem_cache_alloc(kernel_msg_cache, 0));
45         k_msg->srcid = core_id();
46         k_msg->pc = pc;
47         k_msg->arg0 = arg0;
48         k_msg->arg1 = arg1;
49         k_msg->arg2 = arg2;
50         switch (type) {
51                 case KMSG_IMMEDIATE:
52                         spin_lock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
53                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].immed_amsgs, k_msg, link);
54                         spin_unlock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
55                         break;
56                 case KMSG_ROUTINE:
57                         spin_lock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
58                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].routine_amsgs, k_msg, link);
59                         spin_unlock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
60                         break;
61                 default:
62                         panic("Unknown type of kernel message!");
63         }
64         /* if we're sending a routine message locally, we don't want/need an IPI */
65         if ((dst != k_msg->srcid) || (type == KMSG_IMMEDIATE))
66                 send_ipi(dst);
67         return 0;
68 }
69
70 void
71 advance_pc(trapframe_t* state)
72 {
73         state->epc += 4;
74 }
75
76 /* Set stacktop for the current core to be the stack the kernel will start on
77  * when trapping/interrupting from userspace */
78 void set_stack_top(uintptr_t stacktop)
79 {
80         core_stacktops[core_id()] = stacktop;
81 }
82
83 /* Note the assertion assumes we are in the top page of the stack. */
84 uintptr_t get_stack_top(void)
85 {
86         register uintptr_t sp asm ("sp");
87         uintptr_t stacktop = core_stacktops[core_id()];
88         assert(ROUNDUP(sp, PGSIZE) == stacktop);
89         return stacktop;
90 }
91
92 void
93 idt_init(void)
94 {
95 }
96
97 void
98 sysenter_init(void)
99 {
100 }
101
102 /* Helper.  For now, this copies out the TF to pcpui, and sets cur_tf to point
103  * to it. */
104 static void
105 set_current_tf(struct per_cpu_info *pcpui, struct trapframe *tf)
106 {
107         if (irq_is_enabled())
108                 warn("Turn off IRQs until cur_tf is set!");
109         assert(!pcpui->cur_tf);
110         pcpui->actual_tf = *tf;
111         pcpui->cur_tf = &pcpui->actual_tf;
112 }
113
114 static int
115 format_trapframe(trapframe_t *tf, char* buf, int bufsz)
116 {
117         // slightly hackish way to read out the instruction that faulted.
118         // not guaranteed to be right 100% of the time
119         uint32_t insn;
120         if(!(current && !memcpy_from_user(current,&insn,(void*)tf->epc,4)))
121                 insn = -1;
122
123         int len = snprintf(buf,bufsz,"TRAP frame at %p on core %d\n",
124                            tf, core_id());
125         static const char* regnames[] = {
126           "z ", "ra", "v0", "v1", "a0", "a1", "a2", "a3",
127           "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
128           "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3",
129           "s4", "s5", "s6", "s7", "s8", "fp", "sp", "tp"
130         };
131         
132         tf->gpr[0] = 0;
133         
134         for(int i = 0; i < 32; i+=4)
135         {
136                 for(int j = 0; j < 4; j++)
137                         len += snprintf(buf+len, bufsz-len,
138                                         "%s %016lx%c", regnames[i+j], tf->gpr[i+j], 
139                                         j < 3 ? ' ' : '\n');
140         }
141         len += snprintf(buf+len, bufsz-len,
142                         "sr %016lx pc %016lx va %016lx insn       %008x\n", tf->sr, tf->epc,
143                         tf->badvaddr, (uint32_t)tf->insn);
144
145         buf[bufsz-1] = 0;
146         return len;
147 }
148
149 void
150 print_trapframe(trapframe_t* tf)
151 {
152         char buf[1024];
153         int len = format_trapframe(tf,buf,sizeof(buf));
154         cputbuf(buf,len);
155 }
156
157 /* Helper function.  Returns 0 if the list was empty. */
158 static kernel_message_t *get_next_amsg(struct kernel_msg_list *list_head,
159                                        spinlock_t *list_lock)
160 {
161         kernel_message_t *k_msg;
162         spin_lock_irqsave(list_lock);
163         k_msg = STAILQ_FIRST(list_head);
164         if (k_msg)
165                 STAILQ_REMOVE_HEAD(list_head, link);
166         spin_unlock_irqsave(list_lock);
167         return k_msg;
168 }
169
170 /* Mostly the same as x86's implementation.  Keep them in sync.  This assumes
171  * you can send yourself an IPI, and that IPIs can get squashed like on x86. */
172 static void
173 handle_ipi(trapframe_t* tf)
174 {
175         clear_ipi();
176
177         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
178         kernel_message_t msg_cp, *k_msg;
179
180         while (1) { // will break out when there are no more messages
181                 /* Try to get an immediate message.  Exec and free it. */
182                 k_msg = get_next_amsg(&myinfo->immed_amsgs, &myinfo->immed_amsg_lock);
183                 if (k_msg) {
184                         assert(k_msg->pc);
185                         k_msg->pc(tf, k_msg->srcid, k_msg->arg0, k_msg->arg1, k_msg->arg2);
186                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
187                 } else { // no immediate, might be a routine
188                         if (in_kernel(tf))
189                                 return; // don't execute routine msgs if we were in the kernel
190                         k_msg = get_next_amsg(&myinfo->routine_amsgs,
191                                               &myinfo->routine_amsg_lock);
192                         if (!k_msg) // no routines either
193                                 return;
194                         /* copy in, and then free, in case we don't return */
195                         msg_cp = *k_msg;
196                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
197                         /* make sure an IPI is pending if we have more work */
198                         /* techincally, we don't need to lock when checking */
199                         if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
200                                 send_ipi(core_id());
201                         /* Execute the kernel message */
202                         assert(msg_cp.pc);
203                         msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
204                 }
205         }
206 }
207
208 /* Same as in x86.  Might be diff in the future if there is no way to check for
209  * immediate messages or there is the ability to selectively mask IPI vectors.*/
210 void
211 process_routine_kmsg(struct trapframe *tf)
212 {
213         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
214         kernel_message_t msg_cp, *k_msg;
215         int8_t irq_state = 0;
216
217         disable_irqsave(&irq_state);
218         /* If we were told what our TF was, use that.  o/w, go with current_tf. */
219         tf = tf ? tf : current_tf;
220         while (1) {
221                 /* normally, we want ints disabled, so we don't have an empty self-ipi
222                  * for every routine message. (imagine a long list of routines).  But we
223                  * do want immediates to run ahead of routines.  This enabling should
224                  * work (might not in some shitty VMs).  Also note we can receive an
225                  * extra self-ipi for routine messages before we turn off irqs again.
226                  * Not a big deal, since we will process it right away. */
227                 if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
228                         enable_irq();
229                         cpu_relax();
230                         disable_irq();
231                 }
232                 k_msg = get_next_amsg(&myinfo->routine_amsgs,
233                                       &myinfo->routine_amsg_lock);
234                 if (!k_msg) {
235                         enable_irqsave(&irq_state);
236                         return;
237                 }
238                 /* copy in, and then free, in case we don't return */
239                 msg_cp = *k_msg;
240                 kmem_cache_free(kernel_msg_cache, (void*)k_msg);
241                 /* make sure an IPI is pending if we have more work */
242                 if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
243                         send_ipi(core_id());
244                 /* Execute the kernel message */
245                 assert(msg_cp.pc);
246                 msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
247         }
248 }
249
250 static void
251 unhandled_trap(trapframe_t* state, const char* name)
252 {
253         static spinlock_t screwup_lock = SPINLOCK_INITIALIZER;
254         spin_lock(&screwup_lock);
255
256         if(in_kernel(state))
257         {
258                 print_trapframe(state);
259                 panic("Unhandled trap in kernel!\nTrap type: %s", name);
260         }
261         else
262         {
263                 char tf_buf[1024];
264                 format_trapframe(state, tf_buf, sizeof(tf_buf));
265
266                 warn("Unhandled trap in user!\nTrap type: %s\n%s", name, tf_buf);
267                 backtrace();
268                 spin_unlock(&screwup_lock);
269
270                 assert(current);
271                 proc_destroy(current);
272         }
273 }
274
275 static void
276 handle_timer_interrupt(trapframe_t* state)
277 {
278         timer_interrupt(state, NULL);
279 }
280
281 static void
282 handle_interrupt(trapframe_t* tf)
283 {
284         if (!in_kernel(tf))
285                 set_current_tf(&per_cpu_info[core_id()], tf);
286         else if((void*)tf->epc == &cpu_halt) // break out of the cpu_halt loop
287                 advance_pc(tf);
288         
289         typedef void (*trap_handler)(trapframe_t*);
290         
291         const static trap_handler trap_handlers[NIRQ] = {
292           [TIMER_IRQ] = handle_timer_interrupt,
293           [IPI_IRQ] = handle_ipi,
294         };
295
296         uintptr_t interrupts = (tf->cause & CAUSE_IP) >> CAUSE_IP_SHIFT;
297
298         for(uintptr_t i = 0; interrupts; interrupts >>= 1, i++)
299         {
300                 if(interrupts & 1)
301                 {
302                         if(trap_handlers[i])
303                                 trap_handlers[i](tf);
304                         else
305                                 panic("Bad interrupt %d", i);
306                 }
307         }
308 }
309
310 static void
311 handle_misaligned_fetch(trapframe_t* state)
312 {
313         unhandled_trap(state, "Misaligned Fetch");
314 }
315
316 static void
317 handle_misaligned_load(trapframe_t* state)
318 {
319         unhandled_trap(state, "Misaligned Load");
320 }
321
322 static void
323 handle_misaligned_store(trapframe_t* state)
324 {
325         unhandled_trap(state, "Misaligned Store");
326 }
327
328 static void
329 handle_fault_fetch(trapframe_t* state)
330 {
331         if(in_kernel(state))
332         {
333                 print_trapframe(state);
334                 panic("Instruction Page Fault in the Kernel at %p!", state->badvaddr);
335         }
336         
337         if(handle_page_fault(current, state->badvaddr, PROT_EXEC))
338                 unhandled_trap(state, "Instruction Page Fault");
339 }
340
341 static void
342 handle_fault_load(trapframe_t* state)
343 {
344         if(in_kernel(state))
345         {
346                 print_trapframe(state);
347                 panic("Load Page Fault in the Kernel at %p!", state->badvaddr);
348         }
349         
350         if(handle_page_fault(current, state->badvaddr, PROT_READ))
351                 unhandled_trap(state, "Load Page Fault");
352 }
353
354 static void
355 handle_fault_store(trapframe_t* state)
356 {
357         if(in_kernel(state))
358         {
359                 print_trapframe(state);
360                 panic("Store Page Fault in the Kernel at %p!", state->badvaddr);
361         }
362         
363         if(handle_page_fault(current, state->badvaddr, PROT_WRITE))
364                 unhandled_trap(state, "Store Page Fault");
365 }
366
367 static void
368 handle_illegal_instruction(trapframe_t* state)
369 {
370         unhandled_trap(state, "Illegal Instruction");
371 }
372
373 static void
374 handle_fp_disabled(trapframe_t* tf)
375 {
376         if(in_kernel(tf))
377                 panic("kernel executed an FP instruction!");
378
379         tf->sr |= SR_EF;
380         env_pop_tf(tf); /* We didn't save our TF, so don't use proc_restartcore */
381 }
382
383 static void
384 handle_syscall(trapframe_t* state)
385 {
386         uintptr_t a0 = state->gpr[4];
387         uintptr_t a1 = state->gpr[5];
388
389         advance_pc(state);
390         set_current_tf(&per_cpu_info[core_id()], state);
391         enable_irq();
392         prep_syscalls(current, (struct syscall*)a0, a1);
393 }
394
395 static void
396 handle_breakpoint(trapframe_t* state)
397 {
398         advance_pc(state);
399         monitor(state);
400 }
401
402 void
403 handle_trap(trapframe_t* tf)
404 {
405         static void (*const trap_handlers[NUM_CAUSES])(trapframe_t*) = {
406           [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
407           [CAUSE_FAULT_FETCH] = handle_fault_fetch,
408           [CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
409           [CAUSE_PRIVILEGED_INSTRUCTION] = handle_illegal_instruction,
410           [CAUSE_FP_DISABLED] = handle_fp_disabled,
411           [CAUSE_INTERRUPT] = handle_interrupt,
412           [CAUSE_SYSCALL] = handle_syscall,
413           [CAUSE_BREAKPOINT] = handle_breakpoint,
414           [CAUSE_MISALIGNED_LOAD] = handle_misaligned_load,
415           [CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
416           [CAUSE_FAULT_LOAD] = handle_fault_load,
417           [CAUSE_FAULT_STORE] = handle_fault_store,
418         };
419         
420         int exccode = (tf->cause & CAUSE_EXCCODE) >> CAUSE_EXCCODE_SHIFT;
421         assert(exccode < NUM_CAUSES && trap_handlers[exccode]);
422         trap_handlers[exccode](tf);
423         
424         /* Return to the current process, which should be runnable.  If we're the
425          * kernel, we should just return naturally.  Note that current and tf need
426          * to still be okay (might not be after blocking) */
427         if (in_kernel(tf))
428                 env_pop_tf(tf);
429         else
430                 proc_restartcore();
431 }
432
433 /* We don't have NMIs now. */
434 void send_nmi(uint32_t os_coreid)
435 {
436 }