0.9.4.70:
[sbcl.git] / src / runtime / mips-arch.c
blob2774fa253e1b9a42e72a263fc290f0c8ef4e211d
1 /*
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.
6 */
8 #include <stdio.h>
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"
24 #include "genesis/constants.h"
26 #define INSN_LEN 4
28 void
29 arch_init()
31 return;
34 os_vm_address_t
35 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
37 /* Classic CMUCL comment:
39 Finding the bad address on the mips is easy. */
40 return (os_vm_address_t)siginfo->si_addr;
43 static inline unsigned int
44 os_context_register(os_context_t *context, int offset)
46 return (unsigned int)(*os_context_register_addr(context, offset));
49 static inline unsigned int
50 os_context_pc(os_context_t *context)
52 return (unsigned int)(*os_context_pc_addr(context));
55 static inline unsigned int
56 os_context_insn(os_context_t *context)
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));
64 boolean
65 arch_insn_with_bdelay_p(unsigned int insn)
67 switch (insn >> 26) {
68 case 0x0:
69 switch (insn & 0x3f) {
70 /* register jumps */
71 case 0x08:
72 case 0x09:
73 return 1;
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;
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;
105 break;
106 /* branch likely (MIPS II) */
107 case 0x14:
108 case 0x15:
109 case 0x16:
110 case 0x17:
111 return 1;
113 return 0;
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)
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);
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;
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;
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;
204 return tgt;
207 void
208 arch_skip_instruction(os_context_t *context)
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)));
218 unsigned char *
219 arch_internal_error_arguments(os_context_t *context)
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);
227 boolean
228 arch_pseudo_atomic_atomic(os_context_t *context)
230 return os_context_register(context, reg_ALLOC) & 1;
233 void
234 arch_set_pseudo_atomic_interrupted(os_context_t *context)
236 *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
239 unsigned long
240 arch_install_breakpoint(void *pc)
242 unsigned int *ptr = (unsigned int *)pc;
243 unsigned int insn = *ptr;
244 unsigned long result;
246 /* Don't install over a branch/jump with delay slot. */
247 if (arch_insn_with_bdelay_p(insn))
248 ptr++;
250 result = (unsigned long)insn;
251 *ptr = (trap_Breakpoint << 6) | 0xd;
252 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
254 return result;
257 void
258 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
260 unsigned int *ptr = (unsigned int *)pc;
262 *ptr = (unsigned int) orig_inst;
263 os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
266 static unsigned int *skipped_break_addr, displaced_after_inst;
267 static sigset_t orig_sigmask;
269 void
270 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
272 unsigned int *pc = (unsigned int *)os_context_pc(context);
273 unsigned int *break_pc, *next_pc;
274 unsigned int next_inst;
276 orig_sigmask = *os_context_sigmask_addr(context);
277 sigaddset_blockable(os_context_sigmask_addr(context));
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;
288 /* Put the original instruction back. */
289 arch_remove_breakpoint(break_pc, orig_inst);
290 skipped_break_addr = break_pc;
292 /* Figure out where it goes. */
293 next_pc = (unsigned int *)emulate_branch(context, next_inst);
295 displaced_after_inst = arch_install_breakpoint(next_pc);
298 static void
299 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
301 os_context_t *context = arch_os_get_context(&void_context);
302 unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
304 switch (code) {
305 case trap_Halt:
306 fake_foreign_function_call(context);
307 lose("%%primitive halt called; the party is over.\n");
309 case trap_PendingInterrupt:
310 arch_skip_instruction(context);
311 interrupt_handle_pending(context);
312 break;
314 case trap_Error:
315 case trap_Cerror:
316 interrupt_internal_error(signal, info, context, code == trap_Cerror);
317 break;
319 case trap_Breakpoint:
320 handle_breakpoint(signal, info, context);
321 break;
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;
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;
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;
342 default:
343 interrupt_handle_now(signal, info, context);
344 break;
348 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
350 static void
351 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
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;
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;
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;
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;
381 default:
382 interrupt_handle_now(signal, info, context);
383 return;
385 break;
387 case 0x8: /* ADDI */
388 result = FIXNUM_VALUE(os_context_register(context,rs))
389 + (immed >> N_FIXNUM_TAG_BITS);
390 dest = rt;
391 break;
393 default:
394 interrupt_handle_now(signal, info, context);
395 return;
398 dynamic_space_free_pointer =
399 (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
401 *os_context_register_addr(context,dest) = alloc_number(result);
403 *os_context_register_addr(context, reg_ALLOC) =
404 (unsigned int) dynamic_space_free_pointer;
406 arch_skip_instruction(context);
409 void
410 arch_install_interrupt_handlers()
412 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
413 undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
416 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
418 lispobj
419 funcall0(lispobj function)
421 lispobj *args = current_control_stack_pointer;
423 return call_into_lisp(function, args, 0);
426 lispobj
427 funcall1(lispobj function, lispobj arg0)
429 lispobj *args = current_control_stack_pointer;
431 current_control_stack_pointer += 1;
432 args[0] = arg0;
434 return call_into_lisp(function, args, 1);
437 lispobj
438 funcall2(lispobj function, lispobj arg0, lispobj arg1)
440 lispobj *args = current_control_stack_pointer;
442 current_control_stack_pointer += 2;
443 args[0] = arg0;
444 args[1] = arg1;
446 return call_into_lisp(function, args, 2);
449 lispobj
450 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
452 lispobj *args = current_control_stack_pointer;
454 current_control_stack_pointer += 3;
455 args[0] = arg0;
456 args[1] = arg1;
457 args[2] = arg2;
459 return call_into_lisp(function, args, 3);