1.0.5.49: interrupt & GC & PA 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(void)
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     /* FIXME: this foreign_function_call_active test is dubious at
268      * best. If a foreign call is made in a pseudo atomic section
269      * (?) or more likely a pseudo atomic section is in a foreign
270      * call then an interrupt is executed immediately. Maybe it
271      * has to do with C code not maintaining pseudo atomic
272      * properly. MG - 2005-08-10
273      *
274      * The foreign_function_call_active used to live at each call-site
275      * to arch_pseudo_atomic_atomic, but this seems clearer.
276      * --NS 2007-05-15 */
277     return (!foreign_function_call_active)
278         && os_context_register(context, reg_ALLOC) & 1;
279 }
280
281 void
282 arch_set_pseudo_atomic_interrupted(os_context_t *context)
283 {
284     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
285 }
286
287 void
288 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
289 {
290     *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
291 }
292
293 unsigned int
294 arch_install_breakpoint(void *pc)
295 {
296     unsigned int *ptr = (unsigned int *)pc;
297     unsigned int insn;
298
299     /* Don't install over a branch/jump with delay slot. */
300     if (arch_insn_with_bdelay_p(*ptr))
301         ptr++;
302
303     insn = *ptr;
304     *ptr = (trap_Breakpoint << 6) | 0xd;
305     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
306
307     return insn;
308 }
309
310 static inline unsigned int
311 arch_install_after_breakpoint(void *pc)
312 {
313     unsigned int *ptr = (unsigned int *)pc;
314     unsigned int insn;
315
316     /* Don't install over a branch/jump with delay slot. */
317     if (arch_insn_with_bdelay_p(*ptr))
318         ptr++;
319
320     insn = *ptr;
321     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
322     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
323
324     return insn;
325 }
326
327 void
328 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
329 {
330     unsigned int *ptr = (unsigned int *)pc;
331
332     /* We may remove from a branch delay slot. */
333     if (arch_insn_with_bdelay_p(*ptr))
334         ptr++;
335
336     *ptr = orig_inst;
337     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
338 }
339
340 /* Perform the instruction that we overwrote with a breakpoint. As we
341    don't have a single-step facility, this means we have to:
342    - put the instruction back
343    - put a second breakpoint at the following instruction,
344      set after_breakpoint and continue execution.
345
346    When the second breakpoint is hit (very shortly thereafter, we hope)
347    sigtrap_handler gets called again, but follows the AfterBreakpoint
348    arm, which
349    - puts a bpt back in the first breakpoint place (running across a
350      breakpoint shouldn't cause it to be uninstalled)
351    - replaces the second bpt with the instruction it was meant to be
352    - carries on
353
354    Clear? */
355
356 static unsigned int *skipped_break_addr, displaced_after_inst;
357 static sigset_t orig_sigmask;
358
359 void
360 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
361 {
362     unsigned int *pc = (unsigned int *)os_context_pc(context);
363     unsigned int *next_pc;
364
365     orig_sigmask = *os_context_sigmask_addr(context);
366     sigaddset_blockable(os_context_sigmask_addr(context));
367
368     /* Put the original instruction back. */
369     arch_remove_breakpoint(pc, orig_inst);
370     skipped_break_addr = pc;
371
372     /* Figure out where it goes. */
373     next_pc = (unsigned int *)next_insn_addr(context, *pc);
374     displaced_after_inst = arch_install_after_breakpoint(next_pc);
375 }
376
377 void
378 arch_handle_breakpoint(os_context_t *context)
379 {
380     handle_breakpoint(context);
381 }
382
383 void
384 arch_handle_fun_end_breakpoint(os_context_t *context)
385 {
386     *os_context_pc_addr(context)
387         = (os_context_register_t)(unsigned int)
388         handle_fun_end_breakpoint(context);
389 }
390
391 void
392 arch_handle_after_breakpoint(os_context_t *context)
393 {
394     arch_install_breakpoint(skipped_break_addr);
395     arch_remove_breakpoint((unsigned int *)os_context_pc(context),
396                            displaced_after_inst);
397     *os_context_sigmask_addr(context) = orig_sigmask;
398 }
399
400 void
401 arch_handle_single_step_trap(os_context_t *context, int trap)
402 {
403     unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
404     int register_offset = code >> 11 & 0x1f;
405     handle_single_step_trap(context, trap, register_offset);
406     arch_skip_instruction(context);
407 }
408
409 static void
410 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
411 {
412     os_context_t *context = arch_os_get_context(&void_context);
413     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
414     /* FIXME: This magic number is pseudo-atomic-trap from parms.lisp.
415      * Genesis should provide the proper #define, but it specialcases
416      * pseudo-atomic-trap to work around some oddity on SPARC.
417      * Eventually this should go into handle_trap. */
418     if (code==0x10) {
419         arch_clear_pseudo_atomic_interrupted(context);
420         arch_skip_instruction(context);
421         interrupt_handle_pending(context);
422     } else
423         handle_trap(context,code & 0x1f);
424 }
425
426 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
427
428 static void
429 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
430 {
431     os_context_t *context = arch_os_get_context(&void_context);
432     unsigned int bad_inst = os_context_insn(context);
433     unsigned int op, rs, rt, rd, funct, dest = 32;
434     int immed;
435     int result;
436
437     op = (bad_inst >> 26) & 0x3f;
438     rs = (bad_inst >> 21) & 0x1f;
439     rt = (bad_inst >> 16) & 0x1f;
440     rd = (bad_inst >> 11) & 0x1f;
441     funct = bad_inst & 0x3f;
442     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
443
444     switch (op) {
445     case 0x0: /* SPECIAL */
446         switch (funct) {
447         case 0x20: /* ADD */
448             result = FIXNUM_VALUE(os_context_register(context, rs))
449                 + FIXNUM_VALUE(os_context_register(context, rt));
450             dest = rd;
451             break;
452
453         case 0x22: /* SUB */
454             result = FIXNUM_VALUE(os_context_register(context, rs))
455                 - FIXNUM_VALUE(os_context_register(context, rt));
456             dest = rd;
457             break;
458
459         default:
460             interrupt_handle_now(signal, info, context);
461             return;
462         }
463         break;
464
465     case 0x8: /* ADDI */
466         result = FIXNUM_VALUE(os_context_register(context,rs))
467                     + (immed >> N_FIXNUM_TAG_BITS);
468         dest = rt;
469         break;
470
471     default:
472         interrupt_handle_now(signal, info, context);
473         return;
474     }
475
476     dynamic_space_free_pointer =
477         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
478
479     *os_context_register_addr(context,dest) = alloc_number(result);
480
481     *os_context_register_addr(context, reg_ALLOC) =
482         (unsigned int) dynamic_space_free_pointer;
483
484     arch_skip_instruction(context);
485 }
486
487 void
488 arch_install_interrupt_handlers(void)
489 {
490     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
491     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
492 }
493
494 #ifdef LISP_FEATURE_LINKAGE_TABLE
495
496 /* Linkage tables for MIPS
497
498    Linkage entry size is 16, because we need 4 instructions to implement
499    a jump. The entry size constant is defined in parms.lisp.
500
501    Define the register to use in the linkage jump table. For MIPS this
502    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
503 #define LINKAGE_TEMP_REG        reg_ALLOC
504
505 /* Insert the necessary jump instructions at the given address. */
506 void
507 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
508 {
509   /* Make JMP to function entry. The instruction sequence is:
510        lui    $25, 0, %hi(addr)
511        addiu  $25, $25, %lo(addr)
512        jr     $25
513         nop */
514   unsigned int *insn = (unsigned int *)reloc_addr;
515   unsigned int addr = (unsigned int)target_addr;
516   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
517   unsigned int lo = addr & 0xffff;
518
519   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
520   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
521                  | (LINKAGE_TEMP_REG << 16) | lo);
522   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
523   *insn = 0;
524
525   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
526 }
527
528 void
529 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
530 {
531     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
532 }
533
534 #endif