3 $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/ppc-arch.c,v 1.14 2008/11/12 15:04:24 rtoy Rel $
5 This code was written as part of the CMU Common Lisp project at
6 Carnegie Mellon University, and has been placed in the public domain.
14 #include "internals.h"
20 #include "interrupt.h"
23 /* The header files may not define PT_DAR/PT_DSISR. This definition
24 is correct for all versions of ppc linux >= 2.0.30
26 As of DR2.1u4, MkLinux doesn't pass these registers to signal
27 handlers correctly; a patch is necessary in order to (partially)
30 Even with the patch, the DSISR may not have its 'write' bit set
31 correctly (it tends not to be set if the fault was caused by
32 something other than a protection violation.)
45 * A macro to generate the instruction
49 * This is what the ppc port uses to signal various traps like
50 * breakpoints and stuff.
52 #define TWLLEI_R0(code) ((3<<26) | (6 << 21) | code)
55 arch_init(fpu_mode_t mode)
60 os_vm_address_t arch_get_bad_addr(HANDLER_ARGS)
62 os_context_t *os_context = (os_context_t *) context;
65 addr = (os_vm_address_t) SC_REG(os_context, PT_DAR);
71 arch_skip_instruction(os_context_t * context)
73 /* Skip the offending instruction */
78 arch_internal_error_arguments(os_context_t * scp)
80 return (unsigned char *) (SC_PC(scp) + 4);
83 boolean arch_pseudo_atomic_atomic(os_context_t * scp)
85 return (SC_REG(scp, reg_ALLOC) & 4);
88 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
91 arch_set_pseudo_atomic_interrupted(os_context_t * scp)
94 SC_REG(scp, reg_NL3) += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
96 SC_REG(scp, reg_ALLOC) |= 1;
101 arch_install_breakpoint(void *pc)
103 unsigned long *ptr = (unsigned long *) pc;
104 unsigned long result = *ptr;
107 * Insert a twllei r0, trap_Breakpoint instruction.
109 *ptr = TWLLEI_R0(trap_Breakpoint);
110 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
116 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
118 *(unsigned long *) pc = orig_inst;
119 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
122 static unsigned long *skipped_break_addr, displaced_after_inst;
123 static sigset_t orig_sigmask;
126 arch_do_displaced_inst(os_context_t * scp, unsigned long orig_inst)
128 unsigned int *pc = (unsigned int *) SC_PC(scp);
130 orig_sigmask = scp->uc_sigmask;
131 sigemptyset(&scp->uc_sigmask);
132 FILLBLOCKSET(&scp->uc_sigmask);
134 /* Put the original instruction back */
136 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
138 skipped_break_addr = (unsigned long) pc;
141 * Replace the next instruction with a
142 * twllei r0, trap_AfterBreakpoint
144 displaced_after_inst = *++pc;
145 *pc = TWLLEI_R0(trap_AfterBreakpoint);
146 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
153 * Return non-zero if the current instruction is an allocation trap
156 allocation_trap_p(os_context_t * context)
168 * First, the instruction has to be a TWLGE temp, NL3, which as the
170 * | 6| 5| 5 | 5 | 10|1| width
171 * |31|5 |dst|src| 4|0| field
173 pc = (unsigned int *) SC_PC(context);
177 fprintf(stderr, "allocation_trap_p at %p: inst = 0x%08x\n", pc, inst);
181 src = (inst >> 11) & 0x1f;
182 dst = (inst >> 16) & 0x1f;
183 if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
184 && (4 == ((inst >> 1) & 0x3ff))) {
186 * We got the instruction. Now, look back to make sure it was
187 * proceeded by what we expected. 2 instructions back should be
188 * an ADD or ADDI instruction.
190 unsigned int add_inst;
194 fprintf(stderr, " add inst at %p: inst = 0x%08x\n",
197 opcode = add_inst >> 26;
198 if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
200 } else if ((opcode == 14)) {
204 "Whoa! Got allocation trap not preceeded by an ADD or ADDI instruction: 0x%08x\n",
212 * Use this function to enable the minimum number of signals we need
213 * when our trap handler needs to call Lisp code that might cons. For
214 * consing to work with gencgc, we need to be able to trap the SIGILL
215 * signal to perform allocation.
218 enable_some_signals(void)
223 fprintf(stderr, "Enabling some signals\n");
226 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
229 sigaddset(&sigs, SIGILL);
230 sigaddset(&sigs, SIGBUS);
231 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
234 fprintf(stderr, "Some signals enabled\n");
239 handle_allocation_trap(os_context_t * context)
243 unsigned int or_inst;
248 boolean were_in_lisp;
256 fprintf(stderr, "In handle_allocation_trap\n");
259 * I don't think it's possible for us NOT to be in lisp when we get
260 * here. Remove this later?
262 were_in_lisp = !foreign_function_call_active;
265 fake_foreign_function_call(context);
267 fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
271 * Look at current instruction: TWNE temp, NL3. We're here because
272 * temp > NL3 and temp is the end of the allocation, and NL3 is
273 * current-region-end-addr.
275 * We need to adjust temp and alloc-tn.
278 pc = (unsigned int *) SC_PC(context);
280 target = (inst >> 16) & 0x1f;
283 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
284 fprintf(stderr, " trap inst = 0x%08x\n", inst);
285 fprintf(stderr, " target reg = %s\n", lisp_register_names[target]);
288 * Go back and look at the add/addi instruction. The second src arg
289 * is the size of the allocation. Get it and call alloc to allocate
295 fprintf(stderr, " add inst = 0x%08x, opcode = %d\n", inst, opcode);
299 * ADDI temp-tn, alloc-tn, size
303 size = (inst & 0xffff);
304 } else if (opcode == 31) {
306 * ADD temp-tn, alloc-tn, size-tn
312 reg = (inst >> 11) & 0x1f;
314 fprintf(stderr, " add, reg = %s\n", lisp_register_names[reg]);
316 size = SC_REG(context, reg);
319 fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
323 * Well, maybe not. sigill_handler probably shouldn't be unblocking
324 * all signals. So, let's enable just the signals we need. Since
325 * alloc might call GC, we need to have SIGILL enabled so we can do
326 * allocation. Do we need more?
328 enable_some_signals();
331 fprintf(stderr, "Ready to alloc\n");
332 fprintf(stderr, "free_pointer = 0x%08x\n",
333 current_dynamic_space_free_pointer);
336 * alloc-tn was incremented by size. Need to decrement it by size to restore it's original value.
338 current_dynamic_space_free_pointer =
339 (lispobj *) ((long) current_dynamic_space_free_pointer - size);
341 fprintf(stderr, "free_pointer = 0x%08x new\n",
342 current_dynamic_space_free_pointer);
345 memory = (char *) alloc(size);
348 fprintf(stderr, "alloc returned %p\n", memory);
349 fprintf(stderr, "free_pointer = 0x%08x\n",
350 current_dynamic_space_free_pointer);
354 * The allocation macro wants the result to point to the end of the
359 fprintf(stderr, "object end at %p\n", memory);
361 SC_REG(context, target) = (unsigned long) memory;
362 SC_REG(context, reg_ALLOC) =
363 (unsigned long) current_dynamic_space_free_pointer;
366 undo_fake_foreign_function_call(context);
374 sigill_handler(HANDLER_ARGS)
376 os_context_t *os_context = (os_context_t *) context;
380 sigprocmask(SIG_SETMASK, &os_context->uc_sigmask, 0);
381 opcode = *((int *) SC_PC(os_context));
384 printf("SIGILL entry: opcode = 0x%08x\n", opcode);
388 if (opcode == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
389 /* Got a twnei reg_NL3,0 - check for deferred interrupt */
391 /* Clear the pseudo-atomic-interrupted bit */
392 SC_REG(os_context, reg_ALLOC) &= ~1;
394 (SC_REG(os_context, reg_ALLOC) -= PSEUDO_ATOMIC_INTERRUPTED_BIAS);
396 arch_skip_instruction(os_context);
397 interrupt_handle_pending(os_context);
399 /* Work around G5 bug; fix courtesy gbyers via chandler */
400 sigreturn(os_context);
405 /* Is this an allocation trap? */
407 if (allocation_trap_p(os_context)) {
408 handle_allocation_trap(os_context);
409 arch_skip_instruction(os_context);
411 sigreturn(os_context);
417 if ((opcode >> 16) == ((3 << 10) | (6 << 5))) {
418 /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
419 int trap = opcode & 0x1f, extra = (opcode >> 5) & 0x1f;
422 printf("SIGILL: TWLLEI, code = %d\n", trap);
428 fake_foreign_function_call(os_context);
429 lose("%%primitive halt called; the party is over.\n");
433 interrupt_internal_error(signal, code, os_context,
434 trap == trap_Cerror);
437 case trap_PendingInterrupt:
438 arch_skip_instruction(os_context);
439 interrupt_handle_pending(os_context);
442 case trap_Breakpoint:
444 printf("trap_Breakpoint\n");
447 handle_breakpoint(signal, code, os_context);
450 case trap_FunctionEndBreakpoint:
452 printf("trap_FunctionEndBreakpoint\n");
456 (int) handle_function_end_breakpoint(signal, code, os_context);
459 case trap_AfterBreakpoint:
461 fprintf(stderr, "trap_AfterBreakpoint: break_addr = %p\n",
463 fprintf(stderr, " CSP = %p\n",
464 (void *) SC_REG(os_context, reg_CSP));
465 fprintf(stderr, " CFP = %p\n",
466 (void *) SC_REG(os_context, reg_CFP));
467 fprintf(stderr, " OCFP = %p\n",
468 (void *) SC_REG(os_context, reg_OCFP));
470 /* Put our breakpoint instruction back in */
471 *skipped_break_addr = TWLLEI_R0(trap_Breakpoint);
472 skipped_break_addr = NULL;
473 *(unsigned long *) SC_PC(os_context) = displaced_after_inst;
474 os_context->uc_sigmask = orig_sigmask;
476 os_flush_icache((os_vm_address_t) SC_PC(os_context),
477 sizeof(unsigned long));
481 interrupt_handle_now(signal, code, os_context);
485 /* Work around G5 bug; fix courtesy gbyers via chandler */
486 sigreturn(os_context);
490 if (((opcode >> 26) == 3) && (((opcode >> 21) & 31) == 24)) {
491 interrupt_internal_error(signal, code, os_context, 0);
493 /* Work around G5 bug; fix courtesy gbyers via chandler */
494 sigreturn(os_context);
499 interrupt_handle_now(signal, code, os_context);
501 /* Work around G5 bug; fix courtesy gbyers via chandler */
502 sigreturn(os_context);
508 arch_install_interrupt_handlers(void)
510 interrupt_install_low_level_handler(SIGILL, sigill_handler);
511 interrupt_install_low_level_handler(SIGTRAP, sigill_handler);
515 extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
518 funcall0(lispobj function)
520 lispobj *args = current_control_stack_pointer;
522 return call_into_lisp(function, args, 0);
526 funcall1(lispobj function, lispobj arg0)
528 lispobj *args = current_control_stack_pointer;
530 current_control_stack_pointer += 1;
533 return call_into_lisp(function, args, 1);
537 funcall2(lispobj function, lispobj arg0, lispobj arg1)
539 lispobj *args = current_control_stack_pointer;
541 current_control_stack_pointer += 2;
545 return call_into_lisp(function, args, 2);
549 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
551 lispobj *args = current_control_stack_pointer;
553 current_control_stack_pointer += 3;
558 return call_into_lisp(function, args, 3);
562 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
564 os_vm_address_t end =
565 (os_vm_address_t) ((int) (address + length + (32 - 1)) & ~(32 - 1));
566 extern void ppc_flush_cache_line(os_vm_address_t);
568 while (address < end) {
569 ppc_flush_cache_line(address);
575 /* Linkage tables for PowerPC
577 * Linkage entry size is 16, because we need at least 4 instructions to
582 * This had better match lisp::target-foreign-linkage-entry-size in
583 * ppco/parms.lisp! Each entry is 6 instructions long, so at least
586 #ifndef LinkageEntrySize
587 #define LinkageEntrySize (8*4)
591 * Define the registers to use in the linkage jump table. Can be the
592 * same. Some care must be exercised when choosing these. It has to be
593 * a register that is not otherwise being used. reg_NFP is a good
594 * choice. call_into_c trashes reg_NFP without preserving it, so we can
595 * trash it in the linkage jump table.
597 #define LINKAGE_TEMP_REG reg_NFP
598 #define LINKAGE_ADDR_REG reg_A0
601 * Insert the necessary jump instructions at the given address.
604 arch_make_jump_entry(void *reloc_addr, void *target_addr)
607 * Make JMP to function entry.
609 * The instruction sequence is:
611 * addis temp, 0, (hi part of reloc)
612 * ori temp, temp, (lo part of reloc)
613 * addis addr, 0, (hi part of addr)
614 * ori addr, addr, (low part of addr)
620 unsigned long hi; /* Top 16 bits of address */
621 unsigned long lo; /* Low 16 bits of address */
624 inst_ptr = (int *) reloc_addr;
627 * Split the target address into hi and lo parts for the addis/ori
630 hi = (unsigned long) reloc_addr;
635 * addis 3, 0, (hi part)
637 inst = (15 << 26) | (LINKAGE_ADDR_REG << 21) | (0 << 16) | hi;
641 * ori 3, 3, (lo part)
645 (24 << 26) | (LINKAGE_ADDR_REG << 21) | (LINKAGE_ADDR_REG << 16) | lo;
649 * Split the target address into hi and lo parts for the addis/ori
653 hi = (unsigned long) target_addr;
658 * addis 13, 0, (hi part)
661 inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
665 * ori 13, 13, (lo part)
669 (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
676 inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
683 inst = (19 << 26) | (20 << 21) | (528 << 1);
689 os_flush_icache((os_vm_address_t) reloc_addr,
690 (char *) inst_ptr - (char *) reloc_addr);
694 arch_make_linkage_entry(long linkage_entry, void *target_addr, long type)
696 int *reloc_addr = (int *) (FOREIGN_LINKAGE_SPACE_START
698 + linkage_entry * LinkageEntrySize);
700 if (type == 1) { /* code reference */
701 arch_make_jump_entry(reloc_addr, target_addr);
702 } else if (type == 2) {
703 *(unsigned long *) reloc_addr = (unsigned long) target_addr;
707 /* Make a the entry a jump to resolve_linkage_tramp. */
709 extern void resolve_linkage_tramp(void);
712 arch_make_lazy_linkage(long linkage_entry)
714 arch_make_linkage_entry(linkage_entry, (void *) resolve_linkage_tramp, 1);
717 /* Get linkage entry. We're given the return address which should be
718 the address of the jmpl instruction (2nd word) of the linkage
719 entry. Figure out which entry this address belong to. */
722 arch_linkage_entry(unsigned long retaddr)
724 return (retaddr - (FOREIGN_LINKAGE_SPACE_START))
729 int ieee754_rem_pio2(double x, double *y0, double *y1)
731 extern int __ieee754_rem_pio2(double x, double *y);
736 n = __ieee754_rem_pio2(x, y);