1.0.4.13: refactor trap handling
[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
154 #ifdef LISP_FEATURE_GENCGC
155 /*
156  * Return non-zero if the current instruction is an allocation trap
157  */
158 static int
159 allocation_trap_p(os_context_t * context)
160 {
161     int result;
162     unsigned int *pc;
163     unsigned inst;
164     unsigned opcode;
165     unsigned src;
166     unsigned dst;
167
168     result = 0;
169
170     /*
171      * First, the instruction has to be a TWLGE temp, NL3, which has the
172      * format.
173      * | 6| 5| 5 | 5 | 10|1|  width
174      * |31|5 |dst|src|  4|0|  field
175      */
176     pc = (unsigned int *) (*os_context_pc_addr(context));
177     inst = *pc;
178
179 #if 0
180     fprintf(stderr, "allocation_trap_p at %p:  inst = 0x%08x\n", pc, inst);
181 #endif
182
183     opcode = inst >> 26;
184     src = (inst >> 11) & 0x1f;
185     dst = (inst >> 16) & 0x1f;
186     if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
187         && (4 == ((inst >> 1) & 0x3ff))) {
188         /*
189          * We got the instruction.  Now, look back to make sure it was
190          * proceeded by what we expected.  2 instructions back should be
191          * an ADD or ADDI instruction.
192          */
193         unsigned int add_inst;
194
195         add_inst = pc[-3];
196 #if 0
197         fprintf(stderr, "   add inst at %p:  inst = 0x%08x\n",
198                 pc - 3, add_inst);
199 #endif
200         opcode = add_inst >> 26;
201         if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
202             return 1;
203         } else if ((opcode == 14)) {
204             return 1;
205         } else {
206             fprintf(stderr,
207                     "Whoa! Got allocation trap but could not find ADD or ADDI instruction: 0x%08x in the proper place\n",
208                     add_inst);
209         }
210     }
211     return 0;
212 }
213
214 extern struct alloc_region boxed_region;
215
216 void
217 handle_allocation_trap(os_context_t * context)
218 {
219     unsigned int *pc;
220     unsigned int inst;
221     unsigned int target, target_ptr, end_addr;
222     unsigned int opcode;
223     int size;
224     boolean were_in_lisp;
225     char *memory;
226
227     target = 0;
228     size = 0;
229
230 #if 0
231     fprintf(stderr, "In handle_allocation_trap\n");
232 #endif
233
234     /*
235      * I don't think it's possible for us NOT to be in lisp when we get
236      * here.  Remove this later?
237      */
238     were_in_lisp = !foreign_function_call_active;
239
240     if (were_in_lisp) {
241         fake_foreign_function_call(context);
242     } else {
243         fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
244     }
245
246     /*
247      * Look at current instruction: TWNE temp, NL3. We're here because
248      * temp > NL3 and temp is the end of the allocation, and NL3 is
249      * current-region-end-addr.
250      *
251      * We need to adjust temp and alloc-tn.
252      */
253
254     pc = (unsigned int *) (*os_context_pc_addr(context));
255     inst = pc[0];
256     end_addr = (inst >> 11) & 0x1f;
257     target = (inst >> 16) & 0x1f;
258
259     target_ptr = *os_context_register_addr(context, target);
260
261 #if 0
262     fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
263     fprintf(stderr, "boxed_region.free_pointer: %p\n", boxed_region.free_pointer);
264     fprintf(stderr, "boxed_region.end_addr: %p\n", boxed_region.end_addr);
265     fprintf(stderr, "target reg: %d, end_addr reg: %d\n", target, end_addr);
266     fprintf(stderr, "target: %x\n", *os_context_register_addr(context, target));
267     fprintf(stderr, "end_addr: %x\n", *os_context_register_addr(context, end_addr));
268 #endif
269
270 #if 0
271     fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
272     fprintf(stderr, "  trap inst = 0x%08x\n", inst);
273     fprintf(stderr, "  target reg = %s\n", lisp_register_names[target]);
274 #endif
275
276     /*
277      * Go back and look at the add/addi instruction.  The second src arg
278      * is the size of the allocation.  Get it and call alloc to allocate
279      * new space.
280      */
281     inst = pc[-3];
282     opcode = inst >> 26;
283 #if 0
284     fprintf(stderr, "  add inst  = 0x%08x, opcode = %d\n", inst, opcode);
285 #endif
286     if (opcode == 14) {
287         /*
288          * ADDI temp-tn, alloc-tn, size
289          *
290          * Extract the size
291          */
292         size = (inst & 0xffff);
293     } else if (opcode == 31) {
294         /*
295          * ADD temp-tn, alloc-tn, size-tn
296          *
297          * Extract the size
298          */
299         int reg;
300
301         reg = (inst >> 11) & 0x1f;
302 #if 0
303         fprintf(stderr, "  add, reg = %s\n", lisp_register_names[reg]);
304 #endif
305         size = *os_context_register_addr(context, reg);
306
307     }
308
309 #if 0
310     fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
311 #endif
312
313 #if INLINE_ALLOC_DEBUG
314     if ((((unsigned long)boxed_region.end_addr + size) / PAGE_SIZE) ==
315         (((unsigned long)boxed_region.end_addr) / PAGE_SIZE)) {
316       fprintf(stderr,"*** possibly bogus trap allocation of %d bytes at %p\n",
317               size, target_ptr);
318       fprintf(stderr, "    dynamic_space_free_pointer: %p, boxed_region.end_addr %p\n",
319               dynamic_space_free_pointer, boxed_region.end_addr);
320     }
321 #endif
322
323 #if 0
324     fprintf(stderr, "Ready to alloc\n");
325     fprintf(stderr, "free_pointer = 0x%08x\n",
326             dynamic_space_free_pointer);
327 #endif
328
329     /*
330      * alloc-tn was incremented by size.  Need to decrement it by size
331      * to restore its original value. This is not true on GENCGC
332      * anymore. d_s_f_p and reg_alloc get out of sync, but the p_a
333      * bits stay intact and we set it to the proper value when it
334      * needs to be. Keep this comment here for the moment in case
335      * somebody tries to figure out what happened here.
336      */
337     /*    dynamic_space_free_pointer =
338         (lispobj *) ((long) dynamic_space_free_pointer - size);
339     */
340 #if 0
341     fprintf(stderr, "free_pointer = 0x%08x new\n",
342             dynamic_space_free_pointer);
343 #endif
344
345     memory = (char *) alloc(size);
346
347 #if 0
348     fprintf(stderr, "alloc returned %p\n", memory);
349     fprintf(stderr, "free_pointer = 0x%08x\n",
350             dynamic_space_free_pointer);
351 #endif
352
353     /*
354      * The allocation macro wants the result to point to the end of the
355      * object!
356      */
357     memory += size;
358
359 #if 0
360     fprintf(stderr, "object end at %p\n", memory);
361 #endif
362
363     *os_context_register_addr(context, target) = (unsigned long) memory;
364     *os_context_register_addr(context, reg_ALLOC) =
365       (unsigned long) dynamic_space_free_pointer
366       | (*os_context_register_addr(context, reg_ALLOC)
367          & LOWTAG_MASK);
368
369     if (were_in_lisp) {
370         undo_fake_foreign_function_call(context);
371     }
372
373
374 }
375 #endif
376
377 void
378 arch_handle_breakpoint(os_context_t *context)
379 {
380     handle_breakpoint(context);
381 }
382
383 void
384 arch_handle_fun_end_breakpoint(os_context_t *context)
385 {
386     *os_context_pc_addr(context)
387         =(int)handle_fun_end_breakpoint(context);
388 }
389
390 /* FIXME: AFTER-BREAKPOINT-TRAP is defined for PPC, but never
391  * emitted as far as I can see. Should it be emitted, do removed
392  * entirely? */
393 void
394 arch_handle_after_breakpoint(os_context_t *context)
395 {
396     *skipped_break_addr = trap_Breakpoint;
397     skipped_break_addr = NULL;
398     *(unsigned int *)*os_context_pc_addr(context)
399         = displaced_after_inst;
400     *os_context_sigmask_addr(context)= orig_sigmask;
401     os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
402                     sizeof(unsigned int));
403 }
404
405 void
406 arch_handle_single_step_trap(os_context_t *context, int trap)
407 {
408     unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
409     int register_offset = code >> 5 & 0x1f;
410     handle_single_step_trap(context, trap, register_offset);
411     arch_skip_instruction(context);
412 }
413
414 static void
415 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
416 {
417     unsigned int code;
418
419 #ifdef LISP_FEATURE_LINUX
420     os_restore_fp_control(context);
421 #endif
422     code=*((u32 *)(*os_context_pc_addr(context)));
423     if (code == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
424         arch_clear_pseudo_atomic_interrupted(context);
425         arch_skip_instruction(context);
426         /* interrupt or GC was requested in PA; now we're done with the
427            PA section we may as well get around to it */
428         interrupt_handle_pending(context);
429         return;
430     }
431
432 #ifdef LISP_FEATURE_GENCGC
433     /* Is this an allocation trap? */
434     if (allocation_trap_p(context)) {
435         handle_allocation_trap(context);
436         arch_skip_instruction(context);
437 #ifdef LISP_FEATURE_DARWIN
438         DARWIN_FIX_CONTEXT(context);
439 #endif
440         return;
441     }
442 #endif
443
444     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
445         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
446         int trap = code & 0x1f;
447
448         if (!maybe_handle_trap(context,trap))
449             interrupt_handle_now(signal, siginfo, context);
450
451 #ifdef LISP_FEATURE_DARWIN
452         DARWIN_FIX_CONTEXT(context);
453 #endif
454         return;
455     }
456     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
457         interrupt_internal_error(context, 0);
458 #ifdef LISP_FEATURE_DARWIN
459         DARWIN_FIX_CONTEXT(context);
460 #endif
461         return;
462     }
463
464     interrupt_handle_now(signal, code, context);
465 #ifdef LISP_FEATURE_DARWIN
466     /* Work around G5 bug */
467     DARWIN_FIX_CONTEXT(context);
468 #endif
469 }
470
471
472 void arch_install_interrupt_handlers()
473 {
474     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
475     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
476 }
477
478
479 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
480
481 lispobj funcall0(lispobj function)
482 {
483     lispobj *args = current_control_stack_pointer;
484
485     return call_into_lisp(function, args, 0);
486 }
487
488 lispobj funcall1(lispobj function, lispobj arg0)
489 {
490     lispobj *args = current_control_stack_pointer;
491
492     current_control_stack_pointer += 1;
493     args[0] = arg0;
494
495     return call_into_lisp(function, args, 1);
496 }
497
498 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
499 {
500     lispobj *args = current_control_stack_pointer;
501
502     current_control_stack_pointer += 2;
503     args[0] = arg0;
504     args[1] = arg1;
505
506     return call_into_lisp(function, args, 2);
507 }
508
509 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
510 {
511     lispobj *args = current_control_stack_pointer;
512
513     current_control_stack_pointer += 3;
514     args[0] = arg0;
515     args[1] = arg1;
516     args[2] = arg2;
517
518     return call_into_lisp(function, args, 3);
519 }
520
521 void
522 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
523 {
524   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
525   extern void ppc_flush_cache_line(os_vm_address_t);
526
527   while (address < end) {
528     ppc_flush_cache_line(address);
529     address += 32;
530   }
531 }
532
533 #ifdef LISP_FEATURE_LINKAGE_TABLE
534
535 /* Linkage tables for PowerPC
536  *
537  * Linkage entry size is 16, because we need at least 4 instructions to
538  * implement a jump.
539  */
540
541 /*
542  * Define the registers to use in the linkage jump table. Can be the
543  * same. Some care must be exercised when choosing these. It has to be
544  * a register that is not otherwise being used. reg_NFP is a good
545  * choice. call_into_c trashes reg_NFP without preserving it, so we can
546  * trash it in the linkage jump table.
547  */
548 #define LINKAGE_TEMP_REG        reg_NFP
549 #define LINKAGE_ADDR_REG        reg_NFP
550
551 /*
552  * Insert the necessary jump instructions at the given address.
553  */
554 void
555 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
556 {
557   /*
558    * Make JMP to function entry.
559    *
560    * The instruction sequence is:
561    *
562    *        addis 13, 0, (hi part of addr)
563    *        ori   13, 13, (low part of addr)
564    *        mtctr 13
565    *        bctr
566    *
567    */
568   int* inst_ptr;
569   unsigned long hi;                   /* Top 16 bits of address */
570   unsigned long lo;                   /* Low 16 bits of address */
571   unsigned int inst;
572
573   inst_ptr = (int*) reloc_addr;
574
575   /*
576    * Split the target address into hi and lo parts for the sethi
577    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
578    */
579   hi = (unsigned long) target_addr;
580   lo = hi & 0xffff;
581   hi >>= 16;
582
583   /*
584    * addis 13, 0, (hi part)
585    */
586
587   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
588   *inst_ptr++ = inst;
589
590   /*
591    * ori 13, 13, (lo part)
592    */
593
594   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
595   *inst_ptr++ = inst;
596
597   /*
598    * mtctr 13
599    */
600
601   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
602   *inst_ptr++ = inst;
603
604   /*
605    * bctr
606    */
607
608   inst = (19 << 26) | (20 << 21) | (528 << 1);
609   *inst_ptr++ = inst;
610
611
612   *inst_ptr++ = inst;
613
614   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
615 }
616
617 void
618 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
619 {
620     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
621 }
622
623 #endif