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