1.0.5.37: cleanup a small thinko from previous x86-assem.S refactoring
[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
23 #include "genesis/constants.h"
24
25 #define INSN_LEN sizeof(unsigned int)
26
27 void
28 arch_init(void)
29 {
30     return;
31 }
32
33 os_vm_address_t
34 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
35 {
36     /* Classic CMUCL comment:
37
38        Finding the bad address on the mips is easy. */
39     return (os_vm_address_t)siginfo->si_addr;
40 }
41
42 static inline unsigned int
43 os_context_register(os_context_t *context, int offset)
44 {
45     return (unsigned int)(*os_context_register_addr(context, offset));
46 }
47
48 static inline unsigned int
49 os_context_pc(os_context_t *context)
50 {
51     return (unsigned int)(*os_context_pc_addr(context));
52 }
53
54 static inline unsigned int
55 os_context_insn(os_context_t *context)
56 {
57     if (os_context_bd_cause(context))
58         return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
59     else
60         return *(unsigned int *)(os_context_pc(context));
61 }
62
63 boolean
64 arch_insn_with_bdelay_p(unsigned int insn)
65 {
66     switch (insn >> 26) {
67     case 0x0:
68         switch (insn & 0x3f) {
69         /* register jumps */
70         case 0x08:
71         case 0x09:
72             return 1;
73         }
74         break;
75     /* branches and immediate jumps */
76     case 0x1:
77         switch ((insn >> 16) & 0x1f) {
78         case 0x00:
79         case 0x01:
80         case 0x02:
81         case 0x03:
82         case 0x10:
83         case 0x11:
84         case 0x12:
85         case 0x13:
86             return 1;
87         }
88         break;
89     case 0x2:
90     case 0x3:
91     case 0x4:
92     case 0x5:
93     case 0x6:
94     case 0x7:
95         return 1;
96     case 0x10:
97     case 0x11:
98     case 0x12:
99         switch ((insn >> 21) & 0x1f) {
100          /* CP0/CP1/CP2 branches */
101         case 0x08:
102             return 1;
103         }
104         break;
105     /* branch likely (MIPS II) */
106     case 0x14:
107     case 0x15:
108     case 0x16:
109     case 0x17:
110         return 1;
111     }
112     return 0;
113 }
114
115 /* Find the next instruction in the control flow. For a instruction
116    with branch delay slot, this is the branch/jump target if the branch
117    is taken, and PC + 8 if it is not taken. For other instructions it
118    is PC + 4. */
119 static unsigned int
120 next_insn_addr(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(r2) {
148         case 0x00: /* bltz */
149         case 0x02: /* bltzl */
150             if(os_context_register(context, r1) < 0)
151                 tgt += disp;
152             else
153                 tgt += INSN_LEN;
154             break;
155         case 0x01: /* bgez */
156         case 0x03: /* bgezl */
157             if(os_context_register(context, r1) >= 0)
158                 tgt += disp;
159             else
160                 tgt += INSN_LEN;
161             break;
162         case 0x10: /* bltzal */
163         case 0x12: /* bltzall */
164             if(os_context_register(context, r1) < 0) {
165                 tgt += disp;
166                 *os_context_register_addr(context, 31)
167                     = os_context_pc(context) + INSN_LEN;
168             } else
169                 tgt += INSN_LEN;
170             break;
171         case 0x11: /* bgezal */
172         case 0x13: /* bgezall */
173             if(os_context_register(context, r1) >= 0) {
174                 tgt += disp;
175                 *os_context_register_addr(context, 31)
176                     = os_context_pc(context) + INSN_LEN;
177             } else
178                 tgt += INSN_LEN;
179             break;
180         default:
181             tgt += INSN_LEN;
182             break;
183         }
184         break;
185     case 0x2: /* j */
186         tgt = jtgt;
187         break;
188     case 0x3: /* jal */
189         tgt = jtgt;
190         *os_context_register_addr(context, 31)
191             = os_context_pc(context) + INSN_LEN;
192         break;
193     case 0x4: /* beq */
194     case 0x14: /* beql */
195         if(os_context_register(context, r1)
196            == os_context_register(context, r2))
197             tgt += disp;
198         else
199             tgt += INSN_LEN;
200         break;
201     case 0x5: /* bne */
202     case 0x15: /* bnel */
203         if(os_context_register(context, r1)
204            != os_context_register(context, r2))
205             tgt += disp;
206         else
207             tgt += INSN_LEN;
208         break;
209     case 0x6: /* blez */
210     case 0x16: /* blezl */
211         if(os_context_register(context, r1)
212            <= os_context_register(context, r2))
213             tgt += disp;
214         else
215             tgt += INSN_LEN;
216         break;
217     case 0x7: /* bgtz */
218     case 0x17: /* bgtzl */
219         if(os_context_register(context, r1)
220            > os_context_register(context, r2))
221             tgt += disp;
222         else
223             tgt += INSN_LEN;
224         break;
225     case 0x10:
226     case 0x11:
227     case 0x12:
228         switch (r1) {
229          /* CP0/CP1/CP2 branches */
230         case 0x08:
231             /* FIXME */
232             tgt += INSN_LEN;
233             break;
234         }
235         break;
236     default:
237         tgt += INSN_LEN;
238         break;
239     }
240     return tgt;
241 }
242
243 void
244 arch_skip_instruction(os_context_t *context)
245 {
246     /* Skip the offending instruction. Don't use os_context_insn here,
247        since in case of a branch we want the branch insn, not the delay
248        slot. */
249     *os_context_pc_addr(context)
250         = (os_context_register_t)
251             next_insn_addr(context,
252                 *(unsigned int *)(os_context_pc(context)));
253 }
254
255 unsigned char *
256 arch_internal_error_arguments(os_context_t *context)
257 {
258     if (os_context_bd_cause(context))
259         return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
260     else
261         return (unsigned char *)(os_context_pc(context) + INSN_LEN);
262 }
263
264 boolean
265 arch_pseudo_atomic_atomic(os_context_t *context)
266 {
267     return os_context_register(context, reg_ALLOC) & 1;
268 }
269
270 void
271 arch_set_pseudo_atomic_interrupted(os_context_t *context)
272 {
273     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
274 }
275
276 void
277 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
278 {
279     *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
280 }
281
282 unsigned int
283 arch_install_breakpoint(void *pc)
284 {
285     unsigned int *ptr = (unsigned int *)pc;
286     unsigned int insn;
287
288     /* Don't install over a branch/jump with delay slot. */
289     if (arch_insn_with_bdelay_p(*ptr))
290         ptr++;
291
292     insn = *ptr;
293     *ptr = (trap_Breakpoint << 6) | 0xd;
294     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
295
296     return insn;
297 }
298
299 static inline unsigned int
300 arch_install_after_breakpoint(void *pc)
301 {
302     unsigned int *ptr = (unsigned int *)pc;
303     unsigned int insn;
304
305     /* Don't install over a branch/jump with delay slot. */
306     if (arch_insn_with_bdelay_p(*ptr))
307         ptr++;
308
309     insn = *ptr;
310     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
311     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
312
313     return insn;
314 }
315
316 void
317 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
318 {
319     unsigned int *ptr = (unsigned int *)pc;
320
321     /* We may remove from a branch delay slot. */
322     if (arch_insn_with_bdelay_p(*ptr))
323         ptr++;
324
325     *ptr = orig_inst;
326     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
327 }
328
329 /* Perform the instruction that we overwrote with a breakpoint. As we
330    don't have a single-step facility, this means we have to:
331    - put the instruction back
332    - put a second breakpoint at the following instruction,
333      set after_breakpoint and continue execution.
334
335    When the second breakpoint is hit (very shortly thereafter, we hope)
336    sigtrap_handler gets called again, but follows the AfterBreakpoint
337    arm, which
338    - puts a bpt back in the first breakpoint place (running across a
339      breakpoint shouldn't cause it to be uninstalled)
340    - replaces the second bpt with the instruction it was meant to be
341    - carries on
342
343    Clear? */
344
345 static unsigned int *skipped_break_addr, displaced_after_inst;
346 static sigset_t orig_sigmask;
347
348 void
349 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
350 {
351     unsigned int *pc = (unsigned int *)os_context_pc(context);
352     unsigned int *next_pc;
353
354     orig_sigmask = *os_context_sigmask_addr(context);
355     sigaddset_blockable(os_context_sigmask_addr(context));
356
357     /* Put the original instruction back. */
358     arch_remove_breakpoint(pc, orig_inst);
359     skipped_break_addr = pc;
360
361     /* Figure out where it goes. */
362     next_pc = (unsigned int *)next_insn_addr(context, *pc);
363     displaced_after_inst = arch_install_after_breakpoint(next_pc);
364 }
365
366 void
367 arch_handle_breakpoint(os_context_t *context)
368 {
369     handle_breakpoint(context);
370 }
371
372 void
373 arch_handle_fun_end_breakpoint(os_context_t *context)
374 {
375     *os_context_pc_addr(context)
376         = (os_context_register_t)(unsigned int)
377         handle_fun_end_breakpoint(context);
378 }
379
380 void
381 arch_handle_after_breakpoint(os_context_t *context)
382 {
383     arch_install_breakpoint(skipped_break_addr);
384     arch_remove_breakpoint((unsigned int *)os_context_pc(context),
385                            displaced_after_inst);
386     *os_context_sigmask_addr(context) = orig_sigmask;
387 }
388
389 void
390 arch_handle_single_step_trap(os_context_t *context, int trap)
391 {
392     unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
393     int register_offset = code >> 11 & 0x1f;
394     handle_single_step_trap(context, trap, register_offset);
395     arch_skip_instruction(context);
396 }
397
398 static void
399 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
400 {
401     os_context_t *context = arch_os_get_context(&void_context);
402     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
403     /* FIXME: This magic number is pseudo-atomic-trap from parms.lisp.
404      * Genesis should provide the proper #define, but it specialcases
405      * pseudo-atomic-trap to work around some oddity on SPARC.
406      * Eventually this should go into handle_trap. */
407     if (code==0x10) {
408         arch_clear_pseudo_atomic_interrupted(context);
409         arch_skip_instruction(context);
410         interrupt_handle_pending(context);
411     } else
412         handle_trap(context,code & 0x1f);
413 }
414
415 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
416
417 static void
418 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
419 {
420     os_context_t *context = arch_os_get_context(&void_context);
421     unsigned int bad_inst = os_context_insn(context);
422     unsigned int op, rs, rt, rd, funct, dest = 32;
423     int immed;
424     int result;
425
426     op = (bad_inst >> 26) & 0x3f;
427     rs = (bad_inst >> 21) & 0x1f;
428     rt = (bad_inst >> 16) & 0x1f;
429     rd = (bad_inst >> 11) & 0x1f;
430     funct = bad_inst & 0x3f;
431     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
432
433     switch (op) {
434     case 0x0: /* SPECIAL */
435         switch (funct) {
436         case 0x20: /* ADD */
437             result = FIXNUM_VALUE(os_context_register(context, rs))
438                 + FIXNUM_VALUE(os_context_register(context, rt));
439             dest = rd;
440             break;
441
442         case 0x22: /* SUB */
443             result = FIXNUM_VALUE(os_context_register(context, rs))
444                 - FIXNUM_VALUE(os_context_register(context, rt));
445             dest = rd;
446             break;
447
448         default:
449             interrupt_handle_now(signal, info, context);
450             return;
451         }
452         break;
453
454     case 0x8: /* ADDI */
455         result = FIXNUM_VALUE(os_context_register(context,rs))
456                     + (immed >> N_FIXNUM_TAG_BITS);
457         dest = rt;
458         break;
459
460     default:
461         interrupt_handle_now(signal, info, context);
462         return;
463     }
464
465     dynamic_space_free_pointer =
466         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
467
468     *os_context_register_addr(context,dest) = alloc_number(result);
469
470     *os_context_register_addr(context, reg_ALLOC) =
471         (unsigned int) dynamic_space_free_pointer;
472
473     arch_skip_instruction(context);
474 }
475
476 void
477 arch_install_interrupt_handlers(void)
478 {
479     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
480     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
481 }
482
483 #ifdef LISP_FEATURE_LINKAGE_TABLE
484
485 /* Linkage tables for MIPS
486
487    Linkage entry size is 16, because we need 4 instructions to implement
488    a jump. The entry size constant is defined in parms.lisp.
489
490    Define the register to use in the linkage jump table. For MIPS this
491    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
492 #define LINKAGE_TEMP_REG        reg_ALLOC
493
494 /* Insert the necessary jump instructions at the given address. */
495 void
496 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
497 {
498   /* Make JMP to function entry. The instruction sequence is:
499        lui    $25, 0, %hi(addr)
500        addiu  $25, $25, %lo(addr)
501        jr     $25
502         nop */
503   unsigned int *insn = (unsigned int *)reloc_addr;
504   unsigned int addr = (unsigned int)target_addr;
505   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
506   unsigned int lo = addr & 0xffff;
507
508   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
509   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
510                  | (LINKAGE_TEMP_REG << 16) | lo);
511   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
512   *insn = 0;
513
514   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
515 }
516
517 void
518 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
519 {
520     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
521 }
522
523 #endif