0.9.16.38:
[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         case trap_SingleStepAround:
436         case trap_SingleStepBefore:
437             {
438                 int register_offset = code >> 5 & 0x1f;
439
440                 handle_single_step_trap(context, trap, register_offset);
441
442                 arch_skip_instruction(context);
443                 break;
444             }
445         default:
446             interrupt_handle_now(signal, code, context);
447             break;
448         }
449 #ifdef LISP_FEATURE_DARWIN
450         DARWIN_FIX_CONTEXT(context);
451 #endif
452         return;
453     }
454     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
455         interrupt_internal_error(signal, code, context, 0);
456 #ifdef LISP_FEATURE_DARWIN
457         DARWIN_FIX_CONTEXT(context);
458 #endif
459         return;
460     }
461
462     interrupt_handle_now(signal, code, context);
463 #ifdef LISP_FEATURE_DARWIN
464     /* Work around G5 bug */
465     DARWIN_FIX_CONTEXT(context);
466 #endif
467 }
468
469
470 void arch_install_interrupt_handlers()
471 {
472     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
473     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
474 }
475
476
477 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
478
479 lispobj funcall0(lispobj function)
480 {
481     lispobj *args = current_control_stack_pointer;
482
483     return call_into_lisp(function, args, 0);
484 }
485
486 lispobj funcall1(lispobj function, lispobj arg0)
487 {
488     lispobj *args = current_control_stack_pointer;
489
490     current_control_stack_pointer += 1;
491     args[0] = arg0;
492
493     return call_into_lisp(function, args, 1);
494 }
495
496 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
497 {
498     lispobj *args = current_control_stack_pointer;
499
500     current_control_stack_pointer += 2;
501     args[0] = arg0;
502     args[1] = arg1;
503
504     return call_into_lisp(function, args, 2);
505 }
506
507 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
508 {
509     lispobj *args = current_control_stack_pointer;
510
511     current_control_stack_pointer += 3;
512     args[0] = arg0;
513     args[1] = arg1;
514     args[2] = arg2;
515
516     return call_into_lisp(function, args, 3);
517 }
518
519 void
520 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
521 {
522   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
523   extern void ppc_flush_cache_line(os_vm_address_t);
524
525   while (address < end) {
526     ppc_flush_cache_line(address);
527     address += 32;
528   }
529 }
530
531 #ifdef LISP_FEATURE_LINKAGE_TABLE
532
533 /* Linkage tables for PowerPC
534  *
535  * Linkage entry size is 16, because we need at least 4 instructions to
536  * implement a jump.
537  */
538
539 /*
540  * Define the registers to use in the linkage jump table. Can be the
541  * same. Some care must be exercised when choosing these. It has to be
542  * a register that is not otherwise being used. reg_NFP is a good
543  * choice. call_into_c trashes reg_NFP without preserving it, so we can
544  * trash it in the linkage jump table.
545  */
546 #define LINKAGE_TEMP_REG        reg_NFP
547 #define LINKAGE_ADDR_REG        reg_NFP
548
549 /*
550  * Insert the necessary jump instructions at the given address.
551  */
552 void
553 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
554 {
555   /*
556    * Make JMP to function entry.
557    *
558    * The instruction sequence is:
559    *
560    *        addis 13, 0, (hi part of addr)
561    *        ori   13, 13, (low part of addr)
562    *        mtctr 13
563    *        bctr
564    *
565    */
566   int* inst_ptr;
567   unsigned long hi;                   /* Top 16 bits of address */
568   unsigned long lo;                   /* Low 16 bits of address */
569   unsigned int inst;
570
571   inst_ptr = (int*) reloc_addr;
572
573   /*
574    * Split the target address into hi and lo parts for the sethi
575    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
576    */
577   hi = (unsigned long) target_addr;
578   lo = hi & 0xffff;
579   hi >>= 16;
580
581   /*
582    * addis 13, 0, (hi part)
583    */
584
585   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
586   *inst_ptr++ = inst;
587
588   /*
589    * ori 13, 13, (lo part)
590    */
591
592   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
593   *inst_ptr++ = inst;
594
595   /*
596    * mtctr 13
597    */
598
599   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
600   *inst_ptr++ = inst;
601
602   /*
603    * bctr
604    */
605
606   inst = (19 << 26) | (20 << 21) | (528 << 1);
607   *inst_ptr++ = inst;
608
609
610   *inst_ptr++ = inst;
611
612   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
613 }
614
615 void
616 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
617 {
618     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
619 }
620
621 #endif