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