Updated RISC-V boot sequence; use 8KB pages
[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 set_current_tf(struct per_cpu_info *pcpui, struct trapframe *tf)
105 {
106         if (irq_is_enabled())
107                 warn("Turn off IRQs until cur_tf is set!");
108         assert(!pcpui->cur_tf);
109         pcpui->actual_tf = *tf;
110         pcpui->cur_tf = &pcpui->actual_tf;
111 }
112
113 static int
114 format_trapframe(trapframe_t *tf, char* buf, int bufsz)
115 {
116         // slightly hackish way to read out the instruction that faulted.
117         // not guaranteed to be right 100% of the time
118         uint32_t insn;
119         if(!(current && !memcpy_from_user(current,&insn,(void*)tf->epc,4)))
120                 insn = -1;
121
122         int len = snprintf(buf,bufsz,"TRAP frame at %p on core %d\n",
123                            tf, core_id());
124         static const char* regnames[] = {
125           "z ", "ra", "v0", "v1", "a0", "a1", "a2", "a3",
126           "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
127           "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3",
128           "s4", "s5", "s6", "s7", "s8", "fp", "sp", "tp"
129         };
130         
131         tf->gpr[0] = 0;
132         
133         for(int i = 0; i < 32; i+=4)
134         {
135                 for(int j = 0; j < 4; j++)
136                         len += snprintf(buf+len, bufsz-len,
137                                         "%s %016lx%c", regnames[i+j], tf->gpr[i+j], 
138                                         j < 3 ? ' ' : '\n');
139         }
140         len += snprintf(buf+len, bufsz-len,
141                         "sr %016lx pc %016lx va %016lx insn       %008x\n", tf->sr, tf->epc,
142                         tf->badvaddr, (uint32_t)tf->insn);
143
144         return len;
145 }
146
147 void
148 print_trapframe(trapframe_t* tf)
149 {
150         char buf[1024];
151         int len = format_trapframe(tf,buf,sizeof(buf));
152         cputbuf(buf,len);
153 }
154
155 /* Helper function.  Returns 0 if the list was empty. */
156 static kernel_message_t *get_next_amsg(struct kernel_msg_list *list_head,
157                                        spinlock_t *list_lock)
158 {
159         kernel_message_t *k_msg;
160         spin_lock_irqsave(list_lock);
161         k_msg = STAILQ_FIRST(list_head);
162         if (k_msg)
163                 STAILQ_REMOVE_HEAD(list_head, link);
164         spin_unlock_irqsave(list_lock);
165         return k_msg;
166 }
167
168 /* Mostly the same as x86's implementation.  Keep them in sync.  This assumes
169  * you can send yourself an IPI, and that IPIs can get squashed like on x86. */
170 static void
171 handle_ipi(trapframe_t* tf)
172 {
173         clear_ipi();
174
175         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
176         if (!in_kernel(tf))
177                 set_current_tf(pcpui, tf);
178         else if((void*)tf->epc == &cpu_halt) // break out of the cpu_halt loop
179                 advance_pc(tf);
180
181         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
182         kernel_message_t msg_cp, *k_msg;
183
184         while (1) { // will break out when there are no more messages
185                 /* Try to get an immediate message.  Exec and free it. */
186                 k_msg = get_next_amsg(&myinfo->immed_amsgs, &myinfo->immed_amsg_lock);
187                 if (k_msg) {
188                         assert(k_msg->pc);
189                         k_msg->pc(tf, k_msg->srcid, k_msg->arg0, k_msg->arg1, k_msg->arg2);
190                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
191                 } else { // no immediate, might be a routine
192                         if (in_kernel(tf))
193                                 return; // don't execute routine msgs if we were in the kernel
194                         k_msg = get_next_amsg(&myinfo->routine_amsgs,
195                                               &myinfo->routine_amsg_lock);
196                         if (!k_msg) // no routines either
197                                 return;
198                         /* copy in, and then free, in case we don't return */
199                         msg_cp = *k_msg;
200                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
201                         /* make sure an IPI is pending if we have more work */
202                         /* techincally, we don't need to lock when checking */
203                         if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
204                                 send_ipi(core_id());
205                         /* Execute the kernel message */
206                         assert(msg_cp.pc);
207                         msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
208                 }
209         }
210 }
211
212 /* Same as in x86.  Might be diff in the future if there is no way to check for
213  * immediate messages or there is the ability to selectively mask IPI vectors.*/
214 void process_routine_kmsg(struct trapframe *tf)
215 {
216         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
217         kernel_message_t msg_cp, *k_msg;
218         int8_t irq_state = 0;
219
220         disable_irqsave(&irq_state);
221         /* If we were told what our TF was, use that.  o/w, go with current_tf. */
222         tf = tf ? tf : current_tf;
223         while (1) {
224                 /* normally, we want ints disabled, so we don't have an empty self-ipi
225                  * for every routine message. (imagine a long list of routines).  But we
226                  * do want immediates to run ahead of routines.  This enabling should
227                  * work (might not in some shitty VMs).  Also note we can receive an
228                  * extra self-ipi for routine messages before we turn off irqs again.
229                  * Not a big deal, since we will process it right away. */
230                 if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
231                         enable_irq();
232                         cpu_relax();
233                         disable_irq();
234                 }
235                 k_msg = get_next_amsg(&myinfo->routine_amsgs,
236                                       &myinfo->routine_amsg_lock);
237                 if (!k_msg) {
238                         enable_irqsave(&irq_state);
239                         return;
240                 }
241                 /* copy in, and then free, in case we don't return */
242                 msg_cp = *k_msg;
243                 kmem_cache_free(kernel_msg_cache, (void*)k_msg);
244                 /* make sure an IPI is pending if we have more work */
245                 if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
246                         send_ipi(core_id());
247                 /* Execute the kernel message */
248                 assert(msg_cp.pc);
249                 msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
250         }
251 }
252
253 static void
254 unhandled_trap(trapframe_t* state, const char* name)
255 {
256         static spinlock_t screwup_lock = SPINLOCK_INITIALIZER;
257         spin_lock(&screwup_lock);
258
259         if(in_kernel(state))
260         {
261                 print_trapframe(state);
262                 panic("Unhandled trap in kernel!\nTrap type: %s", name);
263         }
264         else
265         {
266                 char tf_buf[1024];
267                 int tf_len = format_trapframe(state, tf_buf, sizeof(tf_buf));
268
269                 warn("Unhandled trap in user!\nTrap type: %s\n%s", name, tf_buf);
270                 backtrace();
271                 spin_unlock(&screwup_lock);
272
273                 assert(current);
274                 proc_destroy(current);
275                 /* Not sure if RISCV has a central point that would run proc_restartcore
276                  */
277                 proc_restartcore();
278         }
279 }
280
281 static void
282 handle_timer_interrupt(trapframe_t* state)
283 {
284         timer_interrupt(state, NULL);
285 }
286
287 static void
288 handle_interrupt(trapframe_t* state)
289 {
290         typedef void (*trap_handler)(trapframe_t*);
291         
292         const static trap_handler trap_handlers[NIRQ] = {
293           [TIMER_IRQ] = handle_timer_interrupt,
294           [IPI_IRQ] = handle_ipi,
295         };
296
297         uintptr_t interrupts = (state->cause & CAUSE_IP) >> CAUSE_IP_SHIFT;
298
299         for(uintptr_t i = 0; interrupts; interrupts >>= 1, i++)
300         {
301                 if(interrupts & 1)
302                 {
303                         if(trap_handlers[i])
304                                 trap_handlers[i](state);
305                         else
306                         {
307                                 char name[32];
308                                 snprintf(name, sizeof(name), "Bad Interrupt %d", i);
309                                 unhandled_trap(state, name);
310                         }
311                 }
312         }
313 }
314
315 static void
316 handle_misaligned_fetch(trapframe_t* state)
317 {
318         unhandled_trap(state, "Misaligned Fetch");
319 }
320
321 static void
322 handle_misaligned_load(trapframe_t* state)
323 {
324         unhandled_trap(state, "Misaligned Load");
325 }
326
327 static void
328 handle_misaligned_store(trapframe_t* state)
329 {
330         unhandled_trap(state, "Misaligned Store");
331 }
332
333 static void
334 handle_fault_fetch(trapframe_t* state)
335 {
336         if(in_kernel(state))
337         {
338                 print_trapframe(state);
339                 panic("Instruction Page Fault in the Kernel at %p!", state->badvaddr);
340         }
341         
342         if(handle_page_fault(current, state->badvaddr, PROT_READ))
343                 unhandled_trap(state, "Instruction Page Fault");
344 }
345
346 static void
347 handle_fault_load(trapframe_t* state)
348 {
349         if(in_kernel(state))
350         {
351                 print_trapframe(state);
352                 panic("Load Page Fault in the Kernel at %p!", state->badvaddr);
353         }
354         
355         if(handle_page_fault(current, state->badvaddr, PROT_READ))
356                 unhandled_trap(state, "Load Page Fault");
357 }
358
359 static void
360 handle_fault_store(trapframe_t* state)
361 {
362         if(in_kernel(state))
363         {
364                 print_trapframe(state);
365                 panic("Store Page Fault in the Kernel at %p!", state->badvaddr);
366         }
367         
368         if(handle_page_fault(current, state->badvaddr, PROT_WRITE))
369                 unhandled_trap(state, "Store Page Fault");
370 }
371
372 static void
373 handle_illegal_instruction(trapframe_t* state)
374 {
375         unhandled_trap(state, "Illegal Instruction");
376 }
377
378 static void
379 handle_fp_disabled(trapframe_t* state)
380 {
381         if(in_kernel(state))
382                 panic("kernel executed an FP instruction!");
383
384         state->sr |= SR_EF;
385 }
386
387 static void
388 handle_syscall(trapframe_t* state)
389 {
390         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
391         uintptr_t a0 = state->gpr[4];
392         uintptr_t a1 = state->gpr[5];
393
394         advance_pc(state);
395         enable_irq();
396         struct per_cpu_info* coreinfo = &per_cpu_info[core_id()];
397
398         set_current_tf(pcpui, state);
399
400         prep_syscalls(current, (struct syscall*)a0, a1);
401
402         proc_restartcore();
403 }
404
405 static void
406 handle_breakpoint(trapframe_t* state)
407 {
408         advance_pc(state);
409         monitor(state);
410 }
411
412 void
413 handle_trap(trapframe_t* tf)
414 {
415         typedef void (*trap_handler)(trapframe_t*);
416         
417         const static trap_handler trap_handlers[NUM_CAUSES] = {
418           [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
419           [CAUSE_FAULT_FETCH] = handle_fault_fetch,
420           [CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
421           [CAUSE_PRIVILEGED_INSTRUCTION] = handle_illegal_instruction,
422           [CAUSE_FP_DISABLED] = handle_fp_disabled,
423           [CAUSE_INTERRUPT] = handle_interrupt,
424           [CAUSE_SYSCALL] = handle_syscall,
425           [CAUSE_BREAKPOINT] = handle_breakpoint,
426           [CAUSE_MISALIGNED_LOAD] = handle_misaligned_load,
427           [CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
428           [CAUSE_FAULT_LOAD] = handle_fault_load,
429           [CAUSE_FAULT_STORE] = handle_fault_store,
430         };
431         
432         int exccode = (tf->cause & CAUSE_EXCCODE) >> CAUSE_EXCCODE_SHIFT;
433         assert(exccode < NUM_CAUSES && trap_handlers[exccode]);
434         
435         trap_handlers[exccode](tf);
436         
437         env_pop_tf(tf);
438 }
439
440 /* We don't have NMIs now. */
441 void send_nmi(uint32_t os_coreid)
442 {
443 }