0.9.13.4:
[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 sizeof(unsigned int)
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 /* Find the next instruction in the control flow. For a instruction
117    with branch delay slot, this is the branch/jump target if the branch
118    is taken, and PC + 8 if it is not taken. For other instructions it
119    is PC + 4. */
120 static unsigned int
121 next_insn_addr(os_context_t *context, unsigned int inst)
122 {
123     unsigned int opcode = inst >> 26;
124     unsigned int r1 = (inst >> 21) & 0x1f;
125     unsigned int r2 = (inst >> 16) & 0x1f;
126     unsigned int r3 = (inst >> 11) & 0x1f;
127     unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
128     unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
129     unsigned int tgt = os_context_pc(context);
130
131     switch(opcode) {
132     case 0x0: /* jr, jalr */
133         switch(inst & 0x3f) {
134         case 0x08: /* jr */
135             tgt = os_context_register(context, r1);
136             break;
137         case 0x09: /* jalr */
138             tgt = os_context_register(context, r1);
139             *os_context_register_addr(context, r3)
140                 = os_context_pc(context) + INSN_LEN;
141             break;
142         default:
143             tgt += INSN_LEN;
144             break;
145         }
146         break;
147     case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
148         switch(r2) {
149         case 0x00: /* bltz */
150         case 0x02: /* bltzl */
151             if(os_context_register(context, r1) < 0)
152                 tgt += disp;
153             else
154                 tgt += INSN_LEN;
155             break;
156         case 0x01: /* bgez */
157         case 0x03: /* bgezl */
158             if(os_context_register(context, r1) >= 0)
159                 tgt += disp;
160             else
161                 tgt += INSN_LEN;
162             break;
163         case 0x10: /* bltzal */
164         case 0x12: /* bltzall */
165             if(os_context_register(context, r1) < 0) {
166                 tgt += disp;
167                 *os_context_register_addr(context, 31)
168                     = os_context_pc(context) + INSN_LEN;
169             } else
170                 tgt += INSN_LEN;
171             break;
172         case 0x11: /* bgezal */
173         case 0x13: /* bgezall */
174             if(os_context_register(context, r1) >= 0) {
175                 tgt += disp;
176                 *os_context_register_addr(context, 31)
177                     = os_context_pc(context) + INSN_LEN;
178             } else
179                 tgt += INSN_LEN;
180             break;
181         default:
182             tgt += INSN_LEN;
183             break;
184         }
185         break;
186     case 0x2: /* j */
187         tgt = jtgt;
188         break;
189     case 0x3: /* jal */
190         tgt = jtgt;
191         *os_context_register_addr(context, 31)
192             = os_context_pc(context) + INSN_LEN;
193         break;
194     case 0x4: /* beq */
195     case 0x14: /* beql */
196         if(os_context_register(context, r1)
197            == os_context_register(context, r2))
198             tgt += disp;
199         else
200             tgt += INSN_LEN;
201         break;
202     case 0x5: /* bne */
203     case 0x15: /* bnel */
204         if(os_context_register(context, r1)
205            != os_context_register(context, r2))
206             tgt += disp;
207         else
208             tgt += INSN_LEN;
209         break;
210     case 0x6: /* blez */
211     case 0x16: /* blezl */
212         if(os_context_register(context, r1)
213            <= os_context_register(context, r2))
214             tgt += disp;
215         else
216             tgt += INSN_LEN;
217         break;
218     case 0x7: /* bgtz */
219     case 0x17: /* bgtzl */
220         if(os_context_register(context, r1)
221            > os_context_register(context, r2))
222             tgt += disp;
223         else
224             tgt += INSN_LEN;
225         break;
226     case 0x10:
227     case 0x11:
228     case 0x12:
229         switch (r1) {
230          /* CP0/CP1/CP2 branches */
231         case 0x08:
232             /* FIXME */
233             tgt += INSN_LEN;
234             break;
235         }
236         break;
237     default:
238         tgt += INSN_LEN;
239         break;
240     }
241     return tgt;
242 }
243
244 void
245 arch_skip_instruction(os_context_t *context)
246 {
247     /* Skip the offending instruction. Don't use os_context_insn here,
248        since in case of a branch we want the branch insn, not the delay
249        slot. */
250     *os_context_pc_addr(context)
251         = (os_context_register_t)
252             next_insn_addr(context,
253                 *(unsigned int *)(os_context_pc(context)));
254 }
255
256 unsigned char *
257 arch_internal_error_arguments(os_context_t *context)
258 {
259     if (os_context_bd_cause(context))
260         return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
261     else
262         return (unsigned char *)(os_context_pc(context) + INSN_LEN);
263 }
264
265 boolean
266 arch_pseudo_atomic_atomic(os_context_t *context)
267 {
268     return os_context_register(context, reg_ALLOC) & 1;
269 }
270
271 void
272 arch_set_pseudo_atomic_interrupted(os_context_t *context)
273 {
274     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
275 }
276
277 void
278 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
279 {
280     *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
281 }
282
283 unsigned int
284 arch_install_breakpoint(void *pc)
285 {
286     unsigned int *ptr = (unsigned int *)pc;
287     unsigned int insn;
288
289     /* Don't install over a branch/jump with delay slot. */
290     if (arch_insn_with_bdelay_p(*ptr))
291         ptr++;
292
293     insn = *ptr;
294     *ptr = (trap_Breakpoint << 6) | 0xd;
295     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
296
297     return insn;
298 }
299
300 static inline unsigned int
301 arch_install_after_breakpoint(void *pc)
302 {
303     unsigned int *ptr = (unsigned int *)pc;
304     unsigned int insn;
305
306     /* Don't install over a branch/jump with delay slot. */
307     if (arch_insn_with_bdelay_p(*ptr))
308         ptr++;
309
310     insn = *ptr;
311     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
312     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
313
314     return insn;
315 }
316
317 void
318 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
319 {
320     unsigned int *ptr = (unsigned int *)pc;
321
322     /* We may remove from a branch delay slot. */
323     if (arch_insn_with_bdelay_p(*ptr))
324         ptr++;
325
326     *ptr = orig_inst;
327     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
328 }
329
330 /* Perform the instruction that we overwrote with a breakpoint. As we
331    don't have a single-step facility, this means we have to:
332    - put the instruction back
333    - put a second breakpoint at the following instruction,
334      set after_breakpoint and continue execution.
335
336    When the second breakpoint is hit (very shortly thereafter, we hope)
337    sigtrap_handler gets called again, but follows the AfterBreakpoint
338    arm, which
339    - puts a bpt back in the first breakpoint place (running across a
340      breakpoint shouldn't cause it to be uninstalled)
341    - replaces the second bpt with the instruction it was meant to be
342    - carries on
343
344    Clear? */
345
346 static unsigned int *skipped_break_addr, displaced_after_inst;
347 static sigset_t orig_sigmask;
348
349 void
350 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
351 {
352     unsigned int *pc = (unsigned int *)os_context_pc(context);
353     unsigned int *next_pc;
354
355     orig_sigmask = *os_context_sigmask_addr(context);
356     sigaddset_blockable(os_context_sigmask_addr(context));
357
358     /* Put the original instruction back. */
359     arch_remove_breakpoint(pc, orig_inst);
360     skipped_break_addr = pc;
361
362     /* Figure out where it goes. */
363     next_pc = (unsigned int *)next_insn_addr(context, *pc);
364     displaced_after_inst = arch_install_after_breakpoint(next_pc);
365 }
366
367 static void
368 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
369 {
370     os_context_t *context = arch_os_get_context(&void_context);
371     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
372
373     switch (code) {
374     case trap_Halt:
375         fake_foreign_function_call(context);
376         lose("%%primitive halt called; the party is over.\n");
377
378     case trap_PendingInterrupt:
379         arch_skip_instruction(context);
380         interrupt_handle_pending(context);
381         break;
382
383     case trap_Error:
384     case trap_Cerror:
385         interrupt_internal_error(signal, info, context, code == trap_Cerror);
386         break;
387
388     case trap_Breakpoint:
389         handle_breakpoint(signal, info, context);
390         break;
391
392     case trap_FunEndBreakpoint:
393         *os_context_pc_addr(context)
394             = (os_context_register_t)(unsigned int)
395                 handle_fun_end_breakpoint(signal, info, context);
396         break;
397
398     case trap_AfterBreakpoint:
399         arch_install_breakpoint(skipped_break_addr);
400         arch_remove_breakpoint((unsigned int *)os_context_pc(context),
401                                displaced_after_inst);
402         *os_context_sigmask_addr(context) = orig_sigmask;
403         break;
404
405     case 0x10:
406         arch_clear_pseudo_atomic_interrupted(context);
407         arch_skip_instruction(context);
408         interrupt_handle_pending(context);
409         return;
410
411     default:
412         interrupt_handle_now(signal, info, context);
413         break;
414     }
415 }
416
417 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
418
419 static void
420 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
421 {
422     os_context_t *context = arch_os_get_context(&void_context);
423     unsigned int bad_inst = os_context_insn(context);
424     unsigned int op, rs, rt, rd, funct, dest = 32;
425     int immed;
426     int result;
427
428     op = (bad_inst >> 26) & 0x3f;
429     rs = (bad_inst >> 21) & 0x1f;
430     rt = (bad_inst >> 16) & 0x1f;
431     rd = (bad_inst >> 11) & 0x1f;
432     funct = bad_inst & 0x3f;
433     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
434
435     switch (op) {
436     case 0x0: /* SPECIAL */
437         switch (funct) {
438         case 0x20: /* ADD */
439             result = FIXNUM_VALUE(os_context_register(context, rs))
440                 + FIXNUM_VALUE(os_context_register(context, rt));
441             dest = rd;
442             break;
443
444         case 0x22: /* SUB */
445             result = FIXNUM_VALUE(os_context_register(context, rs))
446                 - FIXNUM_VALUE(os_context_register(context, rt));
447             dest = rd;
448             break;
449
450         default:
451             interrupt_handle_now(signal, info, context);
452             return;
453         }
454         break;
455
456     case 0x8: /* ADDI */
457         result = FIXNUM_VALUE(os_context_register(context,rs))
458                     + (immed >> N_FIXNUM_TAG_BITS);
459         dest = rt;
460         break;
461
462     default:
463         interrupt_handle_now(signal, info, context);
464         return;
465     }
466
467     dynamic_space_free_pointer =
468         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
469
470     *os_context_register_addr(context,dest) = alloc_number(result);
471
472     *os_context_register_addr(context, reg_ALLOC) =
473         (unsigned int) dynamic_space_free_pointer;
474
475     arch_skip_instruction(context);
476 }
477
478 void
479 arch_install_interrupt_handlers()
480 {
481     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
482     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
483 }
484
485 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
486
487 lispobj
488 funcall0(lispobj function)
489 {
490     lispobj *args = current_control_stack_pointer;
491
492     return call_into_lisp(function, args, 0);
493 }
494
495 lispobj
496 funcall1(lispobj function, lispobj arg0)
497 {
498     lispobj *args = current_control_stack_pointer;
499
500     current_control_stack_pointer += 1;
501     args[0] = arg0;
502
503     return call_into_lisp(function, args, 1);
504 }
505
506 lispobj
507 funcall2(lispobj function, lispobj arg0, lispobj arg1)
508 {
509     lispobj *args = current_control_stack_pointer;
510
511     current_control_stack_pointer += 2;
512     args[0] = arg0;
513     args[1] = arg1;
514
515     return call_into_lisp(function, args, 2);
516 }
517
518 lispobj
519 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
520 {
521     lispobj *args = current_control_stack_pointer;
522
523     current_control_stack_pointer += 3;
524     args[0] = arg0;
525     args[1] = arg1;
526     args[2] = arg2;
527
528     return call_into_lisp(function, args, 3);
529 }
530
531 #ifdef LISP_FEATURE_LINKAGE_TABLE
532
533 /* Linkage tables for MIPS
534
535    Linkage entry size is 16, because we need 4 instructions to implement
536    a jump. The entry size constant is defined in parms.lisp.
537
538    Define the register to use in the linkage jump table. For MIPS this
539    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
540 #define LINKAGE_TEMP_REG        reg_ALLOC
541
542 /* Insert the necessary jump instructions at the given address. */
543 void
544 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
545 {
546   /* Make JMP to function entry. The instruction sequence is:
547        lui    $25, 0, %hi(addr)
548        addiu  $25, $25, %lo(addr)
549        jr     $25
550         nop */
551   unsigned int *insn = (unsigned int *)reloc_addr;
552   unsigned int addr = (unsigned int)target_addr;
553   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
554   unsigned int lo = addr & 0xffff;
555
556   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
557   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
558                  | (LINKAGE_TEMP_REG << 16) | lo);
559   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
560   *insn = 0;
561
562   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
563 }
564
565 void
566 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
567 {
568     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
569 }
570
571 #endif