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