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.
12 #include "internals.h"
18 #include "interrupt.h"
21 /* The header files may not define PT_DAR/PT_DSISR. This definition
22 is correct for all versions of ppc linux >= 2.0.30
24 As of DR2.1u4, MkLinux doesn't pass these registers to signal
25 handlers correctly; a patch is necessary in order to (partially)
28 Even with the patch, the DSISR may not have its 'write' bit set
29 correctly (it tends not to be set if the fault was caused by
30 something other than a protection violation.)
43 * A macro to generate the instruction
47 * This is what the ppc port uses to signal various traps like
48 * breakpoints and stuff.
50 #define TWLLEI_R0(code) ((3<<26) | (6 << 21) | code)
53 arch_init(fpu_mode_t mode)
58 os_vm_address_t arch_get_bad_addr(HANDLER_ARGS)
60 os_context_t *os_context = (os_context_t *) context;
63 addr = (os_vm_address_t) SC_REG(os_context, PT_DAR);
69 arch_skip_instruction(os_context_t * context)
71 /* Skip the offending instruction */
76 arch_internal_error_arguments(os_context_t * scp)
78 return (unsigned char *) (SC_PC(scp) + 4);
81 boolean arch_pseudo_atomic_atomic(os_context_t * scp)
83 return (SC_REG(scp, reg_ALLOC) & 4);
86 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
89 arch_set_pseudo_atomic_interrupted(os_context_t * scp)
92 SC_REG(scp, reg_NL3) += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
94 SC_REG(scp, reg_ALLOC) |= 1;
99 arch_install_breakpoint(void *pc)
101 unsigned long *ptr = (unsigned long *) pc;
102 unsigned long result = *ptr;
105 * Insert a twllei r0, trap_Breakpoint instruction.
107 *ptr = TWLLEI_R0(trap_Breakpoint);
108 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
114 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
116 *(unsigned long *) pc = orig_inst;
117 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
120 static unsigned long *skipped_break_addr, displaced_after_inst;
121 static sigset_t orig_sigmask;
124 arch_do_displaced_inst(os_context_t * scp, unsigned long orig_inst)
126 unsigned int *pc = (unsigned int *) SC_PC(scp);
128 orig_sigmask = scp->uc_sigmask;
129 sigemptyset(&scp->uc_sigmask);
130 FILLBLOCKSET(&scp->uc_sigmask);
132 /* Put the original instruction back */
134 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
136 skipped_break_addr = (unsigned long) pc;
139 * Replace the next instruction with a
140 * twllei r0, trap_AfterBreakpoint
142 displaced_after_inst = *++pc;
143 *pc = TWLLEI_R0(trap_AfterBreakpoint);
144 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
151 * Return non-zero if the current instruction is an allocation trap
154 allocation_trap_p(os_context_t * context)
166 * First, the instruction has to be a TWLGE temp, NL3, which as the
168 * | 6| 5| 5 | 5 | 10|1| width
169 * |31|5 |dst|src| 4|0| field
171 pc = (unsigned int *) SC_PC(context);
175 fprintf(stderr, "allocation_trap_p at %p: inst = 0x%08x\n", pc, inst);
179 src = (inst >> 11) & 0x1f;
180 dst = (inst >> 16) & 0x1f;
181 if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
182 && (4 == ((inst >> 1) & 0x3ff))) {
184 * We got the instruction. Now, look back to make sure it was
185 * proceeded by what we expected. 2 instructions back should be
186 * an ADD or ADDI instruction.
188 unsigned int add_inst;
192 fprintf(stderr, " add inst at %p: inst = 0x%08x\n",
195 opcode = add_inst >> 26;
196 if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
198 } else if ((opcode == 14)) {
202 "Whoa! Got allocation trap not preceeded by an ADD or ADDI instruction: 0x%08x\n",
210 * Use this function to enable the minimum number of signals we need
211 * when our trap handler needs to call Lisp code that might cons. For
212 * consing to work with gencgc, we need to be able to trap the SIGILL
213 * signal to perform allocation.
216 enable_some_signals(void)
221 fprintf(stderr, "Enabling some signals\n");
224 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
227 sigaddset(&sigs, SIGILL);
228 sigaddset(&sigs, SIGBUS);
229 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
232 fprintf(stderr, "Some signals enabled\n");
237 handle_allocation_trap(os_context_t * context)
241 unsigned int or_inst;
246 boolean were_in_lisp;
254 fprintf(stderr, "In handle_allocation_trap\n");
257 * I don't think it's possible for us NOT to be in lisp when we get
258 * here. Remove this later?
260 were_in_lisp = !foreign_function_call_active;
263 fake_foreign_function_call(context);
265 fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
269 * Look at current instruction: TWNE temp, NL3. We're here because
270 * temp > NL3 and temp is the end of the allocation, and NL3 is
271 * current-region-end-addr.
273 * We need to adjust temp and alloc-tn.
276 pc = (unsigned int *) SC_PC(context);
278 target = (inst >> 16) & 0x1f;
281 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
282 fprintf(stderr, " trap inst = 0x%08x\n", inst);
283 fprintf(stderr, " target reg = %s\n", lisp_register_names[target]);
286 * Go back and look at the add/addi instruction. The second src arg
287 * is the size of the allocation. Get it and call alloc to allocate
293 fprintf(stderr, " add inst = 0x%08x, opcode = %d\n", inst, opcode);
297 * ADDI temp-tn, alloc-tn, size
301 size = (inst & 0xffff);
302 } else if (opcode == 31) {
304 * ADD temp-tn, alloc-tn, size-tn
310 reg = (inst >> 11) & 0x1f;
312 fprintf(stderr, " add, reg = %s\n", lisp_register_names[reg]);
314 size = SC_REG(context, reg);
317 fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
321 * Well, maybe not. sigill_handler probably shouldn't be unblocking
322 * all signals. So, let's enable just the signals we need. Since
323 * alloc might call GC, we need to have SIGILL enabled so we can do
324 * allocation. Do we need more?
326 enable_some_signals();
329 fprintf(stderr, "Ready to alloc\n");
330 fprintf(stderr, "free_pointer = 0x%08x\n",
331 current_dynamic_space_free_pointer);
334 * alloc-tn was incremented by size. Need to decrement it by size to restore it's original value.
336 current_dynamic_space_free_pointer =
337 (lispobj *) ((long) current_dynamic_space_free_pointer - size);
339 fprintf(stderr, "free_pointer = 0x%08x new\n",
340 current_dynamic_space_free_pointer);
343 memory = (char *) alloc(size);
346 fprintf(stderr, "alloc returned %p\n", memory);
347 fprintf(stderr, "free_pointer = 0x%08x\n",
348 current_dynamic_space_free_pointer);
352 * The allocation macro wants the result to point to the end of the
357 fprintf(stderr, "object end at %p\n", memory);
359 SC_REG(context, target) = (unsigned long) memory;
360 SC_REG(context, reg_ALLOC) =
361 (unsigned long) current_dynamic_space_free_pointer;
364 undo_fake_foreign_function_call(context);
372 sigill_handler(HANDLER_ARGS)
374 os_context_t *os_context = (os_context_t *) context;
378 sigprocmask(SIG_SETMASK, &os_context->uc_sigmask, 0);
379 opcode = *((int *) SC_PC(os_context));
382 printf("SIGILL entry: opcode = 0x%08x\n", opcode);
386 if (opcode == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
387 /* Got a twnei reg_NL3,0 - check for deferred interrupt */
389 /* Clear the pseudo-atomic-interrupted bit */
390 SC_REG(os_context, reg_ALLOC) &= ~1;
392 (SC_REG(os_context, reg_ALLOC) -= PSEUDO_ATOMIC_INTERRUPTED_BIAS);
394 arch_skip_instruction(os_context);
395 interrupt_handle_pending(os_context);
397 /* Work around G5 bug; fix courtesy gbyers via chandler */
398 sigreturn(os_context);
403 /* Is this an allocation trap? */
405 if (allocation_trap_p(os_context)) {
406 handle_allocation_trap(os_context);
407 arch_skip_instruction(os_context);
409 sigreturn(os_context);
415 if ((opcode >> 16) == ((3 << 10) | (6 << 5))) {
416 /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
417 int trap = opcode & 0x1f, extra = (opcode >> 5) & 0x1f;
420 printf("SIGILL: TWLLEI, code = %d\n", trap);
426 fake_foreign_function_call(os_context);
427 lose("%%primitive halt called; the party is over.\n");
431 interrupt_internal_error(signal, code, os_context,
432 trap == trap_Cerror);
435 case trap_PendingInterrupt:
436 arch_skip_instruction(os_context);
437 interrupt_handle_pending(os_context);
440 case trap_Breakpoint:
442 printf("trap_Breakpoint\n");
445 handle_breakpoint(signal, code, os_context);
448 case trap_FunctionEndBreakpoint:
450 printf("trap_FunctionEndBreakpoint\n");
454 (int) handle_function_end_breakpoint(signal, code, os_context);
457 case trap_AfterBreakpoint:
459 fprintf(stderr, "trap_AfterBreakpoint: break_addr = %p\n",
461 fprintf(stderr, " CSP = %p\n",
462 (void *) SC_REG(os_context, reg_CSP));
463 fprintf(stderr, " CFP = %p\n",
464 (void *) SC_REG(os_context, reg_CFP));
465 fprintf(stderr, " OCFP = %p\n",
466 (void *) SC_REG(os_context, reg_OCFP));
468 /* Put our breakpoint instruction back in */
469 *skipped_break_addr = TWLLEI_R0(trap_Breakpoint);
470 skipped_break_addr = NULL;
471 *(unsigned long *) SC_PC(os_context) = displaced_after_inst;
472 os_context->uc_sigmask = orig_sigmask;
474 os_flush_icache((os_vm_address_t) SC_PC(os_context),
475 sizeof(unsigned long));
479 interrupt_handle_now(signal, code, os_context);
483 /* Work around G5 bug; fix courtesy gbyers via chandler */
484 sigreturn(os_context);
488 if (((opcode >> 26) == 3) && (((opcode >> 21) & 31) == 24)) {
489 interrupt_internal_error(signal, code, os_context, 0);
491 /* Work around G5 bug; fix courtesy gbyers via chandler */
492 sigreturn(os_context);
497 interrupt_handle_now(signal, code, os_context);
499 /* Work around G5 bug; fix courtesy gbyers via chandler */
500 sigreturn(os_context);
506 arch_install_interrupt_handlers(void)
508 interrupt_install_low_level_handler(SIGILL, sigill_handler);
509 interrupt_install_low_level_handler(SIGTRAP, sigill_handler);
513 extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
516 funcall0(lispobj function)
518 lispobj *args = current_control_stack_pointer;
520 return call_into_lisp(function, args, 0);
524 funcall1(lispobj function, lispobj arg0)
526 lispobj *args = current_control_stack_pointer;
528 current_control_stack_pointer += 1;
531 return call_into_lisp(function, args, 1);
535 funcall2(lispobj function, lispobj arg0, lispobj arg1)
537 lispobj *args = current_control_stack_pointer;
539 current_control_stack_pointer += 2;
543 return call_into_lisp(function, args, 2);
547 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
549 lispobj *args = current_control_stack_pointer;
551 current_control_stack_pointer += 3;
556 return call_into_lisp(function, args, 3);
560 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
562 os_vm_address_t end =
563 (os_vm_address_t) ((int) (address + length + (32 - 1)) & ~(32 - 1));
564 extern void ppc_flush_cache_line(os_vm_address_t);
566 while (address < end) {
567 ppc_flush_cache_line(address);
573 /* Linkage tables for PowerPC
575 * Linkage entry size is 16, because we need at least 4 instructions to
580 * This had better match lisp::target-foreign-linkage-entry-size in
581 * ppco/parms.lisp! Each entry is 6 instructions long, so at least
584 #ifndef LinkageEntrySize
585 #define LinkageEntrySize (8*4)
589 * Define the registers to use in the linkage jump table. Can be the
590 * same. Some care must be exercised when choosing these. It has to be
591 * a register that is not otherwise being used. reg_NFP is a good
592 * choice. call_into_c trashes reg_NFP without preserving it, so we can
593 * trash it in the linkage jump table.
595 #define LINKAGE_TEMP_REG reg_NFP
596 #define LINKAGE_ADDR_REG reg_A0
599 * Insert the necessary jump instructions at the given address.
602 arch_make_jump_entry(void *reloc_addr, void *target_addr)
605 * Make JMP to function entry.
607 * The instruction sequence is:
609 * addis temp, 0, (hi part of reloc)
610 * ori temp, temp, (lo part of reloc)
611 * addis addr, 0, (hi part of addr)
612 * ori addr, addr, (low part of addr)
618 unsigned long hi; /* Top 16 bits of address */
619 unsigned long lo; /* Low 16 bits of address */
622 inst_ptr = (int *) reloc_addr;
625 * Split the target address into hi and lo parts for the addis/ori
628 hi = (unsigned long) reloc_addr;
633 * addis 3, 0, (hi part)
635 inst = (15 << 26) | (LINKAGE_ADDR_REG << 21) | (0 << 16) | hi;
639 * ori 3, 3, (lo part)
643 (24 << 26) | (LINKAGE_ADDR_REG << 21) | (LINKAGE_ADDR_REG << 16) | lo;
647 * Split the target address into hi and lo parts for the addis/ori
651 hi = (unsigned long) target_addr;
656 * addis 13, 0, (hi part)
659 inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
663 * ori 13, 13, (lo part)
667 (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
674 inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
681 inst = (19 << 26) | (20 << 21) | (528 << 1);
687 os_flush_icache((os_vm_address_t) reloc_addr,
688 (char *) inst_ptr - (char *) reloc_addr);
692 arch_make_linkage_entry(long linkage_entry, void *target_addr, long type)
694 int *reloc_addr = (int *) (FOREIGN_LINKAGE_SPACE_START
696 + linkage_entry * LinkageEntrySize);
698 if (type == 1) { /* code reference */
699 arch_make_jump_entry(reloc_addr, target_addr);
700 } else if (type == 2) {
701 *(unsigned long *) reloc_addr = (unsigned long) target_addr;
705 /* Make a the entry a jump to resolve_linkage_tramp. */
707 extern void resolve_linkage_tramp(void);
710 arch_make_lazy_linkage(long linkage_entry)
712 arch_make_linkage_entry(linkage_entry, (void *) resolve_linkage_tramp, 1);
715 /* Get linkage entry. We're given the return address which should be
716 the address of the jmpl instruction (2nd word) of the linkage
717 entry. Figure out which entry this address belong to. */
720 arch_linkage_entry(unsigned long retaddr)
722 return (retaddr - (FOREIGN_LINKAGE_SPACE_START))
727 int ieee754_rem_pio2(double x, double *y0, double *y1)
729 extern int __ieee754_rem_pio2(double x, double *y);
734 n = __ieee754_rem_pio2(x, y);