0.9.1.7: "fix" SB-SPROF on non-gencgc platforms
[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 long 
90 arch_install_breakpoint(void *pc)
91 {
92     unsigned long *ptr = (unsigned long *)pc;
93     unsigned long result = *ptr;
94     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
95     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
96     return result;
97 }
98
99 void 
100 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
101 {
102     *(unsigned long *)pc = orig_inst;
103     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
104 }
105
106 static unsigned long *skipped_break_addr, displaced_after_inst;
107 static sigset_t orig_sigmask;
108
109 void 
110 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
111 {
112     /* not sure how we ensure that we get the breakpoint reinstalled
113      * after doing this -dan */
114     unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
115     
116     orig_sigmask = *os_context_sigmask_addr(context);
117     sigaddset_blockable(os_context_sigmask_addr(context));
118     
119     *pc = orig_inst;
120     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
121     skipped_break_addr = pc;
122 }
123
124 static void 
125 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
126 {
127     u32 code;
128     sigset_t *mask;
129 #ifdef LISP_FEATURE_LINUX
130     os_restore_fp_control(context);
131 #endif
132     mask=(os_context_sigmask_addr(context));
133     sigsetmask(mask); 
134     code=*((u32 *)(*os_context_pc_addr(context)));
135     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
136         /* twlti reg_ALLOC,0 - check for deferred interrupt */
137         *os_context_register_addr(context,reg_ALLOC) 
138             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
139         arch_skip_instruction(context);
140         /* interrupt or GC was requested in PA; now we're done with the
141            PA section we may as well get around to it */
142         interrupt_handle_pending(context);
143         return;
144         
145     }
146     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
147         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
148         int trap = code & 0x1f;
149         
150         switch (trap) {
151         case trap_Halt:
152             fake_foreign_function_call(context);
153             lose("%%primitive halt called; the party is over.\n");
154             
155         case trap_Error:
156         case trap_Cerror:
157             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
158             break;
159             
160         case trap_PendingInterrupt:
161             /* This is supposed run after WITHOUT-INTERRUPTS if there
162              * were pending signals. */
163             arch_skip_instruction(context);
164             interrupt_handle_pending(context);
165             break;
166             
167         case trap_Breakpoint:
168             handle_breakpoint(signal, code, context);
169             break;
170             
171         case trap_FunEndBreakpoint:
172             *os_context_pc_addr(context)
173                 =(int)handle_fun_end_breakpoint(signal, code, context);
174             break;
175             
176         case trap_AfterBreakpoint:
177             *skipped_break_addr = trap_Breakpoint;
178             skipped_break_addr = NULL;
179             *(unsigned long *)*os_context_pc_addr(context) 
180                 = displaced_after_inst;
181             *os_context_sigmask_addr(context)= orig_sigmask;
182  
183             os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
184                             sizeof(unsigned long));
185             break;
186             
187         default:
188             interrupt_handle_now(signal, code, context);
189             break;
190         }
191 #ifdef LISP_FEATURE_DARWIN
192         DARWIN_FIX_CONTEXT(context);
193 #endif
194         return;
195     }
196     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
197         interrupt_internal_error(signal, code, context, 0);
198 #ifdef LISP_FEATURE_DARWIN
199         DARWIN_FIX_CONTEXT(context);
200 #endif
201         return;
202     }
203     
204     interrupt_handle_now(signal, code, context);
205 #ifdef LISP_FEATURE_DARWIN
206     /* Work around G5 bug */
207     DARWIN_FIX_CONTEXT(context);
208 #endif
209 }
210
211
212 void arch_install_interrupt_handlers()
213 {
214     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
215     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
216 }
217
218
219 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
220
221 lispobj funcall0(lispobj function)
222 {
223     lispobj *args = current_control_stack_pointer;
224
225     return call_into_lisp(function, args, 0);
226 }
227
228 lispobj funcall1(lispobj function, lispobj arg0)
229 {
230     lispobj *args = current_control_stack_pointer;
231
232     current_control_stack_pointer += 1;
233     args[0] = arg0;
234
235     return call_into_lisp(function, args, 1);
236 }
237
238 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
239 {
240     lispobj *args = current_control_stack_pointer;
241
242     current_control_stack_pointer += 2;
243     args[0] = arg0;
244     args[1] = arg1;
245
246     return call_into_lisp(function, args, 2);
247 }
248
249 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
250 {
251     lispobj *args = current_control_stack_pointer;
252
253     current_control_stack_pointer += 3;
254     args[0] = arg0;
255     args[1] = arg1;
256     args[2] = arg2;
257
258     return call_into_lisp(function, args, 3);
259 }
260
261 void
262 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
263 {
264   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
265   extern void ppc_flush_cache_line(os_vm_address_t);
266
267   while (address < end) {
268     ppc_flush_cache_line(address);
269     address += 32;
270   }
271 }
272
273 #ifdef LISP_FEATURE_LINKAGE_TABLE
274
275 /* Linkage tables for PowerPC
276  *
277  * Linkage entry size is 16, because we need at least 4 instructions to
278  * implement a jump.
279  */
280
281 /*
282  * Define the registers to use in the linkage jump table. Can be the
283  * same. Some care must be exercised when choosing these. It has to be
284  * a register that is not otherwise being used. reg_NFP is a good
285  * choice. call_into_c trashes reg_NFP without preserving it, so we can
286  * trash it in the linkage jump table.
287  */
288 #define LINKAGE_TEMP_REG        reg_NFP
289 #define LINKAGE_ADDR_REG        reg_NFP
290
291 /*
292  * Insert the necessary jump instructions at the given address.
293  */
294 void
295 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
296 {
297   /*
298    * Make JMP to function entry.
299    *
300    * The instruction sequence is:
301    *
302    *        addis 13, 0, (hi part of addr)
303    *        ori   13, 13, (low part of addr)
304    *        mtctr 13
305    *        bctr
306    *        
307    */
308   int* inst_ptr;
309   unsigned long hi;                   /* Top 16 bits of address */
310   unsigned long lo;                   /* Low 16 bits of address */
311   unsigned int inst;
312
313   inst_ptr = (int*) reloc_addr;
314
315   /*
316    * Split the target address into hi and lo parts for the sethi
317    * instruction.  hi is the top 22 bits.  lo is the low 10 bits.
318    */
319   hi = (unsigned long) target_addr;
320   lo = hi & 0xffff;
321   hi >>= 16;
322
323   /*
324    * addis 13, 0, (hi part)
325    */
326       
327   inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
328   *inst_ptr++ = inst;
329
330   /*
331    * ori 13, 13, (lo part)
332    */
333
334   inst = (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
335   *inst_ptr++ = inst;
336   
337   /*
338    * mtctr 13
339    */
340
341   inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
342   *inst_ptr++ = inst;
343
344   /*
345    * bctr
346    */
347
348   inst = (19 << 26) | (20 << 21) | (528 << 1);
349   *inst_ptr++ = inst;
350
351
352   *inst_ptr++ = inst;
353   
354   os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
355 }
356
357 void 
358 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
359 {
360     *(unsigned long *)reloc_addr = (unsigned long)target_addr;
361 }
362
363 #endif