0.8.7.57:
[sbcl.git] / src / runtime / ppc-arch.c
1 #include <stdio.h>
2
3 #include "sbcl.h"
4 #include "arch.h"
5 #include "sbcl.h"
6 #include "globals.h"
7 #include "validate.h"
8 #include "os.h"
9 #include "lispregs.h"
10 #include "signal.h"
11 #include "interrupt.h"
12 #include "interr.h"
13
14   /* The header files may not define PT_DAR/PT_DSISR.  This definition
15      is correct for all versions of ppc linux >= 2.0.30
16
17      As of DR2.1u4, MkLinux doesn't pass these registers to signal
18      handlers correctly; a patch is necessary in order to (partially)
19      correct this.
20
21      Even with the patch, the DSISR may not have its 'write' bit set
22      correctly (it tends not to be set if the fault was caused by
23      something other than a protection violation.)
24      
25      Caveat callers.  */
26
27 #ifndef PT_DAR
28 #define PT_DAR          41
29 #endif
30
31 #ifndef PT_DSISR
32 #define PT_DSISR        42
33 #endif
34
35 void arch_init() {
36 }
37
38 os_vm_address_t 
39 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
40 {
41     unsigned int *pc =  (unsigned int *)(*os_context_pc_addr(context));
42     os_vm_address_t addr;
43     
44     
45     /* Make sure it's not the pc thats bogus, and that it was lisp code */
46     /* that caused the fault. */
47     if ((((unsigned long)pc) & 3) != 0 ||
48         ((pc < READ_ONLY_SPACE_START ||
49           pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
50          ((lispobj *)pc < current_dynamic_space || 
51           (lispobj *)pc >= current_dynamic_space + DYNAMIC_SPACE_SIZE)))
52         return 0;
53     
54     
55     addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
56     return addr;
57 }
58       
59
60 void 
61 arch_skip_instruction(os_context_t *context)
62 {
63     ((char*)*os_context_pc_addr(context)) +=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 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
80
81 void 
82 arch_set_pseudo_atomic_interrupted(os_context_t *context)
83 {
84     *os_context_register_addr(context,reg_NL3) 
85         += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
86 }
87
88 unsigned long 
89 arch_install_breakpoint(void *pc)
90 {
91     unsigned long *ptr = (unsigned long *)pc;
92     unsigned long result = *ptr;
93     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
94     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
95     return result;
96 }
97
98 void 
99 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
100 {
101     *(unsigned long *)pc = orig_inst;
102     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
103 }
104
105 static unsigned long *skipped_break_addr, displaced_after_inst;
106 static sigset_t orig_sigmask;
107
108 void 
109 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
110 {
111     /* not sure how we ensure that we get the breakpoint reinstalled
112      * after doing this -dan */
113     unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
114     
115     orig_sigmask = *os_context_sigmask_addr(context);
116     sigaddset_blockable(os_context_sigmask_addr(context));
117     
118     *pc = orig_inst;
119     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
120     skipped_break_addr = pc;
121 }
122
123 static void 
124 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
125 {
126     u32 code;
127     sigset_t *mask;
128 #ifdef LISP_FEATURE_LINUX
129     os_restore_fp_control(context);
130 #endif
131     mask=(os_context_sigmask_addr(context));
132     sigsetmask(mask); 
133     code=*((u32 *)(*os_context_pc_addr(context)));
134     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
135         /* twlti reg_ALLOC,0 - check for deferred interrupt */
136         *os_context_register_addr(context,reg_ALLOC) 
137             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
138         arch_skip_instruction(context);
139         /* interrupt or GC was requested in PA; now we're done with the
140            PA section we may as well get around to it */
141         interrupt_handle_pending(context);
142         return;
143         
144     }
145     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
146         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
147         int trap = code & 0x1f;
148         
149         switch (trap) {
150         case trap_Halt:
151             fake_foreign_function_call(context);
152             lose("%%primitive halt called; the party is over.\n");
153             
154         case trap_Error:
155         case trap_Cerror:
156             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
157             break;
158             
159         case trap_PendingInterrupt:
160           /* when do we run this branch instead of the twlti code above? */
161             arch_skip_instruction(context);
162             interrupt_handle_pending(context);
163             break;
164             
165         case trap_Breakpoint:
166             handle_breakpoint(signal, code, context);
167             break;
168             
169         case trap_FunEndBreakpoint:
170             *os_context_pc_addr(context)
171                 =(int)handle_fun_end_breakpoint(signal, code, context);
172             break;
173             
174         case trap_AfterBreakpoint:
175             *skipped_break_addr = trap_Breakpoint;
176             skipped_break_addr = NULL;
177             *(unsigned long *)*os_context_pc_addr(context) 
178                 = displaced_after_inst;
179             *os_context_sigmask_addr(context)= orig_sigmask;
180  
181             os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
182                             sizeof(unsigned long));
183             break;
184             
185         default:
186             interrupt_handle_now(signal, code, context);
187             break;
188         }
189 #ifdef LISP_FEATURE_DARWIN
190         sigreturn(context);
191 #endif
192         return;
193     }
194     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
195         interrupt_internal_error(signal, code, context, 0);
196 #ifdef LISP_FEATURE_DARWIN
197         sigreturn(context);
198 #endif
199         return;
200     }
201     
202     interrupt_handle_now(signal, code, context);
203 #ifdef LISP_FEATURE_DARWIN
204     /* Work around G5 bug */
205     sigreturn(context);
206 #endif
207 }
208
209
210 void arch_install_interrupt_handlers()
211 {
212     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
213     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
214 }
215
216
217 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
218
219 lispobj funcall0(lispobj function)
220 {
221     lispobj *args = current_control_stack_pointer;
222
223     return call_into_lisp(function, args, 0);
224 }
225
226 lispobj funcall1(lispobj function, lispobj arg0)
227 {
228     lispobj *args = current_control_stack_pointer;
229
230     current_control_stack_pointer += 1;
231     args[0] = arg0;
232
233     return call_into_lisp(function, args, 1);
234 }
235
236 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
237 {
238     lispobj *args = current_control_stack_pointer;
239
240     current_control_stack_pointer += 2;
241     args[0] = arg0;
242     args[1] = arg1;
243
244     return call_into_lisp(function, args, 2);
245 }
246
247 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
248 {
249     lispobj *args = current_control_stack_pointer;
250
251     current_control_stack_pointer += 3;
252     args[0] = arg0;
253     args[1] = arg1;
254     args[2] = arg2;
255
256     return call_into_lisp(function, args, 3);
257 }
258
259 void
260 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
261 {
262   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
263   extern void ppc_flush_cache_line(os_vm_address_t);
264
265   while (address < end) {
266     ppc_flush_cache_line(address);
267     address += 32;
268   }
269 }