1.0.26.16: fix gencgc on ppc
[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 "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"
26
27 #if defined(LISP_FEATURE_GENCGC)
28 #include "gencgc-alloc-region.h"
29 #endif
30
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
33
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.
37
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.)
41
42      Caveat callers.  */
43
44 #if defined (LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_LINUX)
45 #ifndef PT_DAR
46 #define PT_DAR          41
47 #endif
48
49 #ifndef PT_DSISR
50 #define PT_DSISR        42
51 #endif
52 #endif
53
54 void arch_init() {
55 }
56
57 os_vm_address_t
58 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
59 {
60     os_vm_address_t addr;
61
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;
68 }
69
70
71 void
72 arch_skip_instruction(os_context_t *context)
73 {
74     char** pcptr;
75     pcptr = (char**) os_context_pc_addr(context);
76     *pcptr += 4;
77 }
78
79 unsigned char *
80 arch_internal_error_arguments(os_context_t *context)
81 {
82     return (unsigned char *)(*os_context_pc_addr(context)+4);
83 }
84
85
86 boolean
87 arch_pseudo_atomic_atomic(os_context_t *context)
88 {
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
95      *
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);
101 }
102
103 void
104 arch_set_pseudo_atomic_interrupted(os_context_t *context)
105 {
106     *os_context_register_addr(context,reg_ALLOC) |= 1;
107 }
108
109 void
110 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
111 {
112     *os_context_register_addr(context,reg_ALLOC) &= ~1;
113 }
114
115 unsigned int
116 arch_install_breakpoint(void *pc)
117 {
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;
123 }
124
125 void
126 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
127 {
128     *(unsigned int *)pc = orig_inst;
129     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
130 }
131
132 /*
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.
138  *
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
146  *
147  * Clear?
148  */
149 static unsigned int *skipped_break_addr, displaced_after_inst;
150 static sigset_t orig_sigmask;
151
152 void
153 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
154 {
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));
158
159     orig_sigmask = *os_context_sigmask_addr(context);
160     sigaddset_blockable(os_context_sigmask_addr(context));
161
162     *pc = orig_inst;
163     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
164     skipped_break_addr = pc;
165
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 */
169 }
170
171 #ifdef LISP_FEATURE_GENCGC
172 /*
173  * Return non-zero if the current instruction is an allocation trap
174  */
175 static int
176 allocation_trap_p(os_context_t * context)
177 {
178     int result;
179     unsigned int *pc;
180     unsigned inst;
181     unsigned opcode;
182     unsigned src;
183     unsigned dst;
184
185     result = 0;
186
187     /*
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
192      */
193     pc = (unsigned int *) (*os_context_pc_addr(context));
194     inst = *pc;
195
196 #if 0
197     fprintf(stderr, "allocation_trap_p at %p:  inst = 0x%08x\n", pc, inst);
198 #endif
199
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))) {
205         /*
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.
209          */
210         unsigned int add_inst;
211
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);
226         }
227     }
228     return 0;
229 }
230
231 extern struct alloc_region boxed_region;
232
233 void
234 handle_allocation_trap(os_context_t * context)
235 {
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;
243
244     target = 0;
245     size = 0;
246
247 #if 0
248     fprintf(stderr, "In handle_allocation_trap\n");
249 #endif
250
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;
254
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");
259     }
260
261     /*
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.
265      *
266      * We need to adjust temp and alloc-tn.
267      */
268
269     pc = (unsigned int *) (*os_context_pc_addr(context));
270     inst = pc[0];
271     end_addr = (inst >> 11) & 0x1f;
272     target = (inst >> 16) & 0x1f;
273
274     target_ptr = *os_context_register_addr(context, target);
275
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
284
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
290
291     /*
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.
295      */
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) {
302         /*
303          * ADDI temp-tn, alloc-tn, size
304          *
305          * Extract the size
306          */
307         size = (inst & 0xffff);
308     } else if (opcode == 31) {
309         /*
310          * ADD temp-tn, alloc-tn, size-tn
311          *
312          * Extract the size
313          */
314         int reg;
315
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);
321
322     }
323
324 #if 0
325     fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
326 #endif
327
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);
335     }
336 #endif
337
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
343
344     /*
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.
351      */
352     /*    dynamic_space_free_pointer =
353         (lispobj *) ((long) dynamic_space_free_pointer - size);
354     */
355 #if 0
356     fprintf(stderr, "free_pointer = 0x%08x new\n",
357             dynamic_space_free_pointer);
358 #endif
359
360     {
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;
366     }
367
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
373
374     /*
375      * The allocation macro wants the result to point to the end of the
376      * object!
377      */
378     memory += size;
379
380 #if 0
381     fprintf(stderr, "object end at %p\n", memory);
382 #endif
383
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);
389
390     if (were_in_lisp) {
391         undo_fake_foreign_function_call(context);
392     }
393
394
395 }
396 #endif
397
398 void
399 arch_handle_breakpoint(os_context_t *context)
400 {
401     handle_breakpoint(context);
402 }
403
404 void
405 arch_handle_fun_end_breakpoint(os_context_t *context)
406 {
407     *os_context_pc_addr(context)
408         =(int)handle_fun_end_breakpoint(context);
409 }
410
411 void
412 arch_handle_after_breakpoint(os_context_t *context)
413 {
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));
421 }
422
423 void
424 arch_handle_single_step_trap(os_context_t *context, int trap)
425 {
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);
430 }
431
432 static void
433 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
434 {
435     unsigned int code;
436
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;
445     }
446
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;
453     }
454 #endif
455
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;
461     }
462     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
463         interrupt_internal_error(context, 0);
464         return;
465     }
466
467     interrupt_handle_now(signal, (siginfo_t *)code, context);
468 }
469
470
471 void arch_install_interrupt_handlers()
472 {
473     undoably_install_low_level_interrupt_handler(SIGILL, sigtrap_handler);
474     undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
475 }
476
477 void
478 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
479 {
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);
482
483   while (address < end) {
484     ppc_flush_cache_line(address);
485     address += 32;
486   }
487 }
488
489 #ifdef LISP_FEATURE_LINKAGE_TABLE
490
491 /* Linkage tables for PowerPC
492  *
493  * Linkage entry size is 16, because we need at least 4 instructions to
494  * implement a jump.
495  */
496
497 /*
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.
503  */
504 #define LINKAGE_TEMP_REG        reg_NFP
505 #define LINKAGE_ADDR_REG        reg_NFP
506
507 /*
508  * Insert the necessary jump instructions at the given address.
509  */
510 void
511 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
512 {
513   /*
514    * Make JMP to function entry.
515    *
516    * The instruction sequence is:
517    *
518    *        addis 13, 0, (hi part of addr)
519    *        ori   13, 13, (low part of addr)
520    *        mtctr 13
521    *        bctr
522    *
523    */
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;
528
529   inst_ptr = (int*) reloc_addr;
530
531   /*
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.
534    */
535   hi = (unsigned long) target_addr;
536   lo = hi & 0xffff;
537   hi >>= 16;
538
539   /*
540    * addis 13, 0, (hi part)
541    */
542
543   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
544   *inst_ptr++ = inst;
545
546   /*
547    * ori 13, 13, (lo part)
548    */
549
550   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
551   *inst_ptr++ = inst;
552
553   /*
554    * mtctr 13
555    */
556
557   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
558   *inst_ptr++ = inst;
559
560   /*
561    * bctr
562    */
563
564   inst = (19 << 26) | (20 << 21) | (528 << 1);
565   *inst_ptr++ = inst;
566
567
568   *inst_ptr++ = inst;
569
570   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
571 }
572
573 void
574 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
575 {
576     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
577 }
578
579 #endif