1.0.4.18: trap handling cleanup continues
[sbcl.git] / src / runtime / ppc-arch.c
1 /*
2  * This software is part of the SBCL system. See the README file for
3  * more information.
4  *
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.
10  */
11
12 #include <stdio.h>
13
14 #include "sbcl.h"
15 #include "arch.h"
16 #include "globals.h"
17 #include "validate.h"
18 #include "os.h"
19 #include "lispregs.h"
20 #include "signal.h"
21 #include "interrupt.h"
22 #include "interr.h"
23 #include "breakpoint.h"
24
25 #if defined(LISP_FEATURE_GENCGC)
26 #include "gencgc-alloc-region.h"
27 #endif
28
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
31
32      As of DR2.1u4, MkLinux doesn't pass these registers to signal
33      handlers correctly; a patch is necessary in order to (partially)
34      correct this.
35
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.)
39
40      Caveat callers.  */
41
42 #if defined (LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_LINUX)
43 #ifndef PT_DAR
44 #define PT_DAR          41
45 #endif
46
47 #ifndef PT_DSISR
48 #define PT_DSISR        42
49 #endif
50 #endif
51
52 void arch_init() {
53 }
54
55 os_vm_address_t
56 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
57 {
58     os_vm_address_t addr;
59
60 #if defined(LISP_FEATURE_NETBSD)
61     addr = (os_vm_address_t) (code->si_addr);
62 #else
63     addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
64 #endif
65     return addr;
66 }
67
68
69 void
70 arch_skip_instruction(os_context_t *context)
71 {
72     char** pcptr;
73     pcptr = (char**) os_context_pc_addr(context);
74     *pcptr += 4;
75 }
76
77 unsigned char *
78 arch_internal_error_arguments(os_context_t *context)
79 {
80     return (unsigned char *)(*os_context_pc_addr(context)+4);
81 }
82
83
84 boolean
85 arch_pseudo_atomic_atomic(os_context_t *context)
86 {
87     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
88 }
89
90 void
91 arch_set_pseudo_atomic_interrupted(os_context_t *context)
92 {
93     *os_context_register_addr(context,reg_ALLOC) |= 1;
94 }
95
96 void
97 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
98 {
99     *os_context_register_addr(context,reg_ALLOC) &= ~1;
100 }
101
102 unsigned int
103 arch_install_breakpoint(void *pc)
104 {
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));
109     return result;
110 }
111
112 void
113 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
114 {
115     *(unsigned int *)pc = orig_inst;
116     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
117 }
118
119 /*
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.
125  *
126  * When the second breakpoint is hit (very shortly thereafter, we hope)
127  * sigtrap_handler gets called again, but follows the AfterBreakpoint
128  * arm, which
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
132  * - carries on
133  *
134  * Clear?
135  */
136 static unsigned int *skipped_break_addr, displaced_after_inst;
137 static sigset_t orig_sigmask;
138
139 void
140 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
141 {
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));
145
146     orig_sigmask = *os_context_sigmask_addr(context);
147     sigaddset_blockable(os_context_sigmask_addr(context));
148
149     *pc = orig_inst;
150     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
151     skipped_break_addr = pc;
152
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 */
156 }
157
158 #ifdef LISP_FEATURE_GENCGC
159 /*
160  * Return non-zero if the current instruction is an allocation trap
161  */
162 static int
163 allocation_trap_p(os_context_t * context)
164 {
165     int result;
166     unsigned int *pc;
167     unsigned inst;
168     unsigned opcode;
169     unsigned src;
170     unsigned dst;
171
172     result = 0;
173
174     /*
175      * First, the instruction has to be a TWLGE temp, NL3, which has the
176      * format.
177      * | 6| 5| 5 | 5 | 10|1|  width
178      * |31|5 |dst|src|  4|0|  field
179      */
180     pc = (unsigned int *) (*os_context_pc_addr(context));
181     inst = *pc;
182
183 #if 0
184     fprintf(stderr, "allocation_trap_p at %p:  inst = 0x%08x\n", pc, inst);
185 #endif
186
187     opcode = inst >> 26;
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))) {
192         /*
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.
196          */
197         unsigned int add_inst;
198
199         add_inst = pc[-3];
200 #if 0
201         fprintf(stderr, "   add inst at %p:  inst = 0x%08x\n",
202                 pc - 3, add_inst);
203 #endif
204         opcode = add_inst >> 26;
205         if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
206             return 1;
207         } else if ((opcode == 14)) {
208             return 1;
209         } else {
210             fprintf(stderr,
211                     "Whoa! Got allocation trap but could not find ADD or ADDI instruction: 0x%08x in the proper place\n",
212                     add_inst);
213         }
214     }
215     return 0;
216 }
217
218 extern struct alloc_region boxed_region;
219
220 void
221 handle_allocation_trap(os_context_t * context)
222 {
223     unsigned int *pc;
224     unsigned int inst;
225     unsigned int target, target_ptr, end_addr;
226     unsigned int opcode;
227     int size;
228     boolean were_in_lisp;
229     char *memory;
230
231     target = 0;
232     size = 0;
233
234 #if 0
235     fprintf(stderr, "In handle_allocation_trap\n");
236 #endif
237
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;
241
242     if (were_in_lisp) {
243         fake_foreign_function_call(context);
244     } else {
245         fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
246     }
247
248     /*
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.
252      *
253      * We need to adjust temp and alloc-tn.
254      */
255
256     pc = (unsigned int *) (*os_context_pc_addr(context));
257     inst = pc[0];
258     end_addr = (inst >> 11) & 0x1f;
259     target = (inst >> 16) & 0x1f;
260
261     target_ptr = *os_context_register_addr(context, target);
262
263 #if 0
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));
270 #endif
271
272 #if 0
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]);
276 #endif
277
278     /*
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
281      * new space.
282      */
283     inst = pc[-3];
284     opcode = inst >> 26;
285 #if 0
286     fprintf(stderr, "  add inst  = 0x%08x, opcode = %d\n", inst, opcode);
287 #endif
288     if (opcode == 14) {
289         /*
290          * ADDI temp-tn, alloc-tn, size
291          *
292          * Extract the size
293          */
294         size = (inst & 0xffff);
295     } else if (opcode == 31) {
296         /*
297          * ADD temp-tn, alloc-tn, size-tn
298          *
299          * Extract the size
300          */
301         int reg;
302
303         reg = (inst >> 11) & 0x1f;
304 #if 0
305         fprintf(stderr, "  add, reg = %s\n", lisp_register_names[reg]);
306 #endif
307         size = *os_context_register_addr(context, reg);
308
309     }
310
311 #if 0
312     fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
313 #endif
314
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",
319               size, target_ptr);
320       fprintf(stderr, "    dynamic_space_free_pointer: %p, boxed_region.end_addr %p\n",
321               dynamic_space_free_pointer, boxed_region.end_addr);
322     }
323 #endif
324
325 #if 0
326     fprintf(stderr, "Ready to alloc\n");
327     fprintf(stderr, "free_pointer = 0x%08x\n",
328             dynamic_space_free_pointer);
329 #endif
330
331     /*
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.
338      */
339     /*    dynamic_space_free_pointer =
340         (lispobj *) ((long) dynamic_space_free_pointer - size);
341     */
342 #if 0
343     fprintf(stderr, "free_pointer = 0x%08x new\n",
344             dynamic_space_free_pointer);
345 #endif
346
347     memory = (char *) alloc(size);
348
349 #if 0
350     fprintf(stderr, "alloc returned %p\n", memory);
351     fprintf(stderr, "free_pointer = 0x%08x\n",
352             dynamic_space_free_pointer);
353 #endif
354
355     /*
356      * The allocation macro wants the result to point to the end of the
357      * object!
358      */
359     memory += size;
360
361 #if 0
362     fprintf(stderr, "object end at %p\n", memory);
363 #endif
364
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)
369          & LOWTAG_MASK);
370
371     if (were_in_lisp) {
372         undo_fake_foreign_function_call(context);
373     }
374
375
376 }
377 #endif
378
379 void
380 arch_handle_breakpoint(os_context_t *context)
381 {
382     handle_breakpoint(context);
383 }
384
385 void
386 arch_handle_fun_end_breakpoint(os_context_t *context)
387 {
388     *os_context_pc_addr(context)
389         =(int)handle_fun_end_breakpoint(context);
390 }
391
392 void
393 arch_handle_after_breakpoint(os_context_t *context)
394 {
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));
402 }
403
404 void
405 arch_handle_single_step_trap(os_context_t *context, int trap)
406 {
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);
411 }
412
413 static void
414 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
415 {
416     unsigned int code;
417
418 #ifdef LISP_FEATURE_LINUX
419     os_restore_fp_control(context);
420 #endif
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);
428         return;
429     }
430
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);
438 #endif
439         return;
440     }
441 #endif
442
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);
447
448 #ifdef LISP_FEATURE_DARWIN
449         DARWIN_FIX_CONTEXT(context);
450 #endif
451         return;
452     }
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);
457 #endif
458         return;
459     }
460
461     interrupt_handle_now(signal, code, context);
462 #ifdef LISP_FEATURE_DARWIN
463     /* Work around G5 bug */
464     DARWIN_FIX_CONTEXT(context);
465 #endif
466 }
467
468
469 void arch_install_interrupt_handlers()
470 {
471     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
472     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
473 }
474
475
476 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
477
478 lispobj funcall0(lispobj function)
479 {
480     lispobj *args = current_control_stack_pointer;
481
482     return call_into_lisp(function, args, 0);
483 }
484
485 lispobj funcall1(lispobj function, lispobj arg0)
486 {
487     lispobj *args = current_control_stack_pointer;
488
489     current_control_stack_pointer += 1;
490     args[0] = arg0;
491
492     return call_into_lisp(function, args, 1);
493 }
494
495 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
496 {
497     lispobj *args = current_control_stack_pointer;
498
499     current_control_stack_pointer += 2;
500     args[0] = arg0;
501     args[1] = arg1;
502
503     return call_into_lisp(function, args, 2);
504 }
505
506 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
507 {
508     lispobj *args = current_control_stack_pointer;
509
510     current_control_stack_pointer += 3;
511     args[0] = arg0;
512     args[1] = arg1;
513     args[2] = arg2;
514
515     return call_into_lisp(function, args, 3);
516 }
517
518 void
519 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
520 {
521   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
522   extern void ppc_flush_cache_line(os_vm_address_t);
523
524   while (address < end) {
525     ppc_flush_cache_line(address);
526     address += 32;
527   }
528 }
529
530 #ifdef LISP_FEATURE_LINKAGE_TABLE
531
532 /* Linkage tables for PowerPC
533  *
534  * Linkage entry size is 16, because we need at least 4 instructions to
535  * implement a jump.
536  */
537
538 /*
539  * Define the registers to use in the linkage jump table. Can be the
540  * same. Some care must be exercised when choosing these. It has to be
541  * a register that is not otherwise being used. reg_NFP is a good
542  * choice. call_into_c trashes reg_NFP without preserving it, so we can
543  * trash it in the linkage jump table.
544  */
545 #define LINKAGE_TEMP_REG        reg_NFP
546 #define LINKAGE_ADDR_REG        reg_NFP
547
548 /*
549  * Insert the necessary jump instructions at the given address.
550  */
551 void
552 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
553 {
554   /*
555    * Make JMP to function entry.
556    *
557    * The instruction sequence is:
558    *
559    *        addis 13, 0, (hi part of addr)
560    *        ori   13, 13, (low part of addr)
561    *        mtctr 13
562    *        bctr
563    *
564    */
565   int* inst_ptr;
566   unsigned long hi;                   /* Top 16 bits of address */
567   unsigned long lo;                   /* Low 16 bits of address */
568   unsigned int inst;
569
570   inst_ptr = (int*) reloc_addr;
571
572   /*
573    * Split the target address into hi and lo parts for the sethi
574    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
575    */
576   hi = (unsigned long) target_addr;
577   lo = hi & 0xffff;
578   hi >>= 16;
579
580   /*
581    * addis 13, 0, (hi part)
582    */
583
584   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
585   *inst_ptr++ = inst;
586
587   /*
588    * ori 13, 13, (lo part)
589    */
590
591   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
592   *inst_ptr++ = inst;
593
594   /*
595    * mtctr 13
596    */
597
598   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
599   *inst_ptr++ = inst;
600
601   /*
602    * bctr
603    */
604
605   inst = (19 << 26) | (20 << 21) | (528 << 1);
606   *inst_ptr++ = inst;
607
608
609   *inst_ptr++ = inst;
610
611   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
612 }
613
614 void
615 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
616 {
617     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
618 }
619
620 #endif