Fix step instrumentation on ARM.
[sbcl/nyef.git] / src / runtime / arm-arch.c
blob2ffec2be76200614a46d71685acf0ce9eeb6aa87
1 /*
2 * This software is part of the SBCL system. See the README file for
3 * more information.
5 * This software is derived from the CMU CL system, which was
6 * written at Carnegie Mellon University and released into the
7 * public domain. The software is in the public domain and is
8 * provided with absolutely no warranty. See the COPYING and CREDITS
9 * files for more information.
11 #include <stdio.h>
13 #include "sbcl.h"
14 #include "runtime.h"
15 #include "arch.h"
16 #include "globals.h"
17 #include "validate.h"
18 #include "os.h"
19 #include "lispregs.h"
20 #include "signal.h"
21 #include "alloc.h"
22 #include "interrupt.h"
23 #include "interr.h"
24 #include "breakpoint.h"
25 #include "monitor.h"
27 void arch_init(void)
29 return;
32 os_vm_address_t arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
34 return (os_vm_address_t)code->si_addr;
37 void arch_skip_instruction(os_context_t *context)
39 /* KLUDGE: Other platforms check for trap codes and skip inlined
40 * trap/error parameters. We should too. */
42 /* Note that we're doing integer arithmetic here, not pointer. So
43 * the value that the return value of os_context_pc_addr() points
44 * to will be incremented by 4, not 16.
46 *os_context_pc_addr(context) += 4;
49 unsigned char *arch_internal_error_arguments(os_context_t *context)
51 return (unsigned char *)(*os_context_pc_addr(context) + 5);
54 boolean arch_pseudo_atomic_atomic(os_context_t *context)
56 /* FIXME: this foreign_function_call_active test is dubious at
57 * best. If a foreign call is made in a pseudo atomic section
58 * (?) or more likely a pseudo atomic section is in a foreign
59 * call then an interrupt is executed immediately. Maybe it
60 * has to do with C code not maintaining pseudo atomic
61 * properly. MG - 2005-08-10
63 * The foreign_function_call_active used to live at each call-site
64 * to arch_pseudo_atomic_atomic, but this seems clearer.
65 * --NS 2007-05-15 */
66 return (!foreign_function_call_active)
67 && (NIL != SymbolValue(PSEUDO_ATOMIC_ATOMIC,0));
70 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
72 /* 0x000f0001 is the syscall number for BREAK_POINT. */
73 SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED,MAKE_FIXNUM(0x000f0001),0);
76 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
78 SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED,NIL,0);
81 unsigned int arch_install_breakpoint(void *pc)
83 /* FIXME: Implement. */
85 return 0;
88 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
90 /* FIXME: Implement. */
93 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
95 /* FIXME: Implement. */
98 #if 0
99 static int pseudo_atomic_trap_p(os_context_t *context)
101 unsigned int* pc;
102 unsigned int badinst;
103 int result;
106 pc = (unsigned int*) *os_context_pc_addr(context);
107 badinst = *pc;
108 result = 0;
110 /* Check to see if the current instruction is a pseudo-atomic-trap */
111 if (((badinst >> 30) == 2) && (((badinst >> 19) & 0x3f) == 0x3a)
112 && (((badinst >> 13) & 1) == 1) && ((badinst & 0x7f) == PSEUDO_ATOMIC_TRAP))
114 unsigned int previnst;
115 previnst = pc[-1];
117 * Check to see if the previous instruction was an andcc alloc-tn,
118 * 3, zero-tn instruction.
120 if (((previnst >> 30) == 2) && (((previnst >> 19) & 0x3f) == 0x11)
121 && (((previnst >> 14) & 0x1f) == reg_ALLOC)
122 && (((previnst >> 25) & 0x1f) == reg_ZERO)
123 && (((previnst >> 13) & 1) == 1)
124 && ((previnst & 0x1fff) == 3))
126 result = 1;
128 else
130 fprintf(stderr, "Oops! Got a PSEUDO-ATOMIC-TRAP without a preceeding andcc!\n");
133 return result;
135 #endif
137 void
138 arch_handle_breakpoint(os_context_t *context)
140 handle_breakpoint(context);
143 void
144 arch_handle_fun_end_breakpoint(os_context_t *context)
146 *os_context_pc_addr(context) = (int) handle_fun_end_breakpoint(context);
149 #if 0
150 void
151 arch_handle_after_breakpoint(os_context_t *context)
153 *skipped_break_addr = trap_Breakpoint;
154 os_flush_icache(skipped_break_addr, sizeof(unsigned int));
155 skipped_break_addr = NULL;
156 *(unsigned long *) os_context_pc_addr(context) = displaced_after_inst;
157 /* context->sigmask = orig_sigmask; */
158 os_flush_icache((os_vm_address_t) os_context_pc_addr(context), sizeof(unsigned int));
160 #endif
162 void
163 arch_handle_single_step_trap(os_context_t *context, int trap)
165 unsigned char register_offset =
166 *((unsigned char *)(*os_context_pc_addr(context))+5);
167 handle_single_step_trap(context, trap, register_offset);
168 /* KLUDGE: arch_skip_instruction() only skips one instruction, and
169 * there is a following word to deal with as well, so skip
170 * twice. */
171 arch_skip_instruction(context);
172 arch_skip_instruction(context);
175 #if 0
176 static void sigill_handler(int signal, siginfo_t *siginfo,
177 os_context_t *context)
179 if ((siginfo->si_code) == ILL_ILLOPC
180 #ifdef LISP_FEATURE_LINUX
181 || (linux_sparc_siginfo_bug && (siginfo->si_code == 2))
182 #endif
184 int trap;
185 unsigned int inst;
186 unsigned int* pc = (unsigned int*) siginfo->si_addr;
188 inst = *pc;
189 trap = inst & 0x1f;
190 handle_trap(context,trap);
192 else if ((siginfo->si_code) == ILL_ILLTRP
193 #ifdef LISP_FEATURE_LINUX
194 || (linux_sparc_siginfo_bug && (siginfo->si_code) == 192)
195 #endif
197 if (pseudo_atomic_trap_p(context)) {
198 /* A trap instruction from a pseudo-atomic. We just need
199 to fixup up alloc-tn to remove the interrupted flag,
200 skip over the trap instruction, and then handle the
201 pending interrupt(s). */
202 arch_clear_pseudo_atomic_interrupted(context);
203 arch_skip_instruction(context);
204 interrupt_handle_pending(context);
206 else {
207 interrupt_internal_error(context, 0);
210 else {
211 interrupt_handle_now(signal, siginfo, context);
215 static void sigemt_handler(int signal, siginfo_t *siginfo,
216 os_context_t *context)
218 unsigned int badinst;
219 boolean subtract, immed;
220 int rd, rs1, op1, rs2, op2, result;
222 badinst = *(unsigned int *)os_context_pc_addr(context);
223 if ((badinst >> 30) != 2 || ((badinst >> 20) & 0x1f) != 0x11) {
224 /* It wasn't a tagged add. Pass the signal into lisp. */
225 interrupt_handle_now(signal, siginfo, context);
226 return;
229 fprintf(stderr, "SIGEMT trap handler with tagged op instruction!\n");
231 /* Extract the parts of the inst. */
232 subtract = badinst & (1<<19);
233 rs1 = (badinst>>14) & 0x1f;
234 op1 = *os_context_register_addr(context, rs1);
236 /* If the first arg is $ALLOC then it is really a signal-pending note */
237 /* for the pseudo-atomic noise. */
238 if (rs1 == reg_ALLOC) {
239 /* Perform the op anyway. */
240 op2 = badinst & 0x1fff;
241 if (op2 & (1<<12))
242 op2 |= -1<<13;
243 if (subtract)
244 result = op1 - op2;
245 else
246 result = op1 + op2;
247 /* KLUDGE: this & ~7 is a little bit magical but basically
248 clears pseudo_atomic bits if any */
249 *os_context_register_addr(context, reg_ALLOC) = result & ~7;
250 arch_skip_instruction(context);
251 interrupt_handle_pending(context);
252 return;
255 if ((op1 & 3) != 0) {
256 /* The first arg wan't a fixnum. */
257 interrupt_internal_error(context, 0);
258 return;
261 if (immed = badinst & (1<<13)) {
262 op2 = badinst & 0x1fff;
263 if (op2 & (1<<12))
264 op2 |= -1<<13;
266 else {
267 rs2 = badinst & 0x1f;
268 op2 = *os_context_register_addr(context, rs2);
271 if ((op2 & 3) != 0) {
272 /* The second arg wan't a fixnum. */
273 interrupt_internal_error(context, 0);
274 return;
277 rd = (badinst>>25) & 0x1f;
278 if (rd != 0) {
279 /* Don't bother computing the result unless we are going to use it. */
280 if (subtract)
281 result = (op1>>2) - (op2>>2);
282 else
283 result = (op1>>2) + (op2>>2);
285 dynamic_space_free_pointer =
286 (lispobj *) *os_context_register_addr(context, reg_ALLOC);
288 *os_context_register_addr(context, rd) = alloc_number(result);
290 *os_context_register_addr(context, reg_ALLOC) =
291 (unsigned long) dynamic_space_free_pointer;
294 arch_skip_instruction(context);
296 #endif
298 static void
299 sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
301 unsigned int code = *((unsigned char *)(4+*os_context_pc_addr(context)));
302 u32 trap_instruction = *((u32 *)*os_context_pc_addr(context));
303 int condition_bits = (trap_instruction >> 28) & 0x0f;
305 /* Make sure that we're looking at an SWI instruction or that one
306 * undefined instruction that the kernel recognizes as an explicit
307 * trap. */
308 if ((condition_bits == 15)
309 || (((trap_instruction & 0x0f000000) != 0x0f000000)
310 && (trap_instruction != 0xe7f001f0))) {
311 lose("Unrecognized trap instruction %08lx in sigtrap_handler()",
312 trap_instruction);
315 switch (condition_bits) {
317 case 11: /* LT */
318 /* This is a handle-pending-interrupt trap. */
319 arch_clear_pseudo_atomic_interrupted(context);
320 arch_skip_instruction(context);
321 interrupt_handle_pending(context);
322 break;
324 case 14: /* AL */
325 /* This is a generic trap. */
326 handle_trap(context, code);
327 break;
329 default:
330 /* This is something that we don't recognize and therefore
331 * should not happen. */
332 lose("Unknown trap condition bits 0x%x", condition_bits);
336 void arch_install_interrupt_handlers()
338 #if 0
339 undoably_install_low_level_interrupt_handler(SIGILL , sigill_handler);
340 undoably_install_low_level_interrupt_handler(SIGEMT, sigemt_handler);
341 #endif
342 undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
346 #ifdef LISP_FEATURE_LINKAGE_TABLE
348 /* Linkage tables
350 * Linkage entry size is 16, because we need 4 instructions.
353 #define LINKAGE_TEMP_REG reg_NFP
355 void arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
358 ldr reg, [pc, #4]
359 bx reg
361 address
363 BX is needed for thumb interworking, without it it could take just two words with
364 ldr pc, [pc, #-4]
365 address
367 int* inst_ptr;
368 unsigned inst;
370 inst_ptr = (int*) reloc_addr;
372 // ldr reg, [pc, #4]
373 inst = 0xe59f0000 | LINKAGE_TEMP_REG << 12 | 4;
374 *inst_ptr++ = inst;
376 // bx reg
377 inst = 0xe12fff10 | LINKAGE_TEMP_REG;
378 *inst_ptr++ = inst;
380 // nop aka mov r0, r0
381 inst = 0xe1a00000;
382 *inst_ptr++ = inst;
384 // address
385 *inst_ptr++ = target_addr;
387 os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
390 void
391 arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
393 *(unsigned long *)reloc_addr = (unsigned long)target_addr;
396 #endif