KMSGs no longer self-ipi for routine messages
[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 <umem.h>
14 #include <pmap.h>
15 #include <kdebug.h>
16
17 #ifdef __SHARC__
18 #pragma nosharc
19 #endif
20
21 #ifdef __DEPUTY__
22 #pragma nodeputy
23 #endif
24
25 /* These are the stacks the kernel will load when it receives a trap from user
26  * space.  The deal is that they get set right away in entry.S, and can always
27  * be used for finding the top of the stack (from which you should subtract the
28  * sizeof the trapframe.  Note, we need to have a junk value in the array so
29  * that this is NOT part of the BSS.  If it is in the BSS, it will get 0'd in
30  * kernel_init(), which is after these values get set.
31  *
32  * TODO: if these end up becoming contended cache lines, move this to
33  * per_cpu_info. */
34 uintptr_t core_stacktops[MAX_NUM_CPUS] = {0xcafebabe, 0};
35
36 struct kmem_cache *kernel_msg_cache;
37 void kernel_msg_init(void)
38 {
39         kernel_msg_cache = kmem_cache_create("kernel_msgs",
40                            sizeof(struct kernel_message), HW_CACHE_ALIGN, 0, 0, 0);
41 }
42
43 spinlock_t kernel_message_buf_busy[MAX_NUM_CPUS] = {SPINLOCK_INITIALIZER};
44 kernel_message_t kernel_message_buf[MAX_NUM_CPUS];
45
46 /* This is mostly identical to x86's, minus the different send_ipi call. */
47 uint32_t send_kernel_message(uint32_t dst, amr_t pc, long arg0, long arg1,
48                              long arg2, int type)
49 {
50         kernel_message_t *k_msg;
51         assert(pc);
52         // note this will be freed on the destination core
53         k_msg = (kernel_message_t *CT(1))TC(kmem_cache_alloc(kernel_msg_cache, 0));
54         k_msg->srcid = core_id();
55         k_msg->pc = pc;
56         k_msg->arg0 = arg0;
57         k_msg->arg1 = arg1;
58         k_msg->arg2 = arg2;
59         switch (type) {
60                 case KMSG_IMMEDIATE:
61                         spin_lock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
62                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].immed_amsgs, k_msg, link);
63                         spin_unlock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
64                         break;
65                 case KMSG_ROUTINE:
66                         spin_lock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
67                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].routine_amsgs, k_msg, link);
68                         spin_unlock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
69                         break;
70                 default:
71                         panic("Unknown type of kernel message!");
72         }
73         /* if we're sending a routine message locally, we don't want/need an IPI */
74         if ((dst != k_msg->srcid) || (type == KMSG_IMMEDIATE))
75                 send_ipi(dst);
76         return 0;
77 }
78
79 void
80 advance_pc(trapframe_t* state)
81 {
82         state->pc = state->npc;
83         state->npc += 4;
84 }
85
86 /* Set stacktop for the current core to be the stack the kernel will start on
87  * when trapping/interrupting from userspace */
88 void set_stack_top(uintptr_t stacktop)
89 {
90         core_stacktops[core_id()] = stacktop;
91 }
92
93 /* Note the assertion assumes we are in the top page of the stack. */
94 uintptr_t get_stack_top(void)
95 {
96         uintptr_t sp, stacktop;
97         stacktop = core_stacktops[core_id()];
98         asm volatile("mov %%sp,%0" : "=r"(sp));
99         assert(ROUNDUP(sp, PGSIZE) == stacktop);
100         return stacktop;
101 }
102
103 /* Starts running the current TF. */
104 void pop_kernel_tf(struct trapframe *tf)
105 {
106         /* TODO! also do save_kernel_tf() in kern/arch/sparc/trap.h */
107         panic("Not implemented.  =(");
108 }
109
110 /* Does nothing on sparc... */
111 void send_nmi(uint32_t os_coreid)
112 {
113 }
114
115 void
116 idt_init(void)
117 {
118 }
119
120 void
121 sysenter_init(void)
122 {
123 }
124
125 /* Helper.  For now, this copies out the TF to pcpui, and sets cur_tf to point
126  * to it. */
127 static void set_current_tf(struct per_cpu_info *pcpui, struct trapframe *tf)
128 {
129         if (irq_is_enabled())
130                 warn("Turn off IRQs until cur_tf is set!");
131         assert(!pcpui->cur_tf);
132         pcpui->actual_tf = *tf;
133         pcpui->cur_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                         /* Execute the kernel message */
264                         assert(msg_cp.pc);
265                         msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
266                 }
267         }
268 }
269
270 /* Same as in x86.  Might be diff in the future if there is no way to check for
271  * immediate messages or there is the ability to selectively mask IPI vectors.*/
272 void process_routine_kmsg(struct trapframe *tf)
273 {
274         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
275         kernel_message_t msg_cp, *k_msg;
276         int8_t irq_state = 0;
277
278         disable_irqsave(&irq_state);
279         /* If we were told what our TF was, use that.  o/w, go with current_tf. */
280         tf = tf ? tf : current_tf;
281         while (1) {
282                 /* normally, we want ints disabled, so we don't have an empty self-ipi
283                  * for every routine message. (imagine a long list of routines).  But we
284                  * do want immediates to run ahead of routines.  This enabling should
285                  * work (might not in some shitty VMs).  Also note we can receive an
286                  * extra self-ipi for routine messages before we turn off irqs again.
287                  * Not a big deal, since we will process it right away. */
288                 if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
289                         enable_irq();
290                         cpu_relax();
291                         disable_irq();
292                 }
293                 k_msg = get_next_amsg(&myinfo->routine_amsgs,
294                                       &myinfo->routine_amsg_lock);
295                 if (!k_msg) {
296                         enable_irqsave(&irq_state);
297                         return;
298                 }
299                 /* copy in, and then free, in case we don't return */
300                 msg_cp = *k_msg;
301                 kmem_cache_free(kernel_msg_cache, (void*)k_msg);
302                 /* Execute the kernel message */
303                 assert(msg_cp.pc);
304                 msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
305         }
306 }
307
308 /* extremely dangerous and racy: prints out the immed and routine kmsgs for a
309  * specific core (so possibly remotely).  Same as x86. */
310 void print_kmsgs(uint32_t coreid)
311 {
312         struct per_cpu_info *pcpui = &per_cpu_info[coreid];
313         void __print_kmsgs(struct kernel_msg_list *list, char *type)
314         {
315                 char *fn_name;
316                 struct kernel_message *kmsg_i;
317                 STAILQ_FOREACH(kmsg_i, list, link) {
318                         fn_name = get_fn_name((long)kmsg_i->pc);
319                         printk("%s KMSG on %d from %d to run %08p(%s)\n", type,
320                                kmsg_i->dstid, kmsg_i->srcid, kmsg_i->pc, fn_name); 
321                         kfree(fn_name);
322                 }
323         }
324         __print_kmsgs(&pcpui->immed_amsgs, "Immedte");
325         __print_kmsgs(&pcpui->routine_amsgs, "Routine");
326 }
327
328 void
329 unhandled_trap(trapframe_t* state)
330 {
331         char buf[TRAPNAME_MAX];
332         uint32_t trap_type = (state->tbr >> 4) & 0xFF;
333         get_trapname(trap_type,buf);
334
335         static spinlock_t screwup_lock = SPINLOCK_INITIALIZER;
336         spin_lock(&screwup_lock);
337
338         if(in_kernel(state))
339         {
340                 print_trapframe(state);
341                 panic("Unhandled trap in kernel!\nTrap type: %s",buf);
342         }
343         else
344         {
345                 char tf_buf[1024];
346                 int tf_len = format_trapframe(state,tf_buf,sizeof(tf_buf));
347
348                 warn("Unhandled trap in user!\nTrap type: %s\n%s",buf,tf_buf);
349                 backtrace();
350                 spin_unlock(&screwup_lock);
351
352                 assert(current);
353                 enable_irq();
354                 proc_destroy(current);
355                 /* Not sure if SPARC has a central point that would run proc_restartcore
356                  */
357                 proc_restartcore();
358         }
359 }
360
361 static trapframe_t*
362 stack_fucked(trapframe_t* state)
363 {
364         warn("You just got stack fucked!");
365         extern char tflush1, tflush2;
366         if(state->pc == (uint32_t)&tflush1 || state->pc == (uint32_t)&tflush2)
367                 return (trapframe_t*)(bootstacktop - core_id()*KSTKSIZE
368                                                    - sizeof(trapframe_t));
369         return state;
370 }
371
372 void
373 fill_misaligned(trapframe_t* state)
374 {
375         state = stack_fucked(state);
376         state->tbr = (state->tbr & ~0xFFF) | 0x070;
377         address_unaligned(state);
378 }
379
380 void
381 fill_pagefault(trapframe_t* state)
382 {
383         state = stack_fucked(state);
384         state->tbr = (state->tbr & ~0xFFF) | 0x090;
385         data_access_exception(state);
386 }
387
388 void
389 spill_misaligned(trapframe_t* state)
390 {
391         fill_misaligned(state);
392 }
393
394 void
395 spill_pagefault(trapframe_t* state)
396 {
397         fill_pagefault(state);
398 }
399
400 void
401 address_unaligned(trapframe_t* state)
402 {
403         unhandled_trap(state);
404 }
405
406 void
407 instruction_access_exception(trapframe_t* state)
408 {
409         if(in_kernel(state) || handle_page_fault(current,state->pc,PROT_EXEC))
410                 unhandled_trap(state);
411 }
412
413 void
414 data_access_exception(trapframe_t* state)
415 {
416         int prot = (state->fault_status & MMU_FSR_WR) ? PROT_WRITE : PROT_READ;
417
418         if(in_kernel(state) || handle_page_fault(current,state->fault_addr,prot))
419                 unhandled_trap(state);
420 }
421
422 void
423 illegal_instruction(trapframe_t* state)
424 {
425         unhandled_trap(state);
426 }
427
428 void
429 real_fp_exception(trapframe_t* state, ancillary_state_t* sillystate)
430 {
431         unhandled_trap(state);
432 }
433
434 void
435 fp_exception(trapframe_t* state)
436 {
437         ancillary_state_t sillystate;
438         save_fp_state(&sillystate);     
439
440         // since our FP HW exception behavior is sketchy, reexecute
441         // any faulting FP instruction in SW, which may call
442         // real_fp_exception above
443         emulate_fpu(state,&sillystate);
444
445         restore_fp_state(&sillystate);
446 }
447
448 void
449 fp_disabled(trapframe_t* state)
450 {
451         if(in_kernel(state))
452                 panic("kernel executed an FP instruction!");
453
454         state->psr |= PSR_EF;
455 }
456
457 void
458 handle_pop_tf(trapframe_t* state)
459 {
460         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
461         set_current_tf(pcpui, state);
462
463         trapframe_t tf, *tf_p = &tf;
464         if (memcpy_from_user(current,&tf,(void*)state->gpr[8],sizeof(tf))) {
465                 enable_irq();
466                 proc_destroy(current);
467                 proc_restartcore();
468         }
469
470         proc_secure_trapframe(&tf);
471         set_current_tf(pcpui, tf_p);
472         proc_restartcore();
473 }
474
475 void
476 handle_set_tf(trapframe_t* state)
477 {
478         advance_pc(state);
479         if (memcpy_to_user(current,(void*)state->gpr[8],state,sizeof(*state))) {
480                 proc_incref(current, 1);
481                 enable_irq();
482                 proc_destroy(current);
483                 proc_restartcore();
484         }
485 }
486
487 void
488 handle_syscall(trapframe_t* state)
489 {
490         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
491         uint32_t a0 = state->gpr[1];
492         uint32_t a1 = state->gpr[8];
493
494         advance_pc(state);
495         enable_irq();
496         struct per_cpu_info* coreinfo = &per_cpu_info[core_id()];
497
498         set_current_tf(pcpui, state);
499
500         prep_syscalls(current, (struct syscall*)a0, a1);
501
502         proc_restartcore();
503 }
504
505 void
506 flush_windows()
507 {
508         register int foo asm("g1");
509         register int nwin asm("g2");
510         extern int NWINDOWS;
511
512         nwin = NWINDOWS;
513         foo = nwin;
514
515         asm volatile ("1: deccc %0; bne,a 1b; save %%sp,-64,%%sp"
516                       : "=r"(foo) : "r"(foo));
517
518         foo = nwin;
519         asm volatile ("1: deccc %0; bne,a 1b; restore"
520                       : "=r"(foo) : "r"(foo));
521 }
522    
523 void
524 handle_flushw(trapframe_t* state)
525 {
526         // don't actually need to do anything here.
527         // trap_entry flushes user windows to the stack.
528         advance_pc(state);
529 }
530
531 void
532 handle_breakpoint(trapframe_t* state)
533 {
534         advance_pc(state);
535         monitor(state);
536 }