NMIs and cross-core trapframe inspection
[akaros.git] / kern / arch / sparc / 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 <ros/mman.h>
14 #include <umem.h>
15 #include <pmap.h>
16 #include <kdebug.h>
17
18 #ifdef __SHARC__
19 #pragma nosharc
20 #endif
21
22 #ifdef __DEPUTY__
23 #pragma nodeputy
24 #endif
25
26 /* These are the stacks the kernel will load when it receives a trap from user
27  * space.  The deal is that they get set right away in entry.S, and can always
28  * be used for finding the top of the stack (from which you should subtract the
29  * sizeof the trapframe.  Note, we need to have a junk value in the array so
30  * that this is NOT part of the BSS.  If it is in the BSS, it will get 0'd in
31  * kernel_init(), which is after these values get set.
32  *
33  * TODO: if these end up becoming contended cache lines, move this to
34  * per_cpu_info. */
35 uintptr_t core_stacktops[MAX_NUM_CPUS] = {0xcafebabe, 0};
36
37 struct kmem_cache *kernel_msg_cache;
38 void kernel_msg_init(void)
39 {
40         kernel_msg_cache = kmem_cache_create("kernel_msgs",
41                            sizeof(struct kernel_message), HW_CACHE_ALIGN, 0, 0, 0);
42 }
43
44 spinlock_t kernel_message_buf_busy[MAX_NUM_CPUS] = {SPINLOCK_INITIALIZER};
45 kernel_message_t kernel_message_buf[MAX_NUM_CPUS];
46
47 /* This is mostly identical to x86's, minus the different send_ipi call. */
48 uint32_t send_kernel_message(uint32_t dst, amr_t pc,
49                              TV(a0t) arg0, TV(a1t) arg1, TV(a2t) arg2, int type)
50 {
51         kernel_message_t *k_msg;
52         assert(pc);
53         // note this will be freed on the destination core
54         k_msg = (kernel_message_t *CT(1))TC(kmem_cache_alloc(kernel_msg_cache, 0));
55         k_msg->srcid = core_id();
56         k_msg->pc = pc;
57         k_msg->arg0 = arg0;
58         k_msg->arg1 = arg1;
59         k_msg->arg2 = arg2;
60         switch (type) {
61                 case KMSG_IMMEDIATE:
62                         spin_lock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
63                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].immed_amsgs, k_msg, link);
64                         spin_unlock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
65                         break;
66                 case KMSG_ROUTINE:
67                         spin_lock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
68                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].routine_amsgs, k_msg, link);
69                         spin_unlock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
70                         break;
71                 default:
72                         panic("Unknown type of kernel message!");
73         }
74         /* if we're sending a routine message locally, we don't want/need an IPI */
75         if ((dst != k_msg->srcid) || (type == KMSG_IMMEDIATE))
76                 send_ipi(dst);
77         return 0;
78 }
79
80 void
81 advance_pc(trapframe_t* state)
82 {
83         state->pc = state->npc;
84         state->npc += 4;
85 }
86
87 /* Set stacktop for the current core to be the stack the kernel will start on
88  * when trapping/interrupting from userspace */
89 void set_stack_top(uintptr_t stacktop)
90 {
91         core_stacktops[core_id()] = stacktop;
92 }
93
94 /* Note the assertion assumes we are in the top page of the stack. */
95 uintptr_t get_stack_top(void)
96 {
97         uintptr_t sp, stacktop;
98         stacktop = core_stacktops[core_id()];
99         asm volatile("mov %%sp,%0" : "=r"(sp));
100         assert(ROUNDUP(sp, PGSIZE) == stacktop);
101         return stacktop;
102 }
103
104 /* Starts running the current TF. */
105 void pop_kernel_tf(struct trapframe *tf)
106 {
107         /* TODO! also do save_kernel_tf() in kern/arch/sparc/trap.h */
108         panic("Not implemented.  =(");
109 }
110
111 /* Does nothing on sparc... */
112 void send_nmi(uint32_t os_coreid)
113 {
114 }
115
116 void
117 idt_init(void)
118 {
119 }
120
121 void
122 sysenter_init(void)
123 {
124 }
125
126 /* Helper.  For now, this copies out the TF to pcpui, and sets the tf to use it.
127  * Eventually, we ought to do this in trap_entry.S.  Honestly, do whatever you
128  * want with this.  The **tf is for convenience in x86. */
129 static void set_current_tf(struct per_cpu_info *pcpui, struct trapframe **tf)
130 {
131         pcpui->actual_tf = **tf;
132         pcpui->cur_tf = &pcpui->actual_tf;
133         *tf = &pcpui->actual_tf;
134 }
135
136 static int
137 format_trapframe(trapframe_t *tf, char* buf, int bufsz)
138 {
139         // slightly hackish way to read out the instruction that faulted.
140         // not guaranteed to be right 100% of the time
141         uint32_t insn;
142         if(!(current && !memcpy_from_user(current,&insn,(void*)tf->pc,4)))
143                 insn = -1;
144
145         int len = snprintf(buf,bufsz,"TRAP frame at %p on core %d\n",
146                            tf, core_id());
147
148         for(int i = 0; i < 8; i++)
149         {
150                 len += snprintf(buf+len,bufsz-len,
151                                 "  g%d   0x%08x  o%d   0x%08x"
152                                 "  l%d   0x%08x  i%d   0x%08x\n",
153                                 i,tf->gpr[i],i,tf->gpr[i+8],
154                                 i,tf->gpr[i+16],i,tf->gpr[i+24]);
155         }
156
157         len += snprintf(buf+len,bufsz-len,
158                         "  psr  0x%08x  pc   0x%08x  npc  0x%08x  insn 0x%08x\n",
159                         tf->psr,tf->pc,tf->npc,insn);
160         len += snprintf(buf+len,bufsz-len,
161                         "  y    0x%08x  fsr  0x%08x  far  0x%08x  tbr  0x%08x\n",
162                         tf->y,tf->fault_status,tf->fault_addr,tf->tbr);
163         len += snprintf(buf+len,bufsz-len,
164                         "  timestamp  %21lld\n",tf->timestamp);
165
166         return len;
167 }
168
169 void
170 print_trapframe(trapframe_t* tf)
171 {
172         char buf[1024];
173         int len = format_trapframe(tf,buf,sizeof(buf));
174         cputbuf(buf,len);
175 }
176
177 #define TRAPNAME_MAX    32
178
179 static char*
180 get_trapname(uint8_t tt, char buf[TRAPNAME_MAX])
181 {
182         static const char* trapnames[] = {
183                 [0x00] "reset",
184                 [0x01] "instruction access exception",
185                 [0x02] "illegal instruction",
186                 [0x03] "privileged instruction",
187                 [0x04] "floating point disabled",
188                 [0x05] "window overflow",
189                 [0x06] "window underflow",
190                 [0x07] "memory address not aligned",
191                 [0x08] "floating point exception",
192                 [0x09] "data access exception",
193                 [0x20] "register access error",
194                 [0x21] "instruction access error",
195                 [0x24] "coprocessor disabled",
196                 [0x25] "unimplemented FLUSH",
197                 [0x28] "coprocessor exception",
198                 [0x29] "data access error",
199                 [0x2A] "division by zero",
200                 [0x2B] "data store error",
201                 [0x2C] "data MMU miss",
202                 [0x3C] "instruction MMU miss"
203         };
204
205         if(tt >= 0x80)
206                 snprintf(buf,TRAPNAME_MAX,"user trap 0x%02x",tt);
207         else if(tt >= 0x10 && tt < 0x20)
208                 snprintf(buf,TRAPNAME_MAX,"interrupt 0x%x",tt-0x10);
209         else if(tt >= sizeof(trapnames)/sizeof(trapnames[0]) || !trapnames[tt])
210                 snprintf(buf,TRAPNAME_MAX,"(unknown trap 0x%02x)",tt);
211         else
212         {
213                 strncpy(buf,trapnames[tt],TRAPNAME_MAX);
214                 buf[TRAPNAME_MAX-1] = 0;
215         }
216
217         return buf;
218 }
219
220 /* Helper function.  Returns 0 if the list was empty. */
221 static kernel_message_t *get_next_amsg(struct kernel_msg_list *list_head,
222                                        spinlock_t *list_lock)
223 {
224         kernel_message_t *k_msg;
225         spin_lock_irqsave(list_lock);
226         k_msg = STAILQ_FIRST(list_head);
227         if (k_msg)
228                 STAILQ_REMOVE_HEAD(list_head, link);
229         spin_unlock_irqsave(list_lock);
230         return k_msg;
231 }
232
233 /* Mostly the same as x86's implementation.  Keep them in sync.  This assumes
234  * you can send yourself an IPI, and that IPIs can get squashed like on x86. */
235 void handle_ipi(trapframe_t* tf)
236 {
237         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
238         if (!in_kernel(tf))
239                 set_current_tf(pcpui, &tf);
240         else if((void*)tf->pc == &cpu_halt) // break out of the cpu_halt loop
241                 advance_pc(tf);
242
243         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
244         kernel_message_t msg_cp, *k_msg;
245
246         while (1) { // will break out when there are no more messages
247                 /* Try to get an immediate message.  Exec and free it. */
248                 k_msg = get_next_amsg(&myinfo->immed_amsgs, &myinfo->immed_amsg_lock);
249                 if (k_msg) {
250                         assert(k_msg->pc);
251                         k_msg->pc(tf, k_msg->srcid, k_msg->arg0, k_msg->arg1, k_msg->arg2);
252                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
253                 } else { // no immediate, might be a routine
254                         if (in_kernel(tf))
255                                 return; // don't execute routine msgs if we were in the kernel
256                         k_msg = get_next_amsg(&myinfo->routine_amsgs,
257                                               &myinfo->routine_amsg_lock);
258                         if (!k_msg) // no routines either
259                                 return;
260                         /* copy in, and then free, in case we don't return */
261                         msg_cp = *k_msg;
262                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
263                         /* make sure an IPI is pending if we have more work */
264                         /* techincally, we don't need to lock when checking */
265                         if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
266                                 send_ipi(core_id());
267                         /* Execute the kernel message */
268                         assert(msg_cp.pc);
269                         msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
270                 }
271         }
272 }
273
274 /* Same as in x86.  Might be diff in the future if there is no way to check for
275  * immediate messages or there is the ability to selectively mask IPI vectors.*/
276 void process_routine_kmsg(struct trapframe *tf)
277 {
278         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
279         kernel_message_t msg_cp, *k_msg;
280         int8_t irq_state = 0;
281
282         disable_irqsave(&irq_state);
283         /* If we were told what our TF was, use that.  o/w, go with current_tf. */
284         tf = tf ? tf : current_tf;
285         while (1) {
286                 /* normally, we want ints disabled, so we don't have an empty self-ipi
287                  * for every routine message. (imagine a long list of routines).  But we
288                  * do want immediates to run ahead of routines.  This enabling should
289                  * work (might not in some shitty VMs).  Also note we can receive an
290                  * extra self-ipi for routine messages before we turn off irqs again.
291                  * Not a big deal, since we will process it right away. */
292                 if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
293                         enable_irq();
294                         cpu_relax();
295                         disable_irq();
296                 }
297                 k_msg = get_next_amsg(&myinfo->routine_amsgs,
298                                       &myinfo->routine_amsg_lock);
299                 if (!k_msg) {
300                         enable_irqsave(&irq_state);
301                         return;
302                 }
303                 /* copy in, and then free, in case we don't return */
304                 msg_cp = *k_msg;
305                 kmem_cache_free(kernel_msg_cache, (void*)k_msg);
306                 /* make sure an IPI is pending if we have more work */
307                 if (!STAILQ_EMPTY(&myinfo->routine_amsgs))
308                         send_ipi(core_id());
309                 /* Execute the kernel message */
310                 assert(msg_cp.pc);
311                 msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
312         }
313 }
314
315 void
316 unhandled_trap(trapframe_t* state)
317 {
318         char buf[TRAPNAME_MAX];
319         uint32_t trap_type = (state->tbr >> 4) & 0xFF;
320         get_trapname(trap_type,buf);
321
322         static spinlock_t screwup_lock = SPINLOCK_INITIALIZER;
323         spin_lock(&screwup_lock);
324
325         if(in_kernel(state))
326         {
327                 print_trapframe(state);
328                 panic("Unhandled trap in kernel!\nTrap type: %s",buf);
329         }
330         else
331         {
332                 char tf_buf[1024];
333                 int tf_len = format_trapframe(state,tf_buf,sizeof(tf_buf));
334
335                 warn("Unhandled trap in user!\nTrap type: %s\n%s",buf,tf_buf);
336                 backtrace();
337                 spin_unlock(&screwup_lock);
338
339                 assert(current);
340                 proc_incref(current, 1);
341                 proc_destroy(current);
342
343                 panic("I shouldn't have gotten here!");
344         }
345 }
346
347 static trapframe_t*
348 stack_fucked(trapframe_t* state)
349 {
350         warn("You just got stack fucked!");
351         extern char tflush1, tflush2;
352         if(state->pc == (uint32_t)&tflush1 || state->pc == (uint32_t)&tflush2)
353                 return (trapframe_t*)(bootstacktop - core_id()*KSTKSIZE
354                                                    - sizeof(trapframe_t));
355         return state;
356 }
357
358 void
359 fill_misaligned(trapframe_t* state)
360 {
361         state = stack_fucked(state);
362         state->tbr = (state->tbr & ~0xFFF) | 0x070;
363         address_unaligned(state);
364 }
365
366 void
367 fill_pagefault(trapframe_t* state)
368 {
369         state = stack_fucked(state);
370         state->tbr = (state->tbr & ~0xFFF) | 0x090;
371         data_access_exception(state);
372 }
373
374 void
375 spill_misaligned(trapframe_t* state)
376 {
377         fill_misaligned(state);
378 }
379
380 void
381 spill_pagefault(trapframe_t* state)
382 {
383         fill_pagefault(state);
384 }
385
386 void
387 address_unaligned(trapframe_t* state)
388 {
389         unhandled_trap(state);
390 }
391
392 void
393 instruction_access_exception(trapframe_t* state)
394 {
395         if(in_kernel(state) || handle_page_fault(current,state->pc,PROT_EXEC))
396                 unhandled_trap(state);
397 }
398
399 void
400 data_access_exception(trapframe_t* state)
401 {
402         int prot = (state->fault_status & MMU_FSR_WR) ? PROT_WRITE : PROT_READ;
403
404         if(in_kernel(state) || handle_page_fault(current,state->fault_addr,prot))
405                 unhandled_trap(state);
406 }
407
408 void
409 illegal_instruction(trapframe_t* state)
410 {
411         unhandled_trap(state);
412 }
413
414 void
415 real_fp_exception(trapframe_t* state, ancillary_state_t* sillystate)
416 {
417         unhandled_trap(state);
418 }
419
420 void
421 fp_exception(trapframe_t* state)
422 {
423         ancillary_state_t sillystate;
424         save_fp_state(&sillystate);     
425
426         // since our FP HW exception behavior is sketchy, reexecute
427         // any faulting FP instruction in SW, which may call
428         // real_fp_exception above
429         emulate_fpu(state,&sillystate);
430
431         restore_fp_state(&sillystate);
432 }
433
434 void
435 fp_disabled(trapframe_t* state)
436 {
437         if(in_kernel(state))
438                 panic("kernel executed an FP instruction!");
439
440         state->psr |= PSR_EF;
441 }
442
443 void
444 handle_pop_tf(trapframe_t* state)
445 {
446         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
447         set_current_tf(pcpui, &state);
448
449         trapframe_t tf, *tf_p = &tf;
450         if (memcpy_from_user(current,&tf,(void*)state->gpr[8],sizeof(tf))) {
451                 proc_incref(current, 1);
452                 proc_destroy(current);
453                 assert(0);
454         }
455
456         proc_secure_trapframe(&tf);
457         set_current_tf(pcpui, &tf_p);
458         proc_restartcore();
459 }
460
461 void
462 handle_set_tf(trapframe_t* state)
463 {
464         advance_pc(state);
465         if (memcpy_to_user(current,(void*)state->gpr[8],state,sizeof(*state))) {
466                 proc_incref(current, 1);
467                 proc_destroy(current);
468                 assert(0);
469         }
470 }
471
472 void
473 handle_syscall(trapframe_t* state)
474 {
475         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
476         uint32_t a0 = state->gpr[1];
477         uint32_t a1 = state->gpr[8];
478
479         advance_pc(state);
480         enable_irq();
481         struct per_cpu_info* coreinfo = &per_cpu_info[core_id()];
482
483         set_current_tf(pcpui, &state);
484
485         prep_syscalls(current, (struct syscall*)a0, a1);
486
487         proc_restartcore();
488 }
489
490 void
491 flush_windows()
492 {
493         register int foo asm("g1");
494         register int nwin asm("g2");
495         extern int NWINDOWS;
496
497         nwin = NWINDOWS;
498         foo = nwin;
499
500         asm volatile ("1: deccc %0; bne,a 1b; save %%sp,-64,%%sp"
501                       : "=r"(foo) : "r"(foo));
502
503         foo = nwin;
504         asm volatile ("1: deccc %0; bne,a 1b; restore"
505                       : "=r"(foo) : "r"(foo));
506 }
507    
508 void
509 handle_flushw(trapframe_t* state)
510 {
511         // don't actually need to do anything here.
512         // trap_entry flushes user windows to the stack.
513         advance_pc(state);
514 }
515
516 void
517 handle_breakpoint(trapframe_t* state)
518 {
519         advance_pc(state);
520         monitor(state);
521 }