56fefd00dd1f3f7c11d0b6bed9a6aee95512ba86
[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   /* The header files may not define PT_DAR/PT_DSISR.  This definition
14      is correct for all versions of ppc linux >= 2.0.30
15
16      As of DR2.1u4, MkLinux doesn't pass these registers to signal
17      handlers correctly; a patch is necessary in order to (partially)
18      correct this.
19
20      Even with the patch, the DSISR may not have its 'write' bit set
21      correctly (it tends not to be set if the fault was caused by
22      something other than a protection violation.)
23
24      Caveat callers.  */
25
26 #ifndef PT_DAR
27 #define PT_DAR          41
28 #endif
29
30 #ifndef PT_DSISR
31 #define PT_DSISR        42
32 #endif
33
34 void arch_init() {
35 }
36
37 os_vm_address_t
38 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
39 {
40     unsigned int *pc =  (unsigned int *)(*os_context_pc_addr(context));
41     os_vm_address_t addr;
42
43
44     /* Make sure it's not the pc thats bogus, and that it was lisp code */
45     /* that caused the fault. */
46     if ((((unsigned long)pc) & 3) != 0 ||
47         ((pc < READ_ONLY_SPACE_START ||
48           pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
49          ((lispobj *)pc < current_dynamic_space ||
50           (lispobj *)pc >= current_dynamic_space + DYNAMIC_SPACE_SIZE)))
51         return 0;
52
53
54     addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
55     return addr;
56 }
57
58
59 void
60 arch_skip_instruction(os_context_t *context)
61 {
62     char** pcptr;
63     pcptr = (char**) os_context_pc_addr(context);
64     *pcptr += 4;
65 }
66
67 unsigned char *
68 arch_internal_error_arguments(os_context_t *context)
69 {
70     return (unsigned char *)(*os_context_pc_addr(context)+4);
71 }
72
73
74 boolean
75 arch_pseudo_atomic_atomic(os_context_t *context)
76 {
77     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
78 }
79
80 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
81
82 void
83 arch_set_pseudo_atomic_interrupted(os_context_t *context)
84 {
85     *os_context_register_addr(context,reg_NL3)
86         += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
87 }
88
89 unsigned int
90 arch_install_breakpoint(void *pc)
91 {
92     unsigned int *ptr = (unsigned int *)pc;
93     unsigned int result = *ptr;
94     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
95     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
96     return result;
97 }
98
99 void
100 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
101 {
102     *(unsigned int *)pc = orig_inst;
103     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
104 }
105
106 /*
107  * Perform the instruction that we overwrote with a breakpoint.  As we
108  * don't have a single-step facility, this means we have to:
109  * - put the instruction back
110  * - put a second breakpoint at the following instruction,
111  *   set after_breakpoint and continue execution.
112  *
113  * When the second breakpoint is hit (very shortly thereafter, we hope)
114  * sigtrap_handler gets called again, but follows the AfterBreakpoint
115  * arm, which
116  * - puts a bpt back in the first breakpoint place (running across a
117  *   breakpoint shouldn't cause it to be uninstalled)
118  * - replaces the second bpt with the instruction it was meant to be
119  * - carries on
120  *
121  * Clear?
122  */
123 static unsigned int *skipped_break_addr, displaced_after_inst;
124 static sigset_t orig_sigmask;
125
126 void
127 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
128 {
129     /* not sure how we ensure that we get the breakpoint reinstalled
130      * after doing this -dan */
131     unsigned int *pc = (unsigned int *)(*os_context_pc_addr(context));
132
133     orig_sigmask = *os_context_sigmask_addr(context);
134     sigaddset_blockable(os_context_sigmask_addr(context));
135
136     *pc = orig_inst;
137     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
138     skipped_break_addr = pc;
139 }
140
141 static void
142 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
143 {
144     unsigned int code;
145 #ifdef LISP_FEATURE_LINUX
146     os_restore_fp_control(context);
147 #endif
148     code=*((u32 *)(*os_context_pc_addr(context)));
149     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
150         /* twlti reg_ALLOC,0 - check for deferred interrupt */
151         *os_context_register_addr(context,reg_ALLOC)
152             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
153         arch_skip_instruction(context);
154         /* interrupt or GC was requested in PA; now we're done with the
155            PA section we may as well get around to it */
156         interrupt_handle_pending(context);
157         return;
158
159     }
160     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
161         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
162         int trap = code & 0x1f;
163
164         switch (trap) {
165         case trap_Halt:
166             fake_foreign_function_call(context);
167             lose("%%primitive halt called; the party is over.\n");
168
169         case trap_Error:
170         case trap_Cerror:
171             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
172             break;
173
174         case trap_PendingInterrupt:
175             /* This is supposed run after WITHOUT-INTERRUPTS if there
176              * were pending signals. */
177             arch_skip_instruction(context);
178             interrupt_handle_pending(context);
179             break;
180
181         case trap_Breakpoint:
182             handle_breakpoint(signal, code, context);
183             break;
184
185         case trap_FunEndBreakpoint:
186             *os_context_pc_addr(context)
187                 =(int)handle_fun_end_breakpoint(signal, code, context);
188             break;
189
190         case trap_AfterBreakpoint:
191             *skipped_break_addr = trap_Breakpoint;
192             skipped_break_addr = NULL;
193             *(unsigned int *)*os_context_pc_addr(context)
194                 = displaced_after_inst;
195             *os_context_sigmask_addr(context)= orig_sigmask;
196
197             os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
198                             sizeof(unsigned int));
199             break;
200
201         default:
202             interrupt_handle_now(signal, code, context);
203             break;
204         }
205 #ifdef LISP_FEATURE_DARWIN
206         DARWIN_FIX_CONTEXT(context);
207 #endif
208         return;
209     }
210     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
211         interrupt_internal_error(signal, code, context, 0);
212 #ifdef LISP_FEATURE_DARWIN
213         DARWIN_FIX_CONTEXT(context);
214 #endif
215         return;
216     }
217
218     interrupt_handle_now(signal, code, context);
219 #ifdef LISP_FEATURE_DARWIN
220     /* Work around G5 bug */
221     DARWIN_FIX_CONTEXT(context);
222 #endif
223 }
224
225
226 void arch_install_interrupt_handlers()
227 {
228     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
229     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
230 }
231
232
233 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
234
235 lispobj funcall0(lispobj function)
236 {
237     lispobj *args = current_control_stack_pointer;
238
239     return call_into_lisp(function, args, 0);
240 }
241
242 lispobj funcall1(lispobj function, lispobj arg0)
243 {
244     lispobj *args = current_control_stack_pointer;
245
246     current_control_stack_pointer += 1;
247     args[0] = arg0;
248
249     return call_into_lisp(function, args, 1);
250 }
251
252 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
253 {
254     lispobj *args = current_control_stack_pointer;
255
256     current_control_stack_pointer += 2;
257     args[0] = arg0;
258     args[1] = arg1;
259
260     return call_into_lisp(function, args, 2);
261 }
262
263 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
264 {
265     lispobj *args = current_control_stack_pointer;
266
267     current_control_stack_pointer += 3;
268     args[0] = arg0;
269     args[1] = arg1;
270     args[2] = arg2;
271
272     return call_into_lisp(function, args, 3);
273 }
274
275 void
276 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
277 {
278   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
279   extern void ppc_flush_cache_line(os_vm_address_t);
280
281   while (address < end) {
282     ppc_flush_cache_line(address);
283     address += 32;
284   }
285 }
286
287 #ifdef LISP_FEATURE_LINKAGE_TABLE
288
289 /* Linkage tables for PowerPC
290  *
291  * Linkage entry size is 16, because we need at least 4 instructions to
292  * implement a jump.
293  */
294
295 /*
296  * Define the registers to use in the linkage jump table. Can be the
297  * same. Some care must be exercised when choosing these. It has to be
298  * a register that is not otherwise being used. reg_NFP is a good
299  * choice. call_into_c trashes reg_NFP without preserving it, so we can
300  * trash it in the linkage jump table.
301  */
302 #define LINKAGE_TEMP_REG        reg_NFP
303 #define LINKAGE_ADDR_REG        reg_NFP
304
305 /*
306  * Insert the necessary jump instructions at the given address.
307  */
308 void
309 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
310 {
311   /*
312    * Make JMP to function entry.
313    *
314    * The instruction sequence is:
315    *
316    *        addis 13, 0, (hi part of addr)
317    *        ori   13, 13, (low part of addr)
318    *        mtctr 13
319    *        bctr
320    *
321    */
322   int* inst_ptr;
323   unsigned long hi;                   /* Top 16 bits of address */
324   unsigned long lo;                   /* Low 16 bits of address */
325   unsigned int inst;
326
327   inst_ptr = (int*) reloc_addr;
328
329   /*
330    * Split the target address into hi and lo parts for the sethi
331    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
332    */
333   hi = (unsigned long) target_addr;
334   lo = hi & 0xffff;
335   hi >>= 16;
336
337   /*
338    * addis 13, 0, (hi part)
339    */
340
341   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
342   *inst_ptr++ = inst;
343
344   /*
345    * ori 13, 13, (lo part)
346    */
347
348   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
349   *inst_ptr++ = inst;
350
351   /*
352    * mtctr 13
353    */
354
355   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
356   *inst_ptr++ = inst;
357
358   /*
359    * bctr
360    */
361
362   inst = (19 << 26) | (20 << 21) | (528 << 1);
363   *inst_ptr++ = inst;
364
365
366   *inst_ptr++ = inst;
367
368   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
369 }
370
371 void
372 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
373 {
374     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
375 }
376
377 #endif