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