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