87220217e0a4183e694d6684117dad807c63e218
[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 unsigned int
278 arch_install_breakpoint(void *pc)
279 {
280     unsigned int *ptr = (unsigned int *)pc;
281     unsigned int insn;
282
283     /* Don't install over a branch/jump with delay slot. */
284     if (arch_insn_with_bdelay_p(*ptr))
285         ptr++;
286
287     insn = *ptr;
288     *ptr = (trap_Breakpoint << 6) | 0xd;
289     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
290
291     return insn;
292 }
293
294 static inline unsigned int
295 arch_install_after_breakpoint(void *pc)
296 {
297     unsigned int *ptr = (unsigned int *)pc;
298     unsigned int insn;
299
300     /* Don't install over a branch/jump with delay slot. */
301     if (arch_insn_with_bdelay_p(*ptr))
302         ptr++;
303
304     insn = *ptr;
305     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
306     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
307
308     return insn;
309 }
310
311 void
312 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
313 {
314     unsigned int *ptr = (unsigned int *)pc;
315
316     /* We may remove from a branch delay slot. */
317     if (arch_insn_with_bdelay_p(*ptr))
318         ptr++;
319
320     *ptr = orig_inst;
321     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
322 }
323
324 /* Perform the instruction that we overwrote with a breakpoint. As we
325    don't have a single-step facility, this means we have to:
326    - put the instruction back
327    - put a second breakpoint at the following instruction,
328      set after_breakpoint and continue execution.
329
330    When the second breakpoint is hit (very shortly thereafter, we hope)
331    sigtrap_handler gets called again, but follows the AfterBreakpoint
332    arm, which
333    - puts a bpt back in the first breakpoint place (running across a
334      breakpoint shouldn't cause it to be uninstalled)
335    - replaces the second bpt with the instruction it was meant to be
336    - carries on
337
338    Clear? */
339
340 static unsigned int *skipped_break_addr, displaced_after_inst;
341 static sigset_t orig_sigmask;
342
343 void
344 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
345 {
346     unsigned int *pc = (unsigned int *)os_context_pc(context);
347     unsigned int *next_pc;
348
349     orig_sigmask = *os_context_sigmask_addr(context);
350     sigaddset_blockable(os_context_sigmask_addr(context));
351
352     /* Put the original instruction back. */
353     arch_remove_breakpoint(pc, orig_inst);
354     skipped_break_addr = pc;
355
356     /* Figure out where it goes. */
357     next_pc = (unsigned int *)next_insn_addr(context, *pc);
358     displaced_after_inst = arch_install_after_breakpoint(next_pc);
359 }
360
361 static void
362 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
363 {
364     os_context_t *context = arch_os_get_context(&void_context);
365     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
366
367     switch (code) {
368     case trap_Halt:
369         fake_foreign_function_call(context);
370         lose("%%primitive halt called; the party is over.\n");
371
372     case trap_PendingInterrupt:
373         arch_skip_instruction(context);
374         interrupt_handle_pending(context);
375         break;
376
377     case trap_Error:
378     case trap_Cerror:
379         interrupt_internal_error(signal, info, context, code == trap_Cerror);
380         break;
381
382     case trap_Breakpoint:
383         handle_breakpoint(signal, info, context);
384         break;
385
386     case trap_FunEndBreakpoint:
387         *os_context_pc_addr(context)
388             = (os_context_register_t)(unsigned int)
389                 handle_fun_end_breakpoint(signal, info, context);
390         break;
391
392     case trap_AfterBreakpoint:
393         arch_install_breakpoint(skipped_break_addr);
394         arch_remove_breakpoint((unsigned int *)os_context_pc(context),
395                                displaced_after_inst);
396         *os_context_sigmask_addr(context) = orig_sigmask;
397         break;
398
399     case 0x10:
400         /* Clear the pseudo-atomic flag. */
401         *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
402         arch_skip_instruction(context);
403         interrupt_handle_pending(context);
404         return;
405
406     default:
407         interrupt_handle_now(signal, info, context);
408         break;
409     }
410 }
411
412 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
413
414 static void
415 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
416 {
417     os_context_t *context = arch_os_get_context(&void_context);
418     unsigned int bad_inst = os_context_insn(context);
419     unsigned int op, rs, rt, rd, funct, dest = 32;
420     int immed;
421     int result;
422
423     op = (bad_inst >> 26) & 0x3f;
424     rs = (bad_inst >> 21) & 0x1f;
425     rt = (bad_inst >> 16) & 0x1f;
426     rd = (bad_inst >> 11) & 0x1f;
427     funct = bad_inst & 0x3f;
428     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
429
430     switch (op) {
431     case 0x0: /* SPECIAL */
432         switch (funct) {
433         case 0x20: /* ADD */
434             result = FIXNUM_VALUE(os_context_register(context, rs))
435                 + FIXNUM_VALUE(os_context_register(context, rt));
436             dest = rd;
437             break;
438
439         case 0x22: /* SUB */
440             result = FIXNUM_VALUE(os_context_register(context, rs))
441                 - FIXNUM_VALUE(os_context_register(context, rt));
442             dest = rd;
443             break;
444
445         default:
446             interrupt_handle_now(signal, info, context);
447             return;
448         }
449         break;
450
451     case 0x8: /* ADDI */
452         result = FIXNUM_VALUE(os_context_register(context,rs))
453                     + (immed >> N_FIXNUM_TAG_BITS);
454         dest = rt;
455         break;
456
457     default:
458         interrupt_handle_now(signal, info, context);
459         return;
460     }
461
462     dynamic_space_free_pointer =
463         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
464
465     *os_context_register_addr(context,dest) = alloc_number(result);
466
467     *os_context_register_addr(context, reg_ALLOC) =
468         (unsigned int) dynamic_space_free_pointer;
469
470     arch_skip_instruction(context);
471 }
472
473 void
474 arch_install_interrupt_handlers()
475 {
476     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
477     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
478 }
479
480 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
481
482 lispobj
483 funcall0(lispobj function)
484 {
485     lispobj *args = current_control_stack_pointer;
486
487     return call_into_lisp(function, args, 0);
488 }
489
490 lispobj
491 funcall1(lispobj function, lispobj arg0)
492 {
493     lispobj *args = current_control_stack_pointer;
494
495     current_control_stack_pointer += 1;
496     args[0] = arg0;
497
498     return call_into_lisp(function, args, 1);
499 }
500
501 lispobj
502 funcall2(lispobj function, lispobj arg0, lispobj arg1)
503 {
504     lispobj *args = current_control_stack_pointer;
505
506     current_control_stack_pointer += 2;
507     args[0] = arg0;
508     args[1] = arg1;
509
510     return call_into_lisp(function, args, 2);
511 }
512
513 lispobj
514 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
515 {
516     lispobj *args = current_control_stack_pointer;
517
518     current_control_stack_pointer += 3;
519     args[0] = arg0;
520     args[1] = arg1;
521     args[2] = arg2;
522
523     return call_into_lisp(function, args, 3);
524 }
525
526 #ifdef LISP_FEATURE_LINKAGE_TABLE
527
528 /* Linkage tables for MIPS
529
530    Linkage entry size is 16, because we need 4 instructions to implement
531    a jump. The entry size constant is defined in parms.lisp.
532
533    Define the register to use in the linkage jump table. For MIPS this
534    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
535 #define LINKAGE_TEMP_REG        reg_ALLOC
536
537 /* Insert the necessary jump instructions at the given address. */
538 void
539 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
540 {
541   /* Make JMP to function entry. The instruction sequence is:
542        lui    $25, 0, %hi(addr)
543        addiu  $25, $25, %lo(addr)
544        jr     $25
545         nop */
546   unsigned int *insn = (unsigned int *)reloc_addr;
547   unsigned int addr = (unsigned int)target_addr;
548   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
549   unsigned int lo = addr & 0xffff;
550
551   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
552   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
553                  | (LINKAGE_TEMP_REG << 16) | lo);
554   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
555   *insn = 0;
556
557   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
558 }
559
560 void
561 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
562 {
563     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
564 }
565
566 #endif