0.pre8.112:
[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
38 os_vm_address_t 
39 arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
40 {
41     unsigned long badinstr;
42     unsigned int *pc =  (unsigned int *)(*os_context_pc_addr(context));
43     int instclass;
44     os_vm_address_t addr;
45     
46     
47     /* Make sure it's not the pc thats bogus, and that it was lisp code */
48     /* that caused the fault. */
49     if ((((unsigned long)pc) & 3) != 0 ||
50         ((pc < READ_ONLY_SPACE_START ||
51           pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
52          ((lispobj *)pc < current_dynamic_space &&
53           (lispobj *)pc >= current_dynamic_space + DYNAMIC_SPACE_SIZE)))
54         return 0;
55     
56     
57     addr = (os_vm_address_t) (*os_context_register_addr(context,PT_DAR));
58     return addr;
59 }
60       
61
62 void 
63 arch_skip_instruction(os_context_t *context)
64 {
65     ((char*)*os_context_pc_addr(context)) +=4; 
66 }
67
68 unsigned char *
69 arch_internal_error_arguments(os_context_t *context)
70 {
71     return (unsigned char *)(*os_context_pc_addr(context)+4);
72 }
73
74
75 boolean 
76 arch_pseudo_atomic_atomic(os_context_t *context)
77 {
78     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
79 }
80
81 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
82
83 void 
84 arch_set_pseudo_atomic_interrupted(os_context_t *context)
85 {
86     *os_context_register_addr(context,reg_NL3) 
87         += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
88 }
89
90 unsigned long 
91 arch_install_breakpoint(void *pc)
92 {
93     unsigned long *ptr = (unsigned long *)pc;
94     unsigned long result = *ptr;
95     *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
96     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
97     return result;
98 }
99
100 void 
101 get_spinlock(lispobj *word,int value)
102 {
103     /* FIXME */
104     *word=value;
105 }
106
107
108 void 
109 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
110 {
111     *(unsigned long *)pc = orig_inst;
112     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
113 }
114
115 static unsigned long *skipped_break_addr, displaced_after_inst;
116 static sigset_t orig_sigmask;
117
118 void 
119 arch_do_displaced_inst(os_context_t *context,unsigned int orig_inst)
120 {
121     /* not sure how we ensure that we get the breakpoint reinstalled
122      * after doing this -dan */
123     unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
124     
125     orig_sigmask = *os_context_sigmask_addr(context);
126     sigaddset_blockable(os_context_sigmask_addr(context));
127     
128     *pc = orig_inst;
129     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
130     skipped_break_addr = pc;
131 }
132
133 static void 
134 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
135 {
136     int badinst;
137     u32 code;
138     sigset_t *mask;
139 #ifdef LISP_FEATURE_LINUX
140     os_restore_fp_control(context);
141 #endif
142     mask=(os_context_sigmask_addr(context));
143     sigsetmask(mask); 
144     code=*((u32 *)(*os_context_pc_addr(context)));
145     if (code == ((3 << 26) | (16 << 21) | (reg_ALLOC << 16))) {
146         /* twlti reg_ALLOC,0 - check for deferred interrupt */
147         *os_context_register_addr(context,reg_ALLOC) 
148             -= PSEUDO_ATOMIC_INTERRUPTED_BIAS;
149         arch_skip_instruction(context);
150         /* interrupt or GC was requested in PA; now we're done with the
151            PA section we may as well get around to it */
152         interrupt_handle_pending(context);
153         return;
154         
155     }
156     if ((code >> 16) == ((3 << 10) | (6 << 5))) {
157         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
158         int trap = code & 0x1f, extra = (code >> 5) & 0x1f;
159         
160         switch (trap) {
161         case trap_Halt:
162             fake_foreign_function_call(context);
163             lose("%%primitive halt called; the party is over.\n");
164             
165         case trap_Error:
166         case trap_Cerror:
167             interrupt_internal_error(signal, code, context, trap == trap_Cerror);
168             break;
169             
170         case trap_PendingInterrupt:
171           /* when do we run this branch instead of the twlti code above? */
172             arch_skip_instruction(context);
173             interrupt_handle_pending(context);
174             break;
175             
176         case trap_Breakpoint:
177             handle_breakpoint(signal, code, context);
178             break;
179             
180         case trap_FunEndBreakpoint:
181             *os_context_pc_addr(context)
182                 =(int)handle_fun_end_breakpoint(signal, code, context);
183             break;
184             
185         case trap_AfterBreakpoint:
186             *skipped_break_addr = trap_Breakpoint;
187             skipped_break_addr = NULL;
188             *(unsigned long *)*os_context_pc_addr(context) 
189                 = displaced_after_inst;
190             *os_context_sigmask_addr(context)= orig_sigmask;
191  
192             os_flush_icache((os_vm_address_t) *os_context_pc_addr(context),
193                             sizeof(unsigned long));
194             break;
195             
196         default:
197             interrupt_handle_now(signal, code, context);
198             break;
199         }
200     }
201     if (((code >> 26) == 3) && (((code >> 21) & 31) == 24)) {
202         interrupt_internal_error(signal, code, context, 0);
203     }
204     
205     interrupt_handle_now(signal, code, context);
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 }