0.7.5.13:
[sbcl.git] / src / runtime / ppc-arch.c
1 /*
2
3  $Header$
4
5  This code was written as part of the CMU Common Lisp project at
6  Carnegie Mellon University, and has been placed in the public domain.
7
8 */
9
10 #include <stdio.h>
11
12 #include "arch.h"
13 #include "sbcl.h"
14 #include "globals.h"
15 #include "validate.h"
16 #include "os.h"
17 #include "lispregs.h"
18 #include "signal.h"
19 #include "interrupt.h"
20 #include "interr.h"
21
22   /* The header files may not define PT_DAR/PT_DSISR.  This definition
23      is correct for all versions of ppc linux >= 2.0.30
24
25      As of DR2.1u4, MkLinux doesn't pass these registers to signal
26      handlers correctly; a patch is necessary in order to (partially)
27      correct this.
28
29      Even with the patch, the DSISR may not have its 'write' bit set
30      correctly (it tends not to be set if the fault was caused by
31      something other than a protection violation.)
32      
33      Caveat callers.  */
34
35 #ifndef PT_DAR
36 #define PT_DAR          41
37 #endif
38
39 #ifndef PT_DSISR
40 #define PT_DSISR        42
41 #endif
42
43 void arch_init()
44 {
45 }
46
47 os_vm_address_t 
48 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
49 {
50     unsigned long badinstr;
51     unsigned int *pc =  (unsigned int *)(*os_context_pc_addr(context));
52     int instclass;
53     os_vm_address_t addr;
54     
55     
56     /* Make sure it's not the pc thats bogus, and that it was lisp code */
57     /* that caused the fault. */
58     if ((((unsigned long)pc) & 3) != 0 ||
59         ((pc < READ_ONLY_SPACE_START ||
60           pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
61          ((lispobj *)pc < current_dynamic_space &&
62           (lispobj *)pc >= current_dynamic_space + DYNAMIC_SPACE_SIZE)))
63         return 0;
64     
65     
66     addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
67     return addr;
68 }
69       
70
71 void 
72 arch_skip_instruction(os_context_t *context)
73 {
74     ((char*)*os_context_pc_addr(context)) +=4; 
75 }
76
77 unsigned char *
78 arch_internal_error_arguments(os_context_t *context)
79 {
80     return (unsigned char *)(*os_context_pc_addr(context)+4);
81 }
82
83
84 boolean 
85 arch_pseudo_atomic_atomic(os_context_t *context)
86 {
87     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
88 }
89
90 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
91
92 void 
93 arch_set_pseudo_atomic_interrupted(os_context_t *context)
94 {
95     *os_context_register_addr(context,reg_NL3) 
96         += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
97 }
98
99 unsigned long 
100 arch_install_breakpoint(void *pc)
101 {
102     unsigned long *ptr = (unsigned long *)pc;
103     unsigned long result = *ptr;
104     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
105     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
106     return result;
107 }
108
109 void 
110 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
111 {
112     *(unsigned long *)pc = orig_inst;
113     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
114 }
115
116 static unsigned long *skipped_break_addr, displaced_after_inst;
117 static sigset_t orig_sigmask;
118
119 void 
120 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
121 {
122     /* not sure how we ensure that we get the breakpoint reinstalled
123      * after doing this -dan */
124     unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
125     
126     orig_sigmask = *os_context_sigmask_addr(context);
127     sigaddset_blockable(os_context_sigmask_addr(context));
128     
129     *pc = orig_inst;
130     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
131     skipped_break_addr = pc;
132 }
133
134 static void 
135 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
136 {
137     int badinst;
138     u32 code;
139     sigset_t *mask;
140 #ifdef LISP_FEATURE_LINUX
141     os_restore_fp_control(context);
142 #endif
143     mask=(os_context_sigmask_addr(context));
144     sigsetmask(mask); 
145     code=*((u32 *)(*os_context_pc_addr(context)));
146     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
147         /* twlti reg_ALLOC,0 - check for deferred interrupt */
148         *os_context_register_addr(context,reg_ALLOC) 
149             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
150         arch_skip_instruction(context);
151         /* interrupt or GC was requested in PA; now we're done with the
152            PA section we may as well get around to it */
153         interrupt_handle_pending(context);
154         return;
155         
156     }
157     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
158         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
159         int trap = code & 0x1f, extra = (code >> 5) & 0x1f;
160         
161         switch (trap) {
162         case trap_Halt:
163             fake_foreign_function_call(context);
164             lose("%%primitive halt called; the party is over.\n");
165             
166         case trap_Error:
167         case trap_Cerror:
168             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
169             break;
170             
171         case trap_PendingInterrupt:
172           /* when do we run this branch instead of the twlti code above? */
173             arch_skip_instruction(context);
174             interrupt_handle_pending(context);
175             break;
176             
177         case trap_Breakpoint:
178             handle_breakpoint(signal, code, context);
179             break;
180             
181         case trap_FunEndBreakpoint:
182             *os_context_pc_addr(context)
183                 =(int)handle_fun_end_breakpoint(signal, code, context);
184             break;
185             
186         case trap_AfterBreakpoint:
187             *skipped_break_addr = trap_Breakpoint;
188             skipped_break_addr = NULL;
189             *(unsigned long *)*os_context_pc_addr(context) 
190                 = displaced_after_inst;
191             *os_context_sigmask_addr(context)= orig_sigmask;
192  
193             os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
194                             sizeof(unsigned long));
195             break;
196             
197         default:
198             interrupt_handle_now(signal, code, context);
199             break;
200         }
201     }
202     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
203         interrupt_internal_error(signal, code, context, 0);
204     }
205     
206     interrupt_handle_now(signal, code, context);
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 }