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