1.0.5.49: interrupt & GC & PA 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     /* FIXME: this foreign_function_call_active test is dubious at
88      * best. If a foreign call is made in a pseudo atomic section
89      * (?) or more likely a pseudo atomic section is in a foreign
90      * call then an interrupt is executed immediately. Maybe it
91      * has to do with C code not maintaining pseudo atomic
92      * properly. MG - 2005-08-10
93      *
94      * The foreign_function_call_active used to live at each call-site
95      * to arch_pseudo_atomic_atomic, but this seems clearer.
96      * --NS 2007-05-15 */
97     return (!foreign_function_call_active)
98         && ((*os_context_register_addr(context,reg_ALLOC)) & 4);
99 }
100
101 void
102 arch_set_pseudo_atomic_interrupted(os_context_t *context)
103 {
104     *os_context_register_addr(context,reg_ALLOC) |= 1;
105 }
106
107 void
108 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
109 {
110     *os_context_register_addr(context,reg_ALLOC) &= ~1;
111 }
112
113 unsigned int
114 arch_install_breakpoint(void *pc)
115 {
116     unsigned int *ptr = (unsigned int *)pc;
117     unsigned int result = *ptr;
118     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
119     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
120     return result;
121 }
122
123 void
124 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
125 {
126     *(unsigned int *)pc = orig_inst;
127     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
128 }
129
130 /*
131  * Perform the instruction that we overwrote with a breakpoint.  As we
132  * don't have a single-step facility, this means we have to:
133  * - put the instruction back
134  * - put a second breakpoint at the following instruction,
135  *   set after_breakpoint and continue execution.
136  *
137  * When the second breakpoint is hit (very shortly thereafter, we hope)
138  * sigtrap_handler gets called again, but follows the AfterBreakpoint
139  * arm, which
140  * - puts a bpt back in the first breakpoint place (running across a
141  *   breakpoint shouldn't cause it to be uninstalled)
142  * - replaces the second bpt with the instruction it was meant to be
143  * - carries on
144  *
145  * Clear?
146  */
147 static unsigned int *skipped_break_addr, displaced_after_inst;
148 static sigset_t orig_sigmask;
149
150 void
151 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
152 {
153     /* not sure how we ensure that we get the breakpoint reinstalled
154      * after doing this -dan */
155     unsigned int *pc = (unsigned int *)(*os_context_pc_addr(context));
156
157     orig_sigmask = *os_context_sigmask_addr(context);
158     sigaddset_blockable(os_context_sigmask_addr(context));
159
160     *pc = orig_inst;
161     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
162     skipped_break_addr = pc;
163
164     /* FIXME: we should apparently be installing the after-breakpoint
165      * here, but would need to find the next instruction address for
166      * it first. alpha-arch.c shows how to do it. --NS 2007-04-02 */
167 }
168
169 #ifdef LISP_FEATURE_GENCGC
170 /*
171  * Return non-zero if the current instruction is an allocation trap
172  */
173 static int
174 allocation_trap_p(os_context_t * context)
175 {
176     int result;
177     unsigned int *pc;
178     unsigned inst;
179     unsigned opcode;
180     unsigned src;
181     unsigned dst;
182
183     result = 0;
184
185     /*
186      * First, the instruction has to be a TWLGE temp, NL3, which has the
187      * format.
188      * | 6| 5| 5 | 5 | 10|1|  width
189      * |31|5 |dst|src|  4|0|  field
190      */
191     pc = (unsigned int *) (*os_context_pc_addr(context));
192     inst = *pc;
193
194 #if 0
195     fprintf(stderr, "allocation_trap_p at %p:  inst = 0x%08x\n", pc, inst);
196 #endif
197
198     opcode = inst >> 26;
199     src = (inst >> 11) & 0x1f;
200     dst = (inst >> 16) & 0x1f;
201     if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
202         && (4 == ((inst >> 1) & 0x3ff))) {
203         /*
204          * We got the instruction.  Now, look back to make sure it was
205          * proceeded by what we expected.  2 instructions back should be
206          * an ADD or ADDI instruction.
207          */
208         unsigned int add_inst;
209
210         add_inst = pc[-3];
211 #if 0
212         fprintf(stderr, "   add inst at %p:  inst = 0x%08x\n",
213                 pc - 3, add_inst);
214 #endif
215         opcode = add_inst >> 26;
216         if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
217             return 1;
218         } else if ((opcode == 14)) {
219             return 1;
220         } else {
221             fprintf(stderr,
222                     "Whoa! Got allocation trap but could not find ADD or ADDI instruction: 0x%08x in the proper place\n",
223                     add_inst);
224         }
225     }
226     return 0;
227 }
228
229 extern struct alloc_region boxed_region;
230
231 void
232 handle_allocation_trap(os_context_t * context)
233 {
234     unsigned int *pc;
235     unsigned int inst;
236     unsigned int target, target_ptr, end_addr;
237     unsigned int opcode;
238     int size;
239     boolean were_in_lisp;
240     char *memory;
241
242     target = 0;
243     size = 0;
244
245 #if 0
246     fprintf(stderr, "In handle_allocation_trap\n");
247 #endif
248
249     /* I don't think it's possible for us NOT to be in lisp when we get
250      * here.  Remove this later? */
251     were_in_lisp = !foreign_function_call_active;
252
253     if (were_in_lisp) {
254         fake_foreign_function_call(context);
255     } else {
256         fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
257     }
258
259     /*
260      * Look at current instruction: TWNE temp, NL3. We're here because
261      * temp > NL3 and temp is the end of the allocation, and NL3 is
262      * current-region-end-addr.
263      *
264      * We need to adjust temp and alloc-tn.
265      */
266
267     pc = (unsigned int *) (*os_context_pc_addr(context));
268     inst = pc[0];
269     end_addr = (inst >> 11) & 0x1f;
270     target = (inst >> 16) & 0x1f;
271
272     target_ptr = *os_context_register_addr(context, target);
273
274 #if 0
275     fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
276     fprintf(stderr, "boxed_region.free_pointer: %p\n", boxed_region.free_pointer);
277     fprintf(stderr, "boxed_region.end_addr: %p\n", boxed_region.end_addr);
278     fprintf(stderr, "target reg: %d, end_addr reg: %d\n", target, end_addr);
279     fprintf(stderr, "target: %x\n", *os_context_register_addr(context, target));
280     fprintf(stderr, "end_addr: %x\n", *os_context_register_addr(context, end_addr));
281 #endif
282
283 #if 0
284     fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
285     fprintf(stderr, "  trap inst = 0x%08x\n", inst);
286     fprintf(stderr, "  target reg = %s\n", lisp_register_names[target]);
287 #endif
288
289     /*
290      * Go back and look at the add/addi instruction.  The second src arg
291      * is the size of the allocation.  Get it and call alloc to allocate
292      * new space.
293      */
294     inst = pc[-3];
295     opcode = inst >> 26;
296 #if 0
297     fprintf(stderr, "  add inst  = 0x%08x, opcode = %d\n", inst, opcode);
298 #endif
299     if (opcode == 14) {
300         /*
301          * ADDI temp-tn, alloc-tn, size
302          *
303          * Extract the size
304          */
305         size = (inst & 0xffff);
306     } else if (opcode == 31) {
307         /*
308          * ADD temp-tn, alloc-tn, size-tn
309          *
310          * Extract the size
311          */
312         int reg;
313
314         reg = (inst >> 11) & 0x1f;
315 #if 0
316         fprintf(stderr, "  add, reg = %s\n", lisp_register_names[reg]);
317 #endif
318         size = *os_context_register_addr(context, reg);
319
320     }
321
322 #if 0
323     fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
324 #endif
325
326 #if INLINE_ALLOC_DEBUG
327     if ((((unsigned long)boxed_region.end_addr + size) / PAGE_SIZE) ==
328         (((unsigned long)boxed_region.end_addr) / PAGE_SIZE)) {
329       fprintf(stderr,"*** possibly bogus trap allocation of %d bytes at %p\n",
330               size, target_ptr);
331       fprintf(stderr, "    dynamic_space_free_pointer: %p, boxed_region.end_addr %p\n",
332               dynamic_space_free_pointer, boxed_region.end_addr);
333     }
334 #endif
335
336 #if 0
337     fprintf(stderr, "Ready to alloc\n");
338     fprintf(stderr, "free_pointer = 0x%08x\n",
339             dynamic_space_free_pointer);
340 #endif
341
342     /*
343      * alloc-tn was incremented by size.  Need to decrement it by size
344      * to restore its original value. This is not true on GENCGC
345      * anymore. d_s_f_p and reg_alloc get out of sync, but the p_a
346      * bits stay intact and we set it to the proper value when it
347      * needs to be. Keep this comment here for the moment in case
348      * somebody tries to figure out what happened here.
349      */
350     /*    dynamic_space_free_pointer =
351         (lispobj *) ((long) dynamic_space_free_pointer - size);
352     */
353 #if 0
354     fprintf(stderr, "free_pointer = 0x%08x new\n",
355             dynamic_space_free_pointer);
356 #endif
357
358     memory = (char *) alloc(size);
359
360 #if 0
361     fprintf(stderr, "alloc returned %p\n", memory);
362     fprintf(stderr, "free_pointer = 0x%08x\n",
363             dynamic_space_free_pointer);
364 #endif
365
366     /*
367      * The allocation macro wants the result to point to the end of the
368      * object!
369      */
370     memory += size;
371
372 #if 0
373     fprintf(stderr, "object end at %p\n", memory);
374 #endif
375
376     *os_context_register_addr(context, target) = (unsigned long) memory;
377     *os_context_register_addr(context, reg_ALLOC) =
378       (unsigned long) dynamic_space_free_pointer
379       | (*os_context_register_addr(context, reg_ALLOC)
380          & LOWTAG_MASK);
381
382     if (were_in_lisp) {
383         undo_fake_foreign_function_call(context);
384     }
385
386
387 }
388 #endif
389
390 void
391 arch_handle_breakpoint(os_context_t *context)
392 {
393     handle_breakpoint(context);
394 }
395
396 void
397 arch_handle_fun_end_breakpoint(os_context_t *context)
398 {
399     *os_context_pc_addr(context)
400         =(int)handle_fun_end_breakpoint(context);
401 }
402
403 void
404 arch_handle_after_breakpoint(os_context_t *context)
405 {
406     *skipped_break_addr = trap_Breakpoint;
407     skipped_break_addr = NULL;
408     *(unsigned int *)*os_context_pc_addr(context)
409         = displaced_after_inst;
410     *os_context_sigmask_addr(context)= orig_sigmask;
411     os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
412                     sizeof(unsigned int));
413 }
414
415 void
416 arch_handle_single_step_trap(os_context_t *context, int trap)
417 {
418     unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
419     int register_offset = code >> 5 & 0x1f;
420     handle_single_step_trap(context, trap, register_offset);
421     arch_skip_instruction(context);
422 }
423
424 static void
425 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
426 {
427     unsigned int code;
428
429 #ifdef LISP_FEATURE_LINUX
430     os_restore_fp_control(context);
431 #endif
432     code=*((u32 *)(*os_context_pc_addr(context)));
433     if (code == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
434         arch_clear_pseudo_atomic_interrupted(context);
435         arch_skip_instruction(context);
436         /* interrupt or GC was requested in PA; now we're done with the
437            PA section we may as well get around to it */
438         interrupt_handle_pending(context);
439         return;
440     }
441
442 #ifdef LISP_FEATURE_GENCGC
443     /* Is this an allocation trap? */
444     if (allocation_trap_p(context)) {
445         handle_allocation_trap(context);
446         arch_skip_instruction(context);
447 #ifdef LISP_FEATURE_DARWIN
448         DARWIN_FIX_CONTEXT(context);
449 #endif
450         return;
451     }
452 #endif
453
454     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
455         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
456         int trap = code & 0x1f;
457         handle_trap(context,trap);
458
459 #ifdef LISP_FEATURE_DARWIN
460         DARWIN_FIX_CONTEXT(context);
461 #endif
462         return;
463     }
464     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
465         interrupt_internal_error(context, 0);
466 #ifdef LISP_FEATURE_DARWIN
467         DARWIN_FIX_CONTEXT(context);
468 #endif
469         return;
470     }
471
472     interrupt_handle_now(signal, code, context);
473 #ifdef LISP_FEATURE_DARWIN
474     /* Work around G5 bug */
475     DARWIN_FIX_CONTEXT(context);
476 #endif
477 }
478
479
480 void arch_install_interrupt_handlers()
481 {
482     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
483     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
484 }
485
486 void
487 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
488 {
489   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
490   extern void ppc_flush_cache_line(os_vm_address_t);
491
492   while (address < end) {
493     ppc_flush_cache_line(address);
494     address += 32;
495   }
496 }
497
498 #ifdef LISP_FEATURE_LINKAGE_TABLE
499
500 /* Linkage tables for PowerPC
501  *
502  * Linkage entry size is 16, because we need at least 4 instructions to
503  * implement a jump.
504  */
505
506 /*
507  * Define the registers to use in the linkage jump table. Can be the
508  * same. Some care must be exercised when choosing these. It has to be
509  * a register that is not otherwise being used. reg_NFP is a good
510  * choice. call_into_c trashes reg_NFP without preserving it, so we can
511  * trash it in the linkage jump table.
512  */
513 #define LINKAGE_TEMP_REG        reg_NFP
514 #define LINKAGE_ADDR_REG        reg_NFP
515
516 /*
517  * Insert the necessary jump instructions at the given address.
518  */
519 void
520 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
521 {
522   /*
523    * Make JMP to function entry.
524    *
525    * The instruction sequence is:
526    *
527    *        addis 13, 0, (hi part of addr)
528    *        ori   13, 13, (low part of addr)
529    *        mtctr 13
530    *        bctr
531    *
532    */
533   int* inst_ptr;
534   unsigned long hi;                   /* Top 16 bits of address */
535   unsigned long lo;                   /* Low 16 bits of address */
536   unsigned int inst;
537
538   inst_ptr = (int*) reloc_addr;
539
540   /*
541    * Split the target address into hi and lo parts for the sethi
542    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
543    */
544   hi = (unsigned long) target_addr;
545   lo = hi & 0xffff;
546   hi >>= 16;
547
548   /*
549    * addis 13, 0, (hi part)
550    */
551
552   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
553   *inst_ptr++ = inst;
554
555   /*
556    * ori 13, 13, (lo part)
557    */
558
559   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
560   *inst_ptr++ = inst;
561
562   /*
563    * mtctr 13
564    */
565
566   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
567   *inst_ptr++ = inst;
568
569   /*
570    * bctr
571    */
572
573   inst = (19 << 26) | (20 << 21) | (528 << 1);
574   *inst_ptr++ = inst;
575
576
577   *inst_ptr++ = inst;
578
579   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
580 }
581
582 void
583 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
584 {
585     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
586 }
587
588 #endif