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