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"
24 #include "genesis/constants.h"
26 #define INSN_LEN sizeof(unsigned int)
35 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
37 /* Classic CMUCL comment:
39 Finding the bad address on the mips is easy. */
40 return (os_vm_address_t)siginfo->si_addr;
43 static inline unsigned int
44 os_context_register(os_context_t *context, int offset)
46 return (unsigned int)(*os_context_register_addr(context, offset));
49 static inline unsigned int
50 os_context_pc(os_context_t *context)
52 return (unsigned int)(*os_context_pc_addr(context));
55 static inline unsigned int
56 os_context_insn(os_context_t *context)
58 if (os_context_bd_cause(context))
59 return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
61 return *(unsigned int *)(os_context_pc(context));
65 arch_insn_with_bdelay_p(unsigned int insn)
69 switch (insn & 0x3f) {
76 /* branches and immediate jumps */
78 switch ((insn >> 16) & 0x1f) {
100 switch ((insn >> 21) & 0x1f) {
101 /* CP0/CP1/CP2 branches */
106 /* branch likely (MIPS II) */
116 /* Find the next instruction in the control flow. For a instruction
117 with branch delay slot, this is the branch/jump target if the branch
118 is taken, and PC + 8 if it is not taken. For other instructions it
121 next_insn_addr(os_context_t *context, unsigned int inst)
123 unsigned int opcode = inst >> 26;
124 unsigned int r1 = (inst >> 21) & 0x1f;
125 unsigned int r2 = (inst >> 16) & 0x1f;
126 unsigned int r3 = (inst >> 11) & 0x1f;
127 unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
128 unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
129 unsigned int tgt = os_context_pc(context);
132 case 0x0: /* jr, jalr */
133 switch(inst & 0x3f) {
135 tgt = os_context_register(context, r1);
137 case 0x09: /* jalr */
138 tgt = os_context_register(context, r1);
139 *os_context_register_addr(context, r3)
140 = os_context_pc(context) + INSN_LEN;
147 case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
149 case 0x00: /* bltz */
150 case 0x02: /* bltzl */
151 if(os_context_register(context, r1) < 0)
156 case 0x01: /* bgez */
157 case 0x03: /* bgezl */
158 if(os_context_register(context, r1) >= 0)
163 case 0x10: /* bltzal */
164 case 0x12: /* bltzall */
165 if(os_context_register(context, r1) < 0) {
167 *os_context_register_addr(context, 31)
168 = os_context_pc(context) + INSN_LEN;
172 case 0x11: /* bgezal */
173 case 0x13: /* bgezall */
174 if(os_context_register(context, r1) >= 0) {
176 *os_context_register_addr(context, 31)
177 = os_context_pc(context) + INSN_LEN;
191 *os_context_register_addr(context, 31)
192 = os_context_pc(context) + INSN_LEN;
195 case 0x14: /* beql */
196 if(os_context_register(context, r1)
197 == os_context_register(context, r2))
203 case 0x15: /* bnel */
204 if(os_context_register(context, r1)
205 != os_context_register(context, r2))
211 case 0x16: /* blezl */
212 if(os_context_register(context, r1)
213 <= os_context_register(context, r2))
219 case 0x17: /* bgtzl */
220 if(os_context_register(context, r1)
221 > os_context_register(context, r2))
230 /* CP0/CP1/CP2 branches */
245 arch_skip_instruction(os_context_t *context)
247 /* Skip the offending instruction. Don't use os_context_insn here,
248 since in case of a branch we want the branch insn, not the delay
250 *os_context_pc_addr(context)
251 = (os_context_register_t)
252 next_insn_addr(context,
253 *(unsigned int *)(os_context_pc(context)));
257 arch_internal_error_arguments(os_context_t *context)
259 if (os_context_bd_cause(context))
260 return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
262 return (unsigned char *)(os_context_pc(context) + INSN_LEN);
266 arch_pseudo_atomic_atomic(os_context_t *context)
268 return os_context_register(context, reg_ALLOC) & 1;
272 arch_set_pseudo_atomic_interrupted(os_context_t *context)
274 *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
278 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
280 *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
284 arch_install_breakpoint(void *pc)
286 unsigned int *ptr = (unsigned int *)pc;
289 /* Don't install over a branch/jump with delay slot. */
290 if (arch_insn_with_bdelay_p(*ptr))
294 *ptr = (trap_Breakpoint << 6) | 0xd;
295 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
300 static inline unsigned int
301 arch_install_after_breakpoint(void *pc)
303 unsigned int *ptr = (unsigned int *)pc;
306 /* Don't install over a branch/jump with delay slot. */
307 if (arch_insn_with_bdelay_p(*ptr))
311 *ptr = (trap_AfterBreakpoint << 6) | 0xd;
312 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
318 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
320 unsigned int *ptr = (unsigned int *)pc;
322 /* We may remove from a branch delay slot. */
323 if (arch_insn_with_bdelay_p(*ptr))
327 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
330 /* Perform the instruction that we overwrote with a breakpoint. As we
331 don't have a single-step facility, this means we have to:
332 - put the instruction back
333 - put a second breakpoint at the following instruction,
334 set after_breakpoint and continue execution.
336 When the second breakpoint is hit (very shortly thereafter, we hope)
337 sigtrap_handler gets called again, but follows the AfterBreakpoint
339 - puts a bpt back in the first breakpoint place (running across a
340 breakpoint shouldn't cause it to be uninstalled)
341 - replaces the second bpt with the instruction it was meant to be
346 static unsigned int *skipped_break_addr, displaced_after_inst;
347 static sigset_t orig_sigmask;
350 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
352 unsigned int *pc = (unsigned int *)os_context_pc(context);
353 unsigned int *next_pc;
355 orig_sigmask = *os_context_sigmask_addr(context);
356 sigaddset_blockable(os_context_sigmask_addr(context));
358 /* Put the original instruction back. */
359 arch_remove_breakpoint(pc, orig_inst);
360 skipped_break_addr = pc;
362 /* Figure out where it goes. */
363 next_pc = (unsigned int *)next_insn_addr(context, *pc);
364 displaced_after_inst = arch_install_after_breakpoint(next_pc);
368 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
370 os_context_t *context = arch_os_get_context(&void_context);
371 unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
375 fake_foreign_function_call(context);
376 lose("%%primitive halt called; the party is over.\n");
378 case trap_PendingInterrupt:
379 arch_skip_instruction(context);
380 interrupt_handle_pending(context);
385 interrupt_internal_error(signal, info, context, code == trap_Cerror);
388 case trap_Breakpoint:
389 handle_breakpoint(signal, info, context);
392 case trap_FunEndBreakpoint:
393 *os_context_pc_addr(context)
394 = (os_context_register_t)(unsigned int)
395 handle_fun_end_breakpoint(signal, info, context);
398 case trap_AfterBreakpoint:
399 arch_install_breakpoint(skipped_break_addr);
400 arch_remove_breakpoint((unsigned int *)os_context_pc(context),
401 displaced_after_inst);
402 *os_context_sigmask_addr(context) = orig_sigmask;
406 arch_clear_pseudo_atomic_interrupted(context)
407 arch_skip_instruction(context);
408 interrupt_handle_pending(context);
412 interrupt_handle_now(signal, info, context);
417 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
420 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
422 os_context_t *context = arch_os_get_context(&void_context);
423 unsigned int bad_inst = os_context_insn(context);
424 unsigned int op, rs, rt, rd, funct, dest = 32;
428 op = (bad_inst >> 26) & 0x3f;
429 rs = (bad_inst >> 21) & 0x1f;
430 rt = (bad_inst >> 16) & 0x1f;
431 rd = (bad_inst >> 11) & 0x1f;
432 funct = bad_inst & 0x3f;
433 immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
436 case 0x0: /* SPECIAL */
439 result = FIXNUM_VALUE(os_context_register(context, rs))
440 + FIXNUM_VALUE(os_context_register(context, rt));
445 result = FIXNUM_VALUE(os_context_register(context, rs))
446 - FIXNUM_VALUE(os_context_register(context, rt));
451 interrupt_handle_now(signal, info, context);
457 result = FIXNUM_VALUE(os_context_register(context,rs))
458 + (immed >> N_FIXNUM_TAG_BITS);
463 interrupt_handle_now(signal, info, context);
467 dynamic_space_free_pointer =
468 (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
470 *os_context_register_addr(context,dest) = alloc_number(result);
472 *os_context_register_addr(context, reg_ALLOC) =
473 (unsigned int) dynamic_space_free_pointer;
475 arch_skip_instruction(context);
479 arch_install_interrupt_handlers()
481 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
482 undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
485 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
488 funcall0(lispobj function)
490 lispobj *args = current_control_stack_pointer;
492 return call_into_lisp(function, args, 0);
496 funcall1(lispobj function, lispobj arg0)
498 lispobj *args = current_control_stack_pointer;
500 current_control_stack_pointer += 1;
503 return call_into_lisp(function, args, 1);
507 funcall2(lispobj function, lispobj arg0, lispobj arg1)
509 lispobj *args = current_control_stack_pointer;
511 current_control_stack_pointer += 2;
515 return call_into_lisp(function, args, 2);
519 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
521 lispobj *args = current_control_stack_pointer;
523 current_control_stack_pointer += 3;
528 return call_into_lisp(function, args, 3);
531 #ifdef LISP_FEATURE_LINKAGE_TABLE
533 /* Linkage tables for MIPS
535 Linkage entry size is 16, because we need 4 instructions to implement
536 a jump. The entry size constant is defined in parms.lisp.
538 Define the register to use in the linkage jump table. For MIPS this
539 has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
540 #define LINKAGE_TEMP_REG reg_ALLOC
542 /* Insert the necessary jump instructions at the given address. */
544 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
546 /* Make JMP to function entry. The instruction sequence is:
547 lui $25, 0, %hi(addr)
548 addiu $25, $25, %lo(addr)
551 unsigned int *insn = (unsigned int *)reloc_addr;
552 unsigned int addr = (unsigned int)target_addr;
553 unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
554 unsigned int lo = addr & 0xffff;
556 *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
557 *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
558 | (LINKAGE_TEMP_REG << 16) | lo);
559 *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
562 os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
566 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
568 *(unsigned int *)reloc_addr = (unsigned int)target_addr;