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