1.0.37.57: better DEFMETHOD pretty-printing
[sbcl/pkhuong.git] / src / runtime / ppc-arch.c
blob533293d8835638d4ea38cc4c1f4dedc816cb11ed
1 /*
2 * This software is part of the SBCL system. See the README file for
3 * more information.
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.
12 #include <stdio.h>
14 #include "sbcl.h"
15 #include "arch.h"
16 #include "globals.h"
17 #include "validate.h"
18 #include "os.h"
19 #include "interrupt.h"
20 #include "lispregs.h"
21 #include "signal.h"
22 #include "interrupt.h"
23 #include "interr.h"
24 #include "breakpoint.h"
25 #include "alloc.h"
27 #if defined(LISP_FEATURE_GENCGC)
28 #include "gencgc-alloc-region.h"
29 #endif
31 /* The header files may not define PT_DAR/PT_DSISR. This definition
32 is correct for all versions of ppc linux >= 2.0.30
34 As of DR2.1u4, MkLinux doesn't pass these registers to signal
35 handlers correctly; a patch is necessary in order to (partially)
36 correct this.
38 Even with the patch, the DSISR may not have its 'write' bit set
39 correctly (it tends not to be set if the fault was caused by
40 something other than a protection violation.)
42 Caveat callers. */
44 #if defined (LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_LINUX)
45 #ifndef PT_DAR
46 #define PT_DAR 41
47 #endif
49 #ifndef PT_DSISR
50 #define PT_DSISR 42
51 #endif
52 #endif
54 void arch_init() {
57 os_vm_address_t
58 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
60 os_vm_address_t addr;
62 #if defined(LISP_FEATURE_NETBSD)
63 addr = (os_vm_address_t) (code->si_addr);
64 #else
65 addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
66 #endif
67 return addr;
71 void
72 arch_skip_instruction(os_context_t *context)
74 char** pcptr;
75 pcptr = (char**) os_context_pc_addr(context);
76 *pcptr += 4;
79 unsigned char *
80 arch_internal_error_arguments(os_context_t *context)
82 return (unsigned char *)(*os_context_pc_addr(context)+4);
86 boolean
87 arch_pseudo_atomic_atomic(os_context_t *context)
89 /* FIXME: this foreign_function_call_active test is dubious at
90 * best. If a foreign call is made in a pseudo atomic section
91 * (?) or more likely a pseudo atomic section is in a foreign
92 * call then an interrupt is executed immediately. Maybe it
93 * has to do with C code not maintaining pseudo atomic
94 * properly. MG - 2005-08-10
96 * The foreign_function_call_active used to live at each call-site
97 * to arch_pseudo_atomic_atomic, but this seems clearer.
98 * --NS 2007-05-15 */
99 return (!foreign_function_call_active)
100 && ((*os_context_register_addr(context,reg_ALLOC)) & 4);
103 void
104 arch_set_pseudo_atomic_interrupted(os_context_t *context)
106 *os_context_register_addr(context,reg_ALLOC) |= 1;
109 void
110 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
112 *os_context_register_addr(context,reg_ALLOC) &= ~1;
115 unsigned int
116 arch_install_breakpoint(void *pc)
118 unsigned int *ptr = (unsigned int *)pc;
119 unsigned int result = *ptr;
120 *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
121 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
122 return result;
125 void
126 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
128 *(unsigned int *)pc = orig_inst;
129 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
133 * Perform the instruction that we overwrote with a breakpoint. As we
134 * don't have a single-step facility, this means we have to:
135 * - put the instruction back
136 * - put a second breakpoint at the following instruction,
137 * set after_breakpoint and continue execution.
139 * When the second breakpoint is hit (very shortly thereafter, we hope)
140 * sigtrap_handler gets called again, but follows the AfterBreakpoint
141 * arm, which
142 * - puts a bpt back in the first breakpoint place (running across a
143 * breakpoint shouldn't cause it to be uninstalled)
144 * - replaces the second bpt with the instruction it was meant to be
145 * - carries on
147 * Clear?
149 static unsigned int *skipped_break_addr, displaced_after_inst;
150 static sigset_t orig_sigmask;
152 void
153 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
155 /* not sure how we ensure that we get the breakpoint reinstalled
156 * after doing this -dan */
157 unsigned int *pc = (unsigned int *)(*os_context_pc_addr(context));
159 orig_sigmask = *os_context_sigmask_addr(context);
160 sigaddset_blockable(os_context_sigmask_addr(context));
162 *pc = orig_inst;
163 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
164 skipped_break_addr = pc;
166 /* FIXME: we should apparently be installing the after-breakpoint
167 * here, but would need to find the next instruction address for
168 * it first. alpha-arch.c shows how to do it. --NS 2007-04-02 */
171 #ifdef LISP_FEATURE_GENCGC
173 * Return non-zero if the current instruction is an allocation trap
175 static int
176 allocation_trap_p(os_context_t * context)
178 int result;
179 unsigned int *pc;
180 unsigned inst;
181 unsigned opcode;
182 unsigned src;
183 unsigned dst;
185 result = 0;
188 * First, the instruction has to be a TWLGE temp, NL3, which has the
189 * format.
190 * | 6| 5| 5 | 5 | 10|1| width
191 * |31|5 |dst|src| 4|0| field
193 pc = (unsigned int *) (*os_context_pc_addr(context));
194 inst = *pc;
196 #if 0
197 fprintf(stderr, "allocation_trap_p at %p: inst = 0x%08x\n", pc, inst);
198 #endif
200 opcode = inst >> 26;
201 src = (inst >> 11) & 0x1f;
202 dst = (inst >> 16) & 0x1f;
203 if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
204 && (4 == ((inst >> 1) & 0x3ff))) {
206 * We got the instruction. Now, look back to make sure it was
207 * proceeded by what we expected. 2 instructions back should be
208 * an ADD or ADDI instruction.
210 unsigned int add_inst;
212 add_inst = pc[-3];
213 #if 0
214 fprintf(stderr, " add inst at %p: inst = 0x%08x\n",
215 pc - 3, add_inst);
216 #endif
217 opcode = add_inst >> 26;
218 if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
219 return 1;
220 } else if ((opcode == 14)) {
221 return 1;
222 } else {
223 fprintf(stderr,
224 "Whoa! Got allocation trap but could not find ADD or ADDI instruction: 0x%08x in the proper place\n",
225 add_inst);
228 return 0;
231 extern struct alloc_region boxed_region;
233 void
234 handle_allocation_trap(os_context_t * context)
236 unsigned int *pc;
237 unsigned int inst;
238 unsigned int target, target_ptr, end_addr;
239 unsigned int opcode;
240 int size;
241 boolean were_in_lisp;
242 char *memory;
244 target = 0;
245 size = 0;
247 #if 0
248 fprintf(stderr, "In handle_allocation_trap\n");
249 #endif
251 /* I don't think it's possible for us NOT to be in lisp when we get
252 * here. Remove this later? */
253 were_in_lisp = !foreign_function_call_active;
255 if (were_in_lisp) {
256 fake_foreign_function_call(context);
257 } else {
258 fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
262 * Look at current instruction: TWNE temp, NL3. We're here because
263 * temp > NL3 and temp is the end of the allocation, and NL3 is
264 * current-region-end-addr.
266 * We need to adjust temp and alloc-tn.
269 pc = (unsigned int *) (*os_context_pc_addr(context));
270 inst = pc[0];
271 end_addr = (inst >> 11) & 0x1f;
272 target = (inst >> 16) & 0x1f;
274 target_ptr = *os_context_register_addr(context, target);
276 #if 0
277 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
278 fprintf(stderr, "boxed_region.free_pointer: %p\n", boxed_region.free_pointer);
279 fprintf(stderr, "boxed_region.end_addr: %p\n", boxed_region.end_addr);
280 fprintf(stderr, "target reg: %d, end_addr reg: %d\n", target, end_addr);
281 fprintf(stderr, "target: %x\n", *os_context_register_addr(context, target));
282 fprintf(stderr, "end_addr: %x\n", *os_context_register_addr(context, end_addr));
283 #endif
285 #if 0
286 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
287 fprintf(stderr, " trap inst = 0x%08x\n", inst);
288 fprintf(stderr, " target reg = %s\n", lisp_register_names[target]);
289 #endif
292 * Go back and look at the add/addi instruction. The second src arg
293 * is the size of the allocation. Get it and call alloc to allocate
294 * new space.
296 inst = pc[-3];
297 opcode = inst >> 26;
298 #if 0
299 fprintf(stderr, " add inst = 0x%08x, opcode = %d\n", inst, opcode);
300 #endif
301 if (opcode == 14) {
303 * ADDI temp-tn, alloc-tn, size
305 * Extract the size
307 size = (inst & 0xffff);
308 } else if (opcode == 31) {
310 * ADD temp-tn, alloc-tn, size-tn
312 * Extract the size
314 int reg;
316 reg = (inst >> 11) & 0x1f;
317 #if 0
318 fprintf(stderr, " add, reg = %s\n", lisp_register_names[reg]);
319 #endif
320 size = *os_context_register_addr(context, reg);
324 #if 0
325 fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
326 #endif
328 #if INLINE_ALLOC_DEBUG
329 if ((((unsigned long)boxed_region.end_addr + size) / PAGE_SIZE) ==
330 (((unsigned long)boxed_region.end_addr) / PAGE_SIZE)) {
331 fprintf(stderr,"*** possibly bogus trap allocation of %d bytes at %p\n",
332 size, target_ptr);
333 fprintf(stderr, " dynamic_space_free_pointer: %p, boxed_region.end_addr %p\n",
334 dynamic_space_free_pointer, boxed_region.end_addr);
336 #endif
338 #if 0
339 fprintf(stderr, "Ready to alloc\n");
340 fprintf(stderr, "free_pointer = 0x%08x\n",
341 dynamic_space_free_pointer);
342 #endif
345 * alloc-tn was incremented by size. Need to decrement it by size
346 * to restore its original value. This is not true on GENCGC
347 * anymore. d_s_f_p and reg_alloc get out of sync, but the p_a
348 * bits stay intact and we set it to the proper value when it
349 * needs to be. Keep this comment here for the moment in case
350 * somebody tries to figure out what happened here.
352 /* dynamic_space_free_pointer =
353 (lispobj *) ((long) dynamic_space_free_pointer - size);
355 #if 0
356 fprintf(stderr, "free_pointer = 0x%08x new\n",
357 dynamic_space_free_pointer);
358 #endif
361 struct interrupt_data *data =
362 arch_os_get_current_thread()->interrupt_data;
363 data->allocation_trap_context = context;
364 memory = (char *) alloc(size);
365 data->allocation_trap_context = 0;
368 #if 0
369 fprintf(stderr, "alloc returned %p\n", memory);
370 fprintf(stderr, "free_pointer = 0x%08x\n",
371 dynamic_space_free_pointer);
372 #endif
375 * The allocation macro wants the result to point to the end of the
376 * object!
378 memory += size;
380 #if 0
381 fprintf(stderr, "object end at %p\n", memory);
382 #endif
384 *os_context_register_addr(context, target) = (unsigned long) memory;
385 *os_context_register_addr(context, reg_ALLOC) =
386 (unsigned long) dynamic_space_free_pointer
387 | (*os_context_register_addr(context, reg_ALLOC)
388 & LOWTAG_MASK);
390 if (were_in_lisp) {
391 undo_fake_foreign_function_call(context);
396 #endif
398 void
399 arch_handle_breakpoint(os_context_t *context)
401 handle_breakpoint(context);
404 void
405 arch_handle_fun_end_breakpoint(os_context_t *context)
407 *os_context_pc_addr(context)
408 =(int)handle_fun_end_breakpoint(context);
411 void
412 arch_handle_after_breakpoint(os_context_t *context)
414 *skipped_break_addr = trap_Breakpoint;
415 skipped_break_addr = NULL;
416 *(unsigned int *)*os_context_pc_addr(context)
417 = displaced_after_inst;
418 *os_context_sigmask_addr(context)= orig_sigmask;
419 os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
420 sizeof(unsigned int));
423 void
424 arch_handle_single_step_trap(os_context_t *context, int trap)
426 unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
427 int register_offset = code >> 5 & 0x1f;
428 handle_single_step_trap(context, trap, register_offset);
429 arch_skip_instruction(context);
432 static void
433 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
435 unsigned int code;
437 code=*((u32 *)(*os_context_pc_addr(context)));
438 if (code == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
439 arch_clear_pseudo_atomic_interrupted(context);
440 arch_skip_instruction(context);
441 /* interrupt or GC was requested in PA; now we're done with the
442 PA section we may as well get around to it */
443 interrupt_handle_pending(context);
444 return;
447 #ifdef LISP_FEATURE_GENCGC
448 /* Is this an allocation trap? */
449 if (allocation_trap_p(context)) {
450 handle_allocation_trap(context);
451 arch_skip_instruction(context);
452 return;
454 #endif
456 if ((code >> 16) == ((3 << 10) | (6 << 5))) {
457 /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
458 int trap = code & 0x1f;
459 handle_trap(context,trap);
460 return;
462 if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
463 interrupt_internal_error(context, 0);
464 return;
467 interrupt_handle_now(signal, (siginfo_t *)code, context);
471 void arch_install_interrupt_handlers()
473 undoably_install_low_level_interrupt_handler(SIGILL, sigtrap_handler);
474 undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
477 void
478 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
480 os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
481 extern void ppc_flush_cache_line(os_vm_address_t);
483 while (address < end) {
484 ppc_flush_cache_line(address);
485 address += 32;
489 #ifdef LISP_FEATURE_LINKAGE_TABLE
491 /* Linkage tables for PowerPC
493 * Linkage entry size is 16, because we need at least 4 instructions to
494 * implement a jump.
498 * Define the registers to use in the linkage jump table. Can be the
499 * same. Some care must be exercised when choosing these. It has to be
500 * a register that is not otherwise being used. reg_NFP is a good
501 * choice. call_into_c trashes reg_NFP without preserving it, so we can
502 * trash it in the linkage jump table.
504 #define LINKAGE_TEMP_REG reg_NFP
505 #define LINKAGE_ADDR_REG reg_NFP
508 * Insert the necessary jump instructions at the given address.
510 void
511 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
514 * Make JMP to function entry.
516 * The instruction sequence is:
518 * addis 13, 0, (hi part of addr)
519 * ori 13, 13, (low part of addr)
520 * mtctr 13
521 * bctr
524 int* inst_ptr;
525 unsigned long hi; /* Top 16 bits of address */
526 unsigned long lo; /* Low 16 bits of address */
527 unsigned int inst;
529 inst_ptr = (int*) reloc_addr;
532 * Split the target address into hi and lo parts for the sethi
533 * instruction. hi is the top 22 bits. lo is the low 10 bits.
535 hi = (unsigned long) target_addr;
536 lo = hi & 0xffff;
537 hi >>= 16;
540 * addis 13, 0, (hi part)
543 inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
544 *inst_ptr++ = inst;
547 * ori 13, 13, (lo part)
550 inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
551 *inst_ptr++ = inst;
554 * mtctr 13
557 inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
558 *inst_ptr++ = inst;
561 * bctr
564 inst = (19 << 26) | (20 << 21) | (528 << 1);
565 *inst_ptr++ = inst;
568 *inst_ptr++ = inst;
570 os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
573 void
574 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
576 *(unsigned long *)reloc_addr = (unsigned long)target_addr;
579 #endif