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