2 * This software is part of the SBCL system. See the README file for
5 * This software is derived from the CMU CL system, which was
6 * written at Carnegie Mellon University and released into the
7 * public domain. The software is in the public domain and is
8 * provided with absolutely no warranty. See the COPYING and CREDITS
9 * files for more information.
21 #include "interrupt.h"
23 #include "breakpoint.h"
25 #if defined(LISP_FEATURE_GENCGC)
26 #include "gencgc-alloc-region.h"
29 /* The header files may not define PT_DAR/PT_DSISR. This definition
30 is correct for all versions of ppc linux >= 2.0.30
32 As of DR2.1u4, MkLinux doesn't pass these registers to signal
33 handlers correctly; a patch is necessary in order to (partially)
36 Even with the patch, the DSISR may not have its 'write' bit set
37 correctly (it tends not to be set if the fault was caused by
38 something other than a protection violation.)
42 #if defined (LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_LINUX)
56 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
60 #if defined(LISP_FEATURE_NETBSD)
61 addr = (os_vm_address_t) (code->si_addr);
63 addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
70 arch_skip_instruction(os_context_t *context)
73 pcptr = (char**) os_context_pc_addr(context);
78 arch_internal_error_arguments(os_context_t *context)
80 return (unsigned char *)(*os_context_pc_addr(context)+4);
85 arch_pseudo_atomic_atomic(os_context_t *context)
87 return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
91 arch_set_pseudo_atomic_interrupted(os_context_t *context)
93 *os_context_register_addr(context,reg_ALLOC) |= 1;
97 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
99 *os_context_register_addr(context,reg_ALLOC) &= ~1;
103 arch_install_breakpoint(void *pc)
105 unsigned int *ptr = (unsigned int *)pc;
106 unsigned int result = *ptr;
107 *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
108 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
113 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
115 *(unsigned int *)pc = orig_inst;
116 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
120 * Perform the instruction that we overwrote with a breakpoint. As we
121 * don't have a single-step facility, this means we have to:
122 * - put the instruction back
123 * - put a second breakpoint at the following instruction,
124 * set after_breakpoint and continue execution.
126 * When the second breakpoint is hit (very shortly thereafter, we hope)
127 * sigtrap_handler gets called again, but follows the AfterBreakpoint
129 * - puts a bpt back in the first breakpoint place (running across a
130 * breakpoint shouldn't cause it to be uninstalled)
131 * - replaces the second bpt with the instruction it was meant to be
136 static unsigned int *skipped_break_addr, displaced_after_inst;
137 static sigset_t orig_sigmask;
140 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
142 /* not sure how we ensure that we get the breakpoint reinstalled
143 * after doing this -dan */
144 unsigned int *pc = (unsigned int *)(*os_context_pc_addr(context));
146 orig_sigmask = *os_context_sigmask_addr(context);
147 sigaddset_blockable(os_context_sigmask_addr(context));
150 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
151 skipped_break_addr = pc;
153 /* FIXME: we should apparently be installing the after-breakpoint
154 * here, but would need to find the next instruction address for
155 * it first. alpha-arch.c shows how to do it. --NS 2007-04-02 */
158 #ifdef LISP_FEATURE_GENCGC
160 * Return non-zero if the current instruction is an allocation trap
163 allocation_trap_p(os_context_t * context)
175 * First, the instruction has to be a TWLGE temp, NL3, which has the
177 * | 6| 5| 5 | 5 | 10|1| width
178 * |31|5 |dst|src| 4|0| field
180 pc = (unsigned int *) (*os_context_pc_addr(context));
184 fprintf(stderr, "allocation_trap_p at %p: inst = 0x%08x\n", pc, inst);
188 src = (inst >> 11) & 0x1f;
189 dst = (inst >> 16) & 0x1f;
190 if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
191 && (4 == ((inst >> 1) & 0x3ff))) {
193 * We got the instruction. Now, look back to make sure it was
194 * proceeded by what we expected. 2 instructions back should be
195 * an ADD or ADDI instruction.
197 unsigned int add_inst;
201 fprintf(stderr, " add inst at %p: inst = 0x%08x\n",
204 opcode = add_inst >> 26;
205 if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
207 } else if ((opcode == 14)) {
211 "Whoa! Got allocation trap but could not find ADD or ADDI instruction: 0x%08x in the proper place\n",
218 extern struct alloc_region boxed_region;
221 handle_allocation_trap(os_context_t * context)
225 unsigned int target, target_ptr, end_addr;
228 boolean were_in_lisp;
235 fprintf(stderr, "In handle_allocation_trap\n");
238 /* I don't think it's possible for us NOT to be in lisp when we get
239 * here. Remove this later? */
240 were_in_lisp = !foreign_function_call_active;
243 fake_foreign_function_call(context);
245 fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
249 * Look at current instruction: TWNE temp, NL3. We're here because
250 * temp > NL3 and temp is the end of the allocation, and NL3 is
251 * current-region-end-addr.
253 * We need to adjust temp and alloc-tn.
256 pc = (unsigned int *) (*os_context_pc_addr(context));
258 end_addr = (inst >> 11) & 0x1f;
259 target = (inst >> 16) & 0x1f;
261 target_ptr = *os_context_register_addr(context, target);
264 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
265 fprintf(stderr, "boxed_region.free_pointer: %p\n", boxed_region.free_pointer);
266 fprintf(stderr, "boxed_region.end_addr: %p\n", boxed_region.end_addr);
267 fprintf(stderr, "target reg: %d, end_addr reg: %d\n", target, end_addr);
268 fprintf(stderr, "target: %x\n", *os_context_register_addr(context, target));
269 fprintf(stderr, "end_addr: %x\n", *os_context_register_addr(context, end_addr));
273 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
274 fprintf(stderr, " trap inst = 0x%08x\n", inst);
275 fprintf(stderr, " target reg = %s\n", lisp_register_names[target]);
279 * Go back and look at the add/addi instruction. The second src arg
280 * is the size of the allocation. Get it and call alloc to allocate
286 fprintf(stderr, " add inst = 0x%08x, opcode = %d\n", inst, opcode);
290 * ADDI temp-tn, alloc-tn, size
294 size = (inst & 0xffff);
295 } else if (opcode == 31) {
297 * ADD temp-tn, alloc-tn, size-tn
303 reg = (inst >> 11) & 0x1f;
305 fprintf(stderr, " add, reg = %s\n", lisp_register_names[reg]);
307 size = *os_context_register_addr(context, reg);
312 fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
315 #if INLINE_ALLOC_DEBUG
316 if ((((unsigned long)boxed_region.end_addr + size) / PAGE_SIZE) ==
317 (((unsigned long)boxed_region.end_addr) / PAGE_SIZE)) {
318 fprintf(stderr,"*** possibly bogus trap allocation of %d bytes at %p\n",
320 fprintf(stderr, " dynamic_space_free_pointer: %p, boxed_region.end_addr %p\n",
321 dynamic_space_free_pointer, boxed_region.end_addr);
326 fprintf(stderr, "Ready to alloc\n");
327 fprintf(stderr, "free_pointer = 0x%08x\n",
328 dynamic_space_free_pointer);
332 * alloc-tn was incremented by size. Need to decrement it by size
333 * to restore its original value. This is not true on GENCGC
334 * anymore. d_s_f_p and reg_alloc get out of sync, but the p_a
335 * bits stay intact and we set it to the proper value when it
336 * needs to be. Keep this comment here for the moment in case
337 * somebody tries to figure out what happened here.
339 /* dynamic_space_free_pointer =
340 (lispobj *) ((long) dynamic_space_free_pointer - size);
343 fprintf(stderr, "free_pointer = 0x%08x new\n",
344 dynamic_space_free_pointer);
347 memory = (char *) alloc(size);
350 fprintf(stderr, "alloc returned %p\n", memory);
351 fprintf(stderr, "free_pointer = 0x%08x\n",
352 dynamic_space_free_pointer);
356 * The allocation macro wants the result to point to the end of the
362 fprintf(stderr, "object end at %p\n", memory);
365 *os_context_register_addr(context, target) = (unsigned long) memory;
366 *os_context_register_addr(context, reg_ALLOC) =
367 (unsigned long) dynamic_space_free_pointer
368 | (*os_context_register_addr(context, reg_ALLOC)
372 undo_fake_foreign_function_call(context);
380 arch_handle_breakpoint(os_context_t *context)
382 handle_breakpoint(context);
386 arch_handle_fun_end_breakpoint(os_context_t *context)
388 *os_context_pc_addr(context)
389 =(int)handle_fun_end_breakpoint(context);
393 arch_handle_after_breakpoint(os_context_t *context)
395 *skipped_break_addr = trap_Breakpoint;
396 skipped_break_addr = NULL;
397 *(unsigned int *)*os_context_pc_addr(context)
398 = displaced_after_inst;
399 *os_context_sigmask_addr(context)= orig_sigmask;
400 os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
401 sizeof(unsigned int));
405 arch_handle_single_step_trap(os_context_t *context, int trap)
407 unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
408 int register_offset = code >> 5 & 0x1f;
409 handle_single_step_trap(context, trap, register_offset);
410 arch_skip_instruction(context);
414 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
418 #ifdef LISP_FEATURE_LINUX
419 os_restore_fp_control(context);
421 code=*((u32 *)(*os_context_pc_addr(context)));
422 if (code == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
423 arch_clear_pseudo_atomic_interrupted(context);
424 arch_skip_instruction(context);
425 /* interrupt or GC was requested in PA; now we're done with the
426 PA section we may as well get around to it */
427 interrupt_handle_pending(context);
431 #ifdef LISP_FEATURE_GENCGC
432 /* Is this an allocation trap? */
433 if (allocation_trap_p(context)) {
434 handle_allocation_trap(context);
435 arch_skip_instruction(context);
436 #ifdef LISP_FEATURE_DARWIN
437 DARWIN_FIX_CONTEXT(context);
443 if ((code >> 16) == ((3 << 10) | (6 << 5))) {
444 /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
445 int trap = code & 0x1f;
446 handle_trap(context,trap);
448 #ifdef LISP_FEATURE_DARWIN
449 DARWIN_FIX_CONTEXT(context);
453 if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
454 interrupt_internal_error(context, 0);
455 #ifdef LISP_FEATURE_DARWIN
456 DARWIN_FIX_CONTEXT(context);
461 interrupt_handle_now(signal, code, context);
462 #ifdef LISP_FEATURE_DARWIN
463 /* Work around G5 bug */
464 DARWIN_FIX_CONTEXT(context);
469 void arch_install_interrupt_handlers()
471 undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
472 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
476 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
478 os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
479 extern void ppc_flush_cache_line(os_vm_address_t);
481 while (address < end) {
482 ppc_flush_cache_line(address);
487 #ifdef LISP_FEATURE_LINKAGE_TABLE
489 /* Linkage tables for PowerPC
491 * Linkage entry size is 16, because we need at least 4 instructions to
496 * Define the registers to use in the linkage jump table. Can be the
497 * same. Some care must be exercised when choosing these. It has to be
498 * a register that is not otherwise being used. reg_NFP is a good
499 * choice. call_into_c trashes reg_NFP without preserving it, so we can
500 * trash it in the linkage jump table.
502 #define LINKAGE_TEMP_REG reg_NFP
503 #define LINKAGE_ADDR_REG reg_NFP
506 * Insert the necessary jump instructions at the given address.
509 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
512 * Make JMP to function entry.
514 * The instruction sequence is:
516 * addis 13, 0, (hi part of addr)
517 * ori 13, 13, (low part of addr)
523 unsigned long hi; /* Top 16 bits of address */
524 unsigned long lo; /* Low 16 bits of address */
527 inst_ptr = (int*) reloc_addr;
530 * Split the target address into hi and lo parts for the sethi
531 * instruction. hi is the top 22 bits. lo is the low 10 bits.
533 hi = (unsigned long) target_addr;
538 * addis 13, 0, (hi part)
541 inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
545 * ori 13, 13, (lo part)
548 inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
555 inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
562 inst = (19 << 26) | (20 << 21) | (528 << 1);
568 os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
572 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
574 *(unsigned long *)reloc_addr = (unsigned long)target_addr;