1.0.4.13: refactor trap handling
[sbcl.git] / src / runtime / mips-arch.c
1 /*
2
3  This code was written as part of the CMU Common Lisp project at
4  Carnegie Mellon University, and has been placed in the public domain.
5
6 */
7
8 #include <stdio.h>
9
10 #include "sbcl.h"
11 #include "runtime.h"
12 #include "arch.h"
13 #include "globals.h"
14 #include "validate.h"
15 #include "os.h"
16 #include "lispregs.h"
17 #include "signal.h"
18 #include "alloc.h"
19 #include "interrupt.h"
20 #include "interr.h"
21 #include "breakpoint.h"
22
23 #include "genesis/constants.h"
24
25 #define INSN_LEN sizeof(unsigned int)
26
27 void
28 arch_init()
29 {
30     return;
31 }
32
33 os_vm_address_t
34 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
35 {
36     /* Classic CMUCL comment:
37
38        Finding the bad address on the mips is easy. */
39     return (os_vm_address_t)siginfo->si_addr;
40 }
41
42 static inline unsigned int
43 os_context_register(os_context_t *context, int offset)
44 {
45     return (unsigned int)(*os_context_register_addr(context, offset));
46 }
47
48 static inline unsigned int
49 os_context_pc(os_context_t *context)
50 {
51     return (unsigned int)(*os_context_pc_addr(context));
52 }
53
54 static inline unsigned int
55 os_context_insn(os_context_t *context)
56 {
57     if (os_context_bd_cause(context))
58         return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
59     else
60         return *(unsigned int *)(os_context_pc(context));
61 }
62
63 boolean
64 arch_insn_with_bdelay_p(unsigned int insn)
65 {
66     switch (insn >> 26) {
67     case 0x0:
68         switch (insn & 0x3f) {
69         /* register jumps */
70         case 0x08:
71         case 0x09:
72             return 1;
73         }
74         break;
75     /* branches and immediate jumps */
76     case 0x1:
77         switch ((insn >> 16) & 0x1f) {
78         case 0x00:
79         case 0x01:
80         case 0x02:
81         case 0x03:
82         case 0x10:
83         case 0x11:
84         case 0x12:
85         case 0x13:
86             return 1;
87         }
88         break;
89     case 0x2:
90     case 0x3:
91     case 0x4:
92     case 0x5:
93     case 0x6:
94     case 0x7:
95         return 1;
96     case 0x10:
97     case 0x11:
98     case 0x12:
99         switch ((insn >> 21) & 0x1f) {
100          /* CP0/CP1/CP2 branches */
101         case 0x08:
102             return 1;
103         }
104         break;
105     /* branch likely (MIPS II) */
106     case 0x14:
107     case 0x15:
108     case 0x16:
109     case 0x17:
110         return 1;
111     }
112     return 0;
113 }
114
115 /* Find the next instruction in the control flow. For a instruction
116    with branch delay slot, this is the branch/jump target if the branch
117    is taken, and PC + 8 if it is not taken. For other instructions it
118    is PC + 4. */
119 static unsigned int
120 next_insn_addr(os_context_t *context, unsigned int inst)
121 {
122     unsigned int opcode = inst >> 26;
123     unsigned int r1 = (inst >> 21) & 0x1f;
124     unsigned int r2 = (inst >> 16) & 0x1f;
125     unsigned int r3 = (inst >> 11) & 0x1f;
126     unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
127     unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
128     unsigned int tgt = os_context_pc(context);
129
130     switch(opcode) {
131     case 0x0: /* jr, jalr */
132         switch(inst & 0x3f) {
133         case 0x08: /* jr */
134             tgt = os_context_register(context, r1);
135             break;
136         case 0x09: /* jalr */
137             tgt = os_context_register(context, r1);
138             *os_context_register_addr(context, r3)
139                 = os_context_pc(context) + INSN_LEN;
140             break;
141         default:
142             tgt += INSN_LEN;
143             break;
144         }
145         break;
146     case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
147         switch(r2) {
148         case 0x00: /* bltz */
149         case 0x02: /* bltzl */
150             if(os_context_register(context, r1) < 0)
151                 tgt += disp;
152             else
153                 tgt += INSN_LEN;
154             break;
155         case 0x01: /* bgez */
156         case 0x03: /* bgezl */
157             if(os_context_register(context, r1) >= 0)
158                 tgt += disp;
159             else
160                 tgt += INSN_LEN;
161             break;
162         case 0x10: /* bltzal */
163         case 0x12: /* bltzall */
164             if(os_context_register(context, r1) < 0) {
165                 tgt += disp;
166                 *os_context_register_addr(context, 31)
167                     = os_context_pc(context) + INSN_LEN;
168             } else
169                 tgt += INSN_LEN;
170             break;
171         case 0x11: /* bgezal */
172         case 0x13: /* bgezall */
173             if(os_context_register(context, r1) >= 0) {
174                 tgt += disp;
175                 *os_context_register_addr(context, 31)
176                     = os_context_pc(context) + INSN_LEN;
177             } else
178                 tgt += INSN_LEN;
179             break;
180         default:
181             tgt += INSN_LEN;
182             break;
183         }
184         break;
185     case 0x2: /* j */
186         tgt = jtgt;
187         break;
188     case 0x3: /* jal */
189         tgt = jtgt;
190         *os_context_register_addr(context, 31)
191             = os_context_pc(context) + INSN_LEN;
192         break;
193     case 0x4: /* beq */
194     case 0x14: /* beql */
195         if(os_context_register(context, r1)
196            == os_context_register(context, r2))
197             tgt += disp;
198         else
199             tgt += INSN_LEN;
200         break;
201     case 0x5: /* bne */
202     case 0x15: /* bnel */
203         if(os_context_register(context, r1)
204            != os_context_register(context, r2))
205             tgt += disp;
206         else
207             tgt += INSN_LEN;
208         break;
209     case 0x6: /* blez */
210     case 0x16: /* blezl */
211         if(os_context_register(context, r1)
212            <= os_context_register(context, r2))
213             tgt += disp;
214         else
215             tgt += INSN_LEN;
216         break;
217     case 0x7: /* bgtz */
218     case 0x17: /* bgtzl */
219         if(os_context_register(context, r1)
220            > os_context_register(context, r2))
221             tgt += disp;
222         else
223             tgt += INSN_LEN;
224         break;
225     case 0x10:
226     case 0x11:
227     case 0x12:
228         switch (r1) {
229          /* CP0/CP1/CP2 branches */
230         case 0x08:
231             /* FIXME */
232             tgt += INSN_LEN;
233             break;
234         }
235         break;
236     default:
237         tgt += INSN_LEN;
238         break;
239     }
240     return tgt;
241 }
242
243 void
244 arch_skip_instruction(os_context_t *context)
245 {
246     /* Skip the offending instruction. Don't use os_context_insn here,
247        since in case of a branch we want the branch insn, not the delay
248        slot. */
249     *os_context_pc_addr(context)
250         = (os_context_register_t)
251             next_insn_addr(context,
252                 *(unsigned int *)(os_context_pc(context)));
253 }
254
255 unsigned char *
256 arch_internal_error_arguments(os_context_t *context)
257 {
258     if (os_context_bd_cause(context))
259         return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
260     else
261         return (unsigned char *)(os_context_pc(context) + INSN_LEN);
262 }
263
264 boolean
265 arch_pseudo_atomic_atomic(os_context_t *context)
266 {
267     return os_context_register(context, reg_ALLOC) & 1;
268 }
269
270 void
271 arch_set_pseudo_atomic_interrupted(os_context_t *context)
272 {
273     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
274 }
275
276 void
277 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
278 {
279     *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
280 }
281
282 unsigned int
283 arch_install_breakpoint(void *pc)
284 {
285     unsigned int *ptr = (unsigned int *)pc;
286     unsigned int insn;
287
288     /* Don't install over a branch/jump with delay slot. */
289     if (arch_insn_with_bdelay_p(*ptr))
290         ptr++;
291
292     insn = *ptr;
293     *ptr = (trap_Breakpoint << 6) | 0xd;
294     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
295
296     return insn;
297 }
298
299 static inline unsigned int
300 arch_install_after_breakpoint(void *pc)
301 {
302     unsigned int *ptr = (unsigned int *)pc;
303     unsigned int insn;
304
305     /* Don't install over a branch/jump with delay slot. */
306     if (arch_insn_with_bdelay_p(*ptr))
307         ptr++;
308
309     insn = *ptr;
310     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
311     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
312
313     return insn;
314 }
315
316 void
317 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
318 {
319     unsigned int *ptr = (unsigned int *)pc;
320
321     /* We may remove from a branch delay slot. */
322     if (arch_insn_with_bdelay_p(*ptr))
323         ptr++;
324
325     *ptr = orig_inst;
326     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
327 }
328
329 /* Perform the instruction that we overwrote with a breakpoint. As we
330    don't have a single-step facility, this means we have to:
331    - put the instruction back
332    - put a second breakpoint at the following instruction,
333      set after_breakpoint and continue execution.
334
335    When the second breakpoint is hit (very shortly thereafter, we hope)
336    sigtrap_handler gets called again, but follows the AfterBreakpoint
337    arm, which
338    - puts a bpt back in the first breakpoint place (running across a
339      breakpoint shouldn't cause it to be uninstalled)
340    - replaces the second bpt with the instruction it was meant to be
341    - carries on
342
343    Clear? */
344
345 static unsigned int *skipped_break_addr, displaced_after_inst;
346 static sigset_t orig_sigmask;
347
348 void
349 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
350 {
351     unsigned int *pc = (unsigned int *)os_context_pc(context);
352     unsigned int *next_pc;
353
354     orig_sigmask = *os_context_sigmask_addr(context);
355     sigaddset_blockable(os_context_sigmask_addr(context));
356
357     /* Put the original instruction back. */
358     arch_remove_breakpoint(pc, orig_inst);
359     skipped_break_addr = pc;
360
361     /* Figure out where it goes. */
362     next_pc = (unsigned int *)next_insn_addr(context, *pc);
363     displaced_after_inst = arch_install_after_breakpoint(next_pc);
364 }
365
366 void
367 arch_handle_breakpoint(os_context_t *context)
368 {
369     handle_breakpoint(context);
370 }
371
372 void
373 arch_handle_fun_end_breakpoint(os_context_t *context)
374 {
375     *os_context_pc_addr(context)
376         = (os_context_register_t)(unsigned int)
377         handle_fun_end_breakpoint(context);
378 }
379
380 void
381 arch_handle_after_breakpoint(os_context_t *context)
382 {
383     arch_install_breakpoint(skipped_break_addr);
384     arch_remove_breakpoint((unsigned int *)os_context_pc(context),
385                            displaced_after_inst);
386     *os_context_sigmask_addr(context) = orig_sigmask;
387 }
388
389 static void
390 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
391 {
392     os_context_t *context = arch_os_get_context(&void_context);
393     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
394     /* FIXME: WTF is this magic number? Needs to become a #define
395      * and go into maybe_handle_trap. */
396     if (code==0x10) {
397         arch_clear_pseudo_atomic_interrupted(context);
398         arch_skip_instruction(context);
399         interrupt_handle_pending(context);
400     } else if (!maybe_handle_trap(context,code))
401         interrupt_handle_now(signal, info, void_context);
402 }
403
404 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
405
406 static void
407 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
408 {
409     os_context_t *context = arch_os_get_context(&void_context);
410     unsigned int bad_inst = os_context_insn(context);
411     unsigned int op, rs, rt, rd, funct, dest = 32;
412     int immed;
413     int result;
414
415     op = (bad_inst >> 26) & 0x3f;
416     rs = (bad_inst >> 21) & 0x1f;
417     rt = (bad_inst >> 16) & 0x1f;
418     rd = (bad_inst >> 11) & 0x1f;
419     funct = bad_inst & 0x3f;
420     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
421
422     switch (op) {
423     case 0x0: /* SPECIAL */
424         switch (funct) {
425         case 0x20: /* ADD */
426             result = FIXNUM_VALUE(os_context_register(context, rs))
427                 + FIXNUM_VALUE(os_context_register(context, rt));
428             dest = rd;
429             break;
430
431         case 0x22: /* SUB */
432             result = FIXNUM_VALUE(os_context_register(context, rs))
433                 - FIXNUM_VALUE(os_context_register(context, rt));
434             dest = rd;
435             break;
436
437         default:
438             interrupt_handle_now(signal, info, context);
439             return;
440         }
441         break;
442
443     case 0x8: /* ADDI */
444         result = FIXNUM_VALUE(os_context_register(context,rs))
445                     + (immed >> N_FIXNUM_TAG_BITS);
446         dest = rt;
447         break;
448
449     default:
450         interrupt_handle_now(signal, info, context);
451         return;
452     }
453
454     dynamic_space_free_pointer =
455         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
456
457     *os_context_register_addr(context,dest) = alloc_number(result);
458
459     *os_context_register_addr(context, reg_ALLOC) =
460         (unsigned int) dynamic_space_free_pointer;
461
462     arch_skip_instruction(context);
463 }
464
465 void
466 arch_install_interrupt_handlers()
467 {
468     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
469     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
470 }
471
472 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
473
474 lispobj
475 funcall0(lispobj function)
476 {
477     lispobj *args = current_control_stack_pointer;
478
479     return call_into_lisp(function, args, 0);
480 }
481
482 lispobj
483 funcall1(lispobj function, lispobj arg0)
484 {
485     lispobj *args = current_control_stack_pointer;
486
487     current_control_stack_pointer += 1;
488     args[0] = arg0;
489
490     return call_into_lisp(function, args, 1);
491 }
492
493 lispobj
494 funcall2(lispobj function, lispobj arg0, lispobj arg1)
495 {
496     lispobj *args = current_control_stack_pointer;
497
498     current_control_stack_pointer += 2;
499     args[0] = arg0;
500     args[1] = arg1;
501
502     return call_into_lisp(function, args, 2);
503 }
504
505 lispobj
506 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
507 {
508     lispobj *args = current_control_stack_pointer;
509
510     current_control_stack_pointer += 3;
511     args[0] = arg0;
512     args[1] = arg1;
513     args[2] = arg2;
514
515     return call_into_lisp(function, args, 3);
516 }
517
518 #ifdef LISP_FEATURE_LINKAGE_TABLE
519
520 /* Linkage tables for MIPS
521
522    Linkage entry size is 16, because we need 4 instructions to implement
523    a jump. The entry size constant is defined in parms.lisp.
524
525    Define the register to use in the linkage jump table. For MIPS this
526    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
527 #define LINKAGE_TEMP_REG        reg_ALLOC
528
529 /* Insert the necessary jump instructions at the given address. */
530 void
531 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
532 {
533   /* Make JMP to function entry. The instruction sequence is:
534        lui    $25, 0, %hi(addr)
535        addiu  $25, $25, %lo(addr)
536        jr     $25
537         nop */
538   unsigned int *insn = (unsigned int *)reloc_addr;
539   unsigned int addr = (unsigned int)target_addr;
540   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
541   unsigned int lo = addr & 0xffff;
542
543   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
544   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
545                  | (LINKAGE_TEMP_REG << 16) | lo);
546   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
547   *insn = 0;
548
549   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
550 }
551
552 void
553 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
554 {
555     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
556 }
557
558 #endif