f62739e40381f875997645a5963941c0c34d4c5e
[sbcl.git] / src / runtime / ppc-arch.c
1 #include <stdio.h>
2
3 #include "arch.h"
4 #include "sbcl.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*)*os_context_pc_addr(context)) +=4; 
63 }
64
65 unsigned char *
66 arch_internal_error_arguments(os_context_t *context)
67 {
68     return (unsigned char *)(*os_context_pc_addr(context)+4);
69 }
70
71
72 boolean 
73 arch_pseudo_atomic_atomic(os_context_t *context)
74 {
75     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
76 }
77
78 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
79
80 void 
81 arch_set_pseudo_atomic_interrupted(os_context_t *context)
82 {
83     *os_context_register_addr(context,reg_NL3) 
84         += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
85 }
86
87 unsigned long 
88 arch_install_breakpoint(void *pc)
89 {
90     unsigned long *ptr = (unsigned long *)pc;
91     unsigned long result = *ptr;
92     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
93     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
94     return result;
95 }
96
97 void 
98 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
99 {
100     *(unsigned long *)pc = orig_inst;
101     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
102 }
103
104 static unsigned long *skipped_break_addr, displaced_after_inst;
105 static sigset_t orig_sigmask;
106
107 void 
108 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
109 {
110     /* not sure how we ensure that we get the breakpoint reinstalled
111      * after doing this -dan */
112     unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
113     
114     orig_sigmask = *os_context_sigmask_addr(context);
115     sigaddset_blockable(os_context_sigmask_addr(context));
116     
117     *pc = orig_inst;
118     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
119     skipped_break_addr = pc;
120 }
121
122 static void 
123 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
124 {
125     u32 code;
126     sigset_t *mask;
127 #ifdef LISP_FEATURE_LINUX
128     os_restore_fp_control(context);
129 #endif
130     mask=(os_context_sigmask_addr(context));
131     sigsetmask(mask); 
132     code=*((u32 *)(*os_context_pc_addr(context)));
133     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
134         /* twlti reg_ALLOC,0 - check for deferred interrupt */
135         *os_context_register_addr(context,reg_ALLOC) 
136             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
137         arch_skip_instruction(context);
138         /* interrupt or GC was requested in PA; now we're done with the
139            PA section we may as well get around to it */
140         interrupt_handle_pending(context);
141         return;
142         
143     }
144     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
145         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
146         int trap = code & 0x1f;
147         
148         switch (trap) {
149         case trap_Halt:
150             fake_foreign_function_call(context);
151             lose("%%primitive halt called; the party is over.\n");
152             
153         case trap_Error:
154         case trap_Cerror:
155             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
156             break;
157             
158         case trap_PendingInterrupt:
159           /* when do we run this branch instead of the twlti code above? */
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         return;
189     }
190     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
191         interrupt_internal_error(signal, code, context, 0);
192         return;
193     }
194     
195     interrupt_handle_now(signal, code, context);
196 }
197
198
199 void arch_install_interrupt_handlers()
200 {
201     undoably_install_low_level_interrupt_handler(SIGILL,sigtrap_handler);
202     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
203 }
204
205
206 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
207
208 lispobj funcall0(lispobj function)
209 {
210     lispobj *args = current_control_stack_pointer;
211
212     return call_into_lisp(function, args, 0);
213 }
214
215 lispobj funcall1(lispobj function, lispobj arg0)
216 {
217     lispobj *args = current_control_stack_pointer;
218
219     current_control_stack_pointer += 1;
220     args[0] = arg0;
221
222     return call_into_lisp(function, args, 1);
223 }
224
225 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
226 {
227     lispobj *args = current_control_stack_pointer;
228
229     current_control_stack_pointer += 2;
230     args[0] = arg0;
231     args[1] = arg1;
232
233     return call_into_lisp(function, args, 2);
234 }
235
236 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
237 {
238     lispobj *args = current_control_stack_pointer;
239
240     current_control_stack_pointer += 3;
241     args[0] = arg0;
242     args[1] = arg1;
243     args[2] = arg2;
244
245     return call_into_lisp(function, args, 3);
246 }
247
248 void
249 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
250 {
251   os_vm_address_t end = (os_vm_address_t) ((int)(address+length+(32-1)) &~(32-1));
252   extern void ppc_flush_cache_line(os_vm_address_t);
253
254   while (address < end) {
255     ppc_flush_cache_line(address);
256     address += 32;
257   }
258 }