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.
19 #include "interrupt.h"
21 #include "breakpoint.h"
23 #include "genesis/constants.h"
25 #define INSN_LEN sizeof(unsigned int)
34 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
36 /* Classic CMUCL comment:
38 Finding the bad address on the mips is easy. */
39 return (os_vm_address_t)siginfo->si_addr;
42 static inline unsigned int
43 os_context_register(os_context_t *context, int offset)
45 return (unsigned int)(*os_context_register_addr(context, offset));
48 static inline unsigned int
49 os_context_pc(os_context_t *context)
51 return (unsigned int)(*os_context_pc_addr(context));
54 static inline unsigned int
55 os_context_insn(os_context_t *context)
57 if (os_context_bd_cause(context))
58 return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
60 return *(unsigned int *)(os_context_pc(context));
64 arch_insn_with_bdelay_p(unsigned int insn)
68 switch (insn & 0x3f) {
75 /* branches and immediate jumps */
77 switch ((insn >> 16) & 0x1f) {
99 switch ((insn >> 21) & 0x1f) {
100 /* CP0/CP1/CP2 branches */
105 /* branch likely (MIPS II) */
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
120 next_insn_addr(os_context_t *context, unsigned int inst)
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);
131 case 0x0: /* jr, jalr */
132 switch(inst & 0x3f) {
134 tgt = os_context_register(context, r1);
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;
146 case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
148 case 0x00: /* bltz */
149 case 0x02: /* bltzl */
150 if(os_context_register(context, r1) < 0)
155 case 0x01: /* bgez */
156 case 0x03: /* bgezl */
157 if(os_context_register(context, r1) >= 0)
162 case 0x10: /* bltzal */
163 case 0x12: /* bltzall */
164 if(os_context_register(context, r1) < 0) {
166 *os_context_register_addr(context, 31)
167 = os_context_pc(context) + INSN_LEN;
171 case 0x11: /* bgezal */
172 case 0x13: /* bgezall */
173 if(os_context_register(context, r1) >= 0) {
175 *os_context_register_addr(context, 31)
176 = os_context_pc(context) + INSN_LEN;
190 *os_context_register_addr(context, 31)
191 = os_context_pc(context) + INSN_LEN;
194 case 0x14: /* beql */
195 if(os_context_register(context, r1)
196 == os_context_register(context, r2))
202 case 0x15: /* bnel */
203 if(os_context_register(context, r1)
204 != os_context_register(context, r2))
210 case 0x16: /* blezl */
211 if(os_context_register(context, r1)
212 <= os_context_register(context, r2))
218 case 0x17: /* bgtzl */
219 if(os_context_register(context, r1)
220 > os_context_register(context, r2))
229 /* CP0/CP1/CP2 branches */
244 arch_skip_instruction(os_context_t *context)
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
249 *os_context_pc_addr(context)
250 = (os_context_register_t)
251 next_insn_addr(context,
252 *(unsigned int *)(os_context_pc(context)));
256 arch_internal_error_arguments(os_context_t *context)
258 if (os_context_bd_cause(context))
259 return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
261 return (unsigned char *)(os_context_pc(context) + INSN_LEN);
265 arch_pseudo_atomic_atomic(os_context_t *context)
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
274 * The foreign_function_call_active used to live at each call-site
275 * to arch_pseudo_atomic_atomic, but this seems clearer.
277 return (!foreign_function_call_active)
278 && os_context_register(context, reg_ALLOC) & 1;
282 arch_set_pseudo_atomic_interrupted(os_context_t *context)
284 *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
288 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
290 *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
294 arch_install_breakpoint(void *pc)
296 unsigned int *ptr = (unsigned int *)pc;
299 /* Don't install over a branch/jump with delay slot. */
300 if (arch_insn_with_bdelay_p(*ptr))
304 *ptr = (trap_Breakpoint << 6) | 0xd;
305 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
310 static inline unsigned int
311 arch_install_after_breakpoint(void *pc)
313 unsigned int *ptr = (unsigned int *)pc;
316 /* Don't install over a branch/jump with delay slot. */
317 if (arch_insn_with_bdelay_p(*ptr))
321 *ptr = (trap_AfterBreakpoint << 6) | 0xd;
322 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
328 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
330 unsigned int *ptr = (unsigned int *)pc;
332 /* We may remove from a branch delay slot. */
333 if (arch_insn_with_bdelay_p(*ptr))
337 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
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.
346 When the second breakpoint is hit (very shortly thereafter, we hope)
347 sigtrap_handler gets called again, but follows the AfterBreakpoint
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
356 static unsigned int *skipped_break_addr, displaced_after_inst;
357 static sigset_t orig_sigmask;
360 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
362 unsigned int *pc = (unsigned int *)os_context_pc(context);
363 unsigned int *next_pc;
365 orig_sigmask = *os_context_sigmask_addr(context);
366 sigaddset_blockable(os_context_sigmask_addr(context));
368 /* Put the original instruction back. */
369 arch_remove_breakpoint(pc, orig_inst);
370 skipped_break_addr = pc;
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);
378 arch_handle_breakpoint(os_context_t *context)
380 handle_breakpoint(context);
384 arch_handle_fun_end_breakpoint(os_context_t *context)
386 *os_context_pc_addr(context)
387 = (os_context_register_t)(unsigned int)
388 handle_fun_end_breakpoint(context);
392 arch_handle_after_breakpoint(os_context_t *context)
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;
401 arch_handle_single_step_trap(os_context_t *context, int trap)
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);
410 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
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. */
419 arch_clear_pseudo_atomic_interrupted(context);
420 arch_skip_instruction(context);
421 interrupt_handle_pending(context);
423 handle_trap(context,code & 0x1f);
426 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
429 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
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;
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;
445 case 0x0: /* SPECIAL */
448 result = FIXNUM_VALUE(os_context_register(context, rs))
449 + FIXNUM_VALUE(os_context_register(context, rt));
454 result = FIXNUM_VALUE(os_context_register(context, rs))
455 - FIXNUM_VALUE(os_context_register(context, rt));
460 interrupt_handle_now(signal, info, context);
466 result = FIXNUM_VALUE(os_context_register(context,rs))
467 + (immed >> N_FIXNUM_TAG_BITS);
472 interrupt_handle_now(signal, info, context);
476 dynamic_space_free_pointer =
477 (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
479 *os_context_register_addr(context,dest) = alloc_number(result);
481 *os_context_register_addr(context, reg_ALLOC) =
482 (unsigned int) dynamic_space_free_pointer;
484 arch_skip_instruction(context);
488 arch_install_interrupt_handlers(void)
490 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
491 undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
494 #ifdef LISP_FEATURE_LINKAGE_TABLE
496 /* Linkage tables for MIPS
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.
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
505 /* Insert the necessary jump instructions at the given address. */
507 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
509 /* Make JMP to function entry. The instruction sequence is:
510 lui $25, 0, %hi(addr)
511 addiu $25, $25, %lo(addr)
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;
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;
525 os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
529 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
531 *(unsigned int *)reloc_addr = (unsigned int)target_addr;