In the mips sigtrap hander, and for the case of a break instruction in a
[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 inline unsigned int
42 os_context_register(os_context_t *context, int offset)
43 {
44     return (unsigned int)(*os_context_register_addr(context, offset));
45 }
46
47 static inline unsigned int
48 os_context_pc(os_context_t *context)
49 {
50     return (unsigned int)(*os_context_pc_addr(context));
51 }
52
53 static inline unsigned int
54 os_context_insn(os_context_t *context)
55 {
56     if (os_context_bd_cause(context))
57         return *(unsigned int *)(os_context_pc(context) + 4);
58     else
59         return *(unsigned int *)(os_context_pc(context));
60 }
61
62 /* This function is somewhat misnamed, it actually just jumps to the
63    correct target address without attempting to execute the delay slot.
64    For other instructions it just increments the returned PC value. */
65 static unsigned int
66 emulate_branch(os_context_t *context, unsigned int inst)
67 {
68     unsigned int opcode = inst >> 26;
69     unsigned int r1 = (inst >> 21) & 0x1f;
70     unsigned int r2 = (inst >> 16) & 0x1f;
71     unsigned int r3 = (inst >> 11) & 0x1f;
72     unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
73     unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
74     unsigned int tgt = os_context_pc(context);
75
76     switch(opcode) {
77     case 0x0: /* jr, jalr */
78         switch(inst & 0x3f) {
79         case 0x08: /* jr */
80             tgt = os_context_register(context, r1);
81             break;
82         case 0x09: /* jalr */
83             tgt = os_context_register(context, r1);
84             *os_context_register_addr(context, r3)
85                 = os_context_pc(context) + 4;
86             break;
87         default:
88             tgt += 4;
89             break;
90         }
91         break;
92     case 0x1: /* bltz, bgez, bltzal, bgezal */
93         switch((inst >> 16) & 0x1f) {
94         case 0x00: /* bltz */
95             if(os_context_register(context, r1) < 0)
96                 tgt += disp;
97             break;
98         case 0x01: /* bgez */
99             if(os_context_register(context, r1) >= 0)
100                 tgt += disp;
101             break;
102         case 0x10: /* bltzal */
103             if(os_context_register(context, r1) < 0)
104                 tgt += disp;
105             *os_context_register_addr(context, 31)
106                 = os_context_pc(context) + 4;
107             break;
108         case 0x11: /* bgezal */
109             if(os_context_register(context, r1) >= 0)
110                 tgt += disp;
111             *os_context_register_addr(context, 31)
112                 = os_context_pc(context) + 4;
113             break;
114         default: /* conditional branches/traps for > MIPS I, ignore for now. */
115             break;
116         }
117         break;
118     case 0x4: /* beq */
119         if(os_context_register(context, r1)
120            == os_context_register(context, r2))
121             tgt += disp;
122         break;
123     case 0x5: /* bne */
124         if(os_context_register(context, r1)
125            != os_context_register(context, r2))
126             tgt += disp;
127         break;
128     case 0x6: /* blez */
129         if(os_context_register(context, r1)
130            <= os_context_register(context, r2))
131             tgt += disp;
132         break;
133     case 0x7: /* bgtz */
134         if(os_context_register(context, r1)
135            > os_context_register(context, r2))
136             tgt += disp;
137         break;
138     case 0x2: /* j */
139         tgt = jtgt;
140         break;
141     case 0x3: /* jal */
142         tgt = jtgt;
143         *os_context_register_addr(context, 31)
144             = os_context_pc(context) + 4;
145         break;
146     default:
147         tgt += 4;
148         break;
149     }
150     return tgt;
151 }
152
153 void
154 arch_skip_instruction(os_context_t *context)
155 {
156     /* Skip the offending instruction.  Don't use os_context_insn here,
157        since in case of a branch we want the branch insn, not the delay
158        slot.  */
159       *os_context_pc_addr(context)
160           = emulate_branch(context,
161               *(unsigned int *)(os_context_pc(context)));
162 }
163
164 unsigned char *
165 arch_internal_error_arguments(os_context_t *context)
166 {
167     if (os_context_bd_cause(context))
168         return (unsigned char *)(os_context_pc(context) + 8);
169     else
170         return (unsigned char *)(os_context_pc(context) + 4);
171 }
172
173 boolean
174 arch_pseudo_atomic_atomic(os_context_t *context)
175 {
176     return os_context_register(context, reg_ALLOC) & 1;
177 }
178
179 void
180 arch_set_pseudo_atomic_interrupted(os_context_t *context)
181 {
182     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
183 }
184
185 unsigned long
186 arch_install_breakpoint(void *pc)
187 {
188     unsigned int *ptr = (unsigned int *)pc;
189     unsigned long result;
190
191     /* Don't install over a branch/jump.  */
192     switch (*ptr >> 26) {
193     case 0x0: /* immediate jumps */
194         switch (*ptr & 0x3f) {
195         case 0x08:
196         case 0x09:
197             ptr++;
198         }
199         break;
200     /* branches and register jumps */
201     case 0x1:
202     case 0x2:
203     case 0x3:
204     case 0x4:
205     case 0x5:
206     case 0x6:
207     case 0x7:
208         ptr++;
209     }
210
211     result = (unsigned long) *ptr;
212     *ptr = (trap_Breakpoint << 16) | 0xd;
213     os_flush_icache((os_vm_address_t)ptr, sizeof(unsigned int));
214
215     return result;
216 }
217
218 void
219 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
220 {
221     unsigned int *ptr = (unsigned int *)pc;
222
223     *ptr = (unsigned int) orig_inst;
224     os_flush_icache((os_vm_address_t)ptr, sizeof(unsigned int));
225 }
226
227 static unsigned int *skipped_break_addr, displaced_after_inst;
228 static sigset_t orig_sigmask;
229
230 void
231 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
232 {
233     unsigned int *pc = (unsigned int *)os_context_pc(context);
234     unsigned int *break_pc, *next_pc;
235     unsigned int next_inst;
236
237     orig_sigmask = *os_context_sigmask_addr(context);
238     sigaddset_blockable(os_context_sigmask_addr(context));
239
240     /* Figure out where the breakpoint is, and what happens next. */
241     if (os_context_bd_cause(context)) {
242         break_pc = pc+1;
243         next_inst = *pc;
244     } else {
245         break_pc = pc;
246         next_inst = orig_inst;
247     }
248
249     /* Put the original instruction back. */
250     arch_remove_breakpoint(break_pc, orig_inst);
251     skipped_break_addr = break_pc;
252
253     /* Figure out where it goes. */
254     next_pc = (unsigned int *)emulate_branch(context, next_inst);
255
256     displaced_after_inst = arch_install_breakpoint(next_pc);
257 }
258
259 static void
260 sigill_handler(int signal, siginfo_t *info, void *void_context)
261 {
262     os_context_t *context = arch_os_get_context(&void_context);
263
264     fake_foreign_function_call(context);
265     monitor_or_something();
266 }
267
268 static void
269 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
270 {
271     os_context_t *context = arch_os_get_context(&void_context);
272     unsigned int code = (os_context_insn(context) >> 16) & 0x1f;
273
274     switch (code) {
275     case trap_Halt:
276         fake_foreign_function_call(context);
277         lose("%%primitive halt called; the party is over.\n");
278
279     case trap_PendingInterrupt:
280         arch_skip_instruction(context);
281         interrupt_handle_pending(context);
282         break;
283
284     case trap_Error:
285     case trap_Cerror:
286         interrupt_internal_error(signal, info, context, code == trap_Cerror);
287         break;
288
289     case trap_Breakpoint:
290         handle_breakpoint(signal, info, context);
291         break;
292
293     case trap_FunEndBreakpoint:
294         *os_context_pc_addr(context)
295             = (os_context_register_t)(unsigned int)
296                 handle_fun_end_breakpoint(signal, info, context);
297         break;
298
299     case trap_AfterBreakpoint:
300         arch_remove_breakpoint(os_context_pc_addr(context), displaced_after_inst);
301         displaced_after_inst = arch_install_breakpoint(skipped_break_addr);
302         *os_context_sigmask_addr(context) = orig_sigmask;
303         break;
304
305     case 0x10:
306         /* Clear the pseudo-atomic flag */
307         *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
308         arch_skip_instruction(context);
309         interrupt_handle_pending(context);
310         return;
311
312     default:
313         interrupt_handle_now(signal, info, context);
314         break;
315     }
316 }
317
318 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
319
320 static void
321 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
322 {
323     os_context_t *context = arch_os_get_context(&void_context);
324     unsigned int bad_inst = os_context_insn(context);
325     unsigned int op, rs, rt, rd, funct, dest = 32;
326     int immed;
327     int result;
328
329     op = (bad_inst >> 26) & 0x3f;
330     rs = (bad_inst >> 21) & 0x1f;
331     rt = (bad_inst >> 16) & 0x1f;
332     rd = (bad_inst >> 11) & 0x1f;
333     funct = bad_inst & 0x3f;
334     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
335
336     switch (op) {
337     case 0x0: /* SPECIAL */
338         switch (funct) {
339         case 0x20: /* ADD */
340             result = FIXNUM_VALUE(os_context_register(context, rs))
341                 + FIXNUM_VALUE(os_context_register(context, rt));
342             dest = rd;
343             break;
344
345         case 0x22: /* SUB */
346             result = FIXNUM_VALUE(os_context_register(context, rs))
347                 - FIXNUM_VALUE(os_context_register(context, rt));
348             dest = rd;
349             break;
350
351         default:
352             interrupt_handle_now(signal, info, context);
353             return;
354         }
355         break;
356
357     case 0x8: /* ADDI */
358         result = FIXNUM_VALUE(os_context_register(context,rs))
359                     + (immed >> N_FIXNUM_TAG_BITS);
360         dest = rt;
361         break;
362
363     default:
364         interrupt_handle_now(signal, info, context);
365         return;
366     }
367
368     dynamic_space_free_pointer =
369         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
370
371     *os_context_register_addr(context,dest) = alloc_number(result);
372
373     *os_context_register_addr(context, reg_ALLOC) =
374         (unsigned int) dynamic_space_free_pointer;
375
376     arch_skip_instruction(context);
377 }
378
379 void
380 arch_install_interrupt_handlers()
381 {
382     undoably_install_low_level_interrupt_handler(SIGILL,sigill_handler);
383     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
384     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
385 }
386
387 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
388
389 lispobj
390 funcall0(lispobj function)
391 {
392     lispobj *args = current_control_stack_pointer;
393
394     return call_into_lisp(function, args, 0);
395 }
396
397 lispobj
398 funcall1(lispobj function, lispobj arg0)
399 {
400     lispobj *args = current_control_stack_pointer;
401
402     current_control_stack_pointer += 1;
403     args[0] = arg0;
404
405     return call_into_lisp(function, args, 1);
406 }
407
408 lispobj
409 funcall2(lispobj function, lispobj arg0, lispobj arg1)
410 {
411     lispobj *args = current_control_stack_pointer;
412
413     current_control_stack_pointer += 2;
414     args[0] = arg0;
415     args[1] = arg1;
416
417     return call_into_lisp(function, args, 2);
418 }
419
420 lispobj
421 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
422 {
423     lispobj *args = current_control_stack_pointer;
424
425     current_control_stack_pointer += 3;
426     args[0] = arg0;
427     args[1] = arg1;
428     args[2] = arg2;
429
430     return call_into_lisp(function, args, 3);
431 }