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