0.9.0.7:
[sbcl.git] / src / runtime / mips-arch.c
1 /*
2
3  This code was written as part of the CMU Common Lisp project at
4  Carnegie Mellon University, and has been placed in the public domain.
5
6 */
7
8 #include <stdio.h>
9
10 #include "sbcl.h"
11 #include "runtime.h"
12 #include "arch.h"
13 #include "globals.h"
14 #include "validate.h"
15 #include "os.h"
16 #include "lispregs.h"
17 #include "signal.h"
18 #include "alloc.h"
19 #include "interrupt.h"
20 #include "interr.h"
21 #include "breakpoint.h"
22 #include "monitor.h"
23
24 #include "genesis/constants.h"
25
26 void
27 arch_init()
28 {
29     return;
30 }
31
32 os_vm_address_t
33 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
34 {
35     /* Classic CMUCL comment:
36
37        Finding the bad address on the mips is easy. */
38     return (os_vm_address_t) siginfo->si_addr;
39 }
40
41 static unsigned int
42 emulate_branch(os_context_t *context, unsigned int inst)
43 {
44     unsigned int opcode = inst >> 26;
45     unsigned int r1 = (inst >> 21) & 0x1f;
46     unsigned int r2 = (inst >> 16) & 0x1f;
47     unsigned int bdisp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
48     unsigned int jdisp = (inst&0x3ffffff) << 2;
49     unsigned int disp = 0;
50
51     switch(opcode) {
52     case 0x1: /* bltz, bgez, bltzal, bgezal */
53         switch((inst >> 16) & 0x1f) {
54         case 0x00: /* bltz */
55             if(*os_context_register_addr(context, r1) < 0)
56                 disp = bdisp;
57             break;
58         case 0x01: /* bgez */
59             if(*os_context_register_addr(context, r1) >= 0)
60                 disp = bdisp;
61             break;
62         case 0x10: /* bltzal */
63             if(*os_context_register_addr(context, r1) < 0)
64                 disp = bdisp;
65             *os_context_register_addr(context, 31)
66                 = *os_context_pc_addr(context) + 4;
67             break;
68         case 0x11: /* bgezal */
69             if(*os_context_register_addr(context, r1) >= 0)
70                 disp = bdisp;
71             *os_context_register_addr(context, 31)
72                 = *os_context_pc_addr(context) + 4;
73             break;
74         }
75         break;
76     case 0x4: /* beq */
77         if(*os_context_register_addr(context, r1)
78            == *os_context_register_addr(context, r2))
79             disp = bdisp;
80         break;
81     case 0x5: /* bne */
82         if(*os_context_register_addr(context, r1) 
83            != *os_context_register_addr(context, r2))
84             disp = bdisp;
85         break;
86     case 0x6: /* blez */
87         if(*os_context_register_addr(context, r1)
88            <= *os_context_register_addr(context, r2))
89             disp = bdisp;
90         break;
91     case 0x7: /* bgtz */
92         if(*os_context_register_addr(context, r1)
93            > *os_context_register_addr(context, r2))
94             disp = bdisp;
95         break;
96     case 0x2: /* j */
97         disp = jdisp;
98         break;
99     case 0x3: /* jal */
100         disp = jdisp;
101         *os_context_register_addr(context, 31)
102             = *os_context_pc_addr(context) + 4;
103         break;
104     }
105     return *os_context_pc_addr(context) + disp;
106 }
107
108 void
109 arch_skip_instruction(os_context_t *context)
110 {
111     /* Skip the offending instruction */
112     if (os_context_bd_cause(context)) {
113         /* Currently, we never get here, because Linux' support for
114            bd_cause seems not terribly solid (c.f os_context_bd_cause
115            in mips-linux-os.c).  If a port to Irix comes along, this
116            code will be executed, because presumably Irix' support is
117            better (it can hardly be worse).  We lose() to remind the
118            porter to review this code.  -- CSR, 2002-09-06 */
119         lose("bd_cause branch taken; review code for new OS?\n");
120         *os_context_pc_addr(context)
121             = emulate_branch(context, *os_context_pc_addr(context));
122     } else
123         *os_context_pc_addr(context) += 4;
124
125     os_flush_icache((os_vm_address_t)*os_context_pc_addr(context),
126                     sizeof(unsigned int));
127 }
128
129 unsigned char *
130 arch_internal_error_arguments(os_context_t *context)
131 {
132     if (os_context_bd_cause(context))
133         return (unsigned char *)(*os_context_pc_addr(context) + 8);
134     else
135         return (unsigned char *)(*os_context_pc_addr(context) + 4);
136 }
137
138 boolean
139 arch_pseudo_atomic_atomic(os_context_t *context)
140 {
141     return *os_context_register_addr(context, reg_ALLOC) & 1;
142 }
143
144 void
145 arch_set_pseudo_atomic_interrupted(os_context_t *context)
146 {
147     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
148 }
149
150 unsigned long
151 arch_install_breakpoint(void *pc)
152 {
153     unsigned int *ptr = (unsigned int *)pc;
154     unsigned long result = (unsigned long) *ptr;
155
156     *ptr = (trap_Breakpoint << 16) | 0xd;
157     os_flush_icache((os_vm_address_t)ptr, sizeof(unsigned int));
158
159     return result;
160 }
161
162 void
163 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
164 {
165     unsigned int *ptr = (unsigned int *)pc;
166
167     *ptr = (unsigned int) orig_inst;
168     os_flush_icache((os_vm_address_t)ptr, sizeof(unsigned int));
169 }
170
171 static unsigned int *skipped_break_addr, displaced_after_inst;
172 static sigset_t orig_sigmask;
173
174 void
175 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
176 {
177     unsigned int *pc = (unsigned int *)*os_context_pc_addr(context);
178     unsigned int *break_pc, *next_pc;
179     unsigned int next_inst;
180     int opcode;
181
182     orig_sigmask = *os_context_sigmask_addr(context);
183     sigaddset_blockable(os_context_sigmask_addr(context));
184
185     /* Figure out where the breakpoint is, and what happens next. */
186     if (os_context_bd_cause(context)) {
187         break_pc = pc+1;
188         next_inst = *pc;
189     }
190     else {
191         break_pc = pc;
192         next_inst = orig_inst;
193     }
194
195     /* Put the original instruction back. */
196     arch_remove_breakpoint(break_pc, orig_inst);
197     skipped_break_addr = break_pc;
198
199     /* Figure out where it goes. */
200     opcode = next_inst >> 26;
201     if (opcode == 1 || ((opcode & 0x3c) == 0x4) || ((next_inst & 0xf00e0000) == 0x80000000))
202         next_pc = (unsigned int *)emulate_branch(context, next_inst);
203     else
204         next_pc = pc+1;
205
206     displaced_after_inst = arch_install_breakpoint(next_pc);
207 }
208
209 static void
210 sigill_handler(int signal, siginfo_t *info, void *void_context)
211 {
212     os_context_t *context = arch_os_get_context(&void_context);
213
214     fake_foreign_function_call(context);
215     monitor_or_something();
216 }
217
218 static void
219 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
220 {
221     os_context_t *context = arch_os_get_context(&void_context);
222     sigset_t *mask;
223     unsigned int code;
224     sigset_t ss;
225
226     /* Don't disallow recursive breakpoint traps.  Otherwise, we can't
227        use debugger breakpoints anywhere in here. */
228     mask = os_context_sigmask_addr(context);
229     sigprocmask(SIG_SETMASK, mask, NULL);
230     code = ((*(int *) (*os_context_pc_addr(context))) >> 16) & 0x1f;
231
232     switch (code) {
233     case trap_Halt:
234         fake_foreign_function_call(context);
235         lose("%%primitive halt called; the party is over.\n");
236
237     case trap_PendingInterrupt:
238         arch_skip_instruction(context);
239         sigemptyset(&ss);
240         sigaddset(&ss,SIGTRAP);
241         sigprocmask(SIG_UNBLOCK,&ss,0);
242         interrupt_handle_pending(context);
243         break;
244
245     case trap_Error:
246     case trap_Cerror:
247         interrupt_internal_error(signal, info, context, code==trap_Cerror);
248         break;
249
250     case trap_Breakpoint:
251         handle_breakpoint(signal, info, context);
252         break;
253
254     case trap_FunEndBreakpoint:
255         *os_context_pc_addr(context) = (int)handle_fun_end_breakpoint(signal, info, context);
256         os_flush_icache((os_vm_address_t)*os_context_pc_addr(context), sizeof(unsigned int));
257         break;
258
259     case trap_AfterBreakpoint:
260         arch_remove_breakpoint(os_context_pc_addr(context), displaced_after_inst);
261         displaced_after_inst = arch_install_breakpoint(skipped_break_addr);
262         *os_context_sigmask_addr(context) = orig_sigmask;
263         break;
264
265     case 0x10:
266         /* Clear the pseudo-atomic flag */
267         *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
268         arch_skip_instruction(context);
269         interrupt_handle_pending(context);
270         return;
271         
272     default:
273         interrupt_handle_now(signal, info, context);
274         break;
275     }
276 }
277
278 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
279
280 static void
281 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
282 {
283     unsigned int bad_inst;
284     unsigned int op, rs, rt, rd, funct, dest = 32;
285     int immed;
286     unsigned int result;
287     os_context_t *context = arch_os_get_context(&void_context);
288
289     if (os_context_bd_cause(context))
290         bad_inst = *(unsigned int *)(*os_context_pc_addr(context) + 4);
291     else
292         bad_inst = *(unsigned int *)(*os_context_pc_addr(context));
293
294     op = (bad_inst >> 26) & 0x3f;
295     rs = (bad_inst >> 21) & 0x1f;
296     rt = (bad_inst >> 16) & 0x1f;
297     rd = (bad_inst >> 11) & 0x1f;
298     funct = bad_inst & 0x3f;
299     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
300
301     switch (op) {
302     case 0x0: /* SPECIAL */
303         switch (funct) {
304         case 0x20: /* ADD */
305             /* FIXME: Hopefully, this whole section can just go away,
306                with the rewrite of pseudo-atomic and the deletion of
307                overflow VOPs */
308             /* Check to see if this is really a pa_interrupted hit */
309             if (rs == reg_ALLOC && rt == reg_NL4) {
310                 *os_context_register_addr(context, reg_ALLOC)
311                     += *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
312                 arch_skip_instruction(context);
313                 interrupt_handle_pending(context);
314                 return;
315             }
316             result = FIXNUM_VALUE(*os_context_register_addr(context, rs))
317                 + FIXNUM_VALUE(*os_context_register_addr(context, rt));
318             dest = rd;
319             break;
320             
321         case 0x22: /* SUB */
322             result = FIXNUM_VALUE(*os_context_register_addr(context, rs))
323                 - FIXNUM_VALUE(*os_context_register_addr(context, rt));
324             dest = rd;
325             break;
326         }
327         break;
328         
329     case 0x8: /* ADDI */
330         result = FIXNUM_VALUE(*os_context_register_addr(context,rs)) + (immed>>2);
331         dest = rt;
332         break;
333     }
334     
335     if (dest < 32) {
336         dynamic_space_free_pointer =
337             (lispobj *) *os_context_register_addr(context,reg_ALLOC);
338
339         *os_context_register_addr(context,dest) = alloc_number(result);
340
341         *os_context_register_addr(context, reg_ALLOC) =
342             (unsigned int) dynamic_space_free_pointer;
343
344         arch_skip_instruction(context);
345         
346     }
347     else
348         interrupt_handle_now(signal, info, context);
349 }
350
351 void
352 arch_install_interrupt_handlers()
353 {    
354     undoably_install_low_level_interrupt_handler(SIGILL,sigill_handler);
355     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
356     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
357 }
358
359 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
360
361 lispobj
362 funcall0(lispobj function)
363 {
364     lispobj *args = current_control_stack_pointer;
365
366     return call_into_lisp(function, args, 0);
367 }
368
369 lispobj
370 funcall1(lispobj function, lispobj arg0)
371 {
372     lispobj *args = current_control_stack_pointer;
373
374     current_control_stack_pointer += 1;
375     args[0] = arg0;
376
377     return call_into_lisp(function, args, 1);
378 }
379
380 lispobj
381 funcall2(lispobj function, lispobj arg0, lispobj arg1)
382 {
383     lispobj *args = current_control_stack_pointer;
384
385     current_control_stack_pointer += 2;
386     args[0] = arg0;
387     args[1] = arg1;
388
389     return call_into_lisp(function, args, 2);
390 }
391
392 lispobj
393 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
394 {
395     lispobj *args = current_control_stack_pointer;
396
397     current_control_stack_pointer += 3;
398     args[0] = arg0;
399     args[1] = arg1;
400     args[2] = arg2;
401
402     return call_into_lisp(function, args, 3);
403 }