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