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