1.0.5.35: stack alignment on x86/Darwin, once more
[sbcl.git] / src / runtime / hppa-arch.c
blob46fdb3468befd84334e1984c371bcf7c8c4844f5
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 /* Copied from sparc-arch.c. Not all of these are necessary, probably */
14 #include "sbcl.h"
15 #include "runtime.h"
16 #include "arch.h"
17 #include "globals.h"
18 #include "validate.h"
19 #include "os.h"
20 #include "lispregs.h"
21 #include "signal.h"
22 #include "alloc.h"
23 #include "interrupt.h"
24 #include "interr.h"
25 #include "breakpoint.h"
27 void arch_init(void)
29 return;
32 os_vm_address_t arch_get_bad_addr(int signal, siginfo_t *siginfo, os_context_t *context)
34 return siginfo->si_addr;
35 #if 0
36 #ifdef hpux
37 struct save_state *state;
38 os_vm_address_t addr;
40 state = (struct save_state *)(&(scp->sc_sl.sl_ss));
42 if (state == NULL)
43 return NULL;
45 /* Check the instruction address first. */
46 addr = (os_vm_address_t)((unsigned long)scp->sc_pcoq_head & ~3);
47 if (addr < (os_vm_address_t)0x1000)
48 return addr;
50 /* Otherwise, it must have been a data fault. */
51 return (os_vm_address_t)state->ss_cr21;
52 #else
53 struct hp800_thread_state *state;
54 os_vm_address_t addr;
56 state = (struct hp800_thread_state *)(scp->sc_ap);
58 if (state == NULL)
59 return NULL;
61 /* Check the instruction address first. */
62 addr = scp->sc_pcoqh & ~3;
63 if (addr < 0x1000)
64 return addr;
66 /* Otherwise, it must have been a data fault. */
67 return state->cr21;
68 #endif
69 #endif
72 unsigned char *arch_internal_error_arguments(os_context_t *context)
74 return (unsigned char *)((*os_context_pc_addr(context) & ~3) + 4);
77 boolean arch_pseudo_atomic_atomic(os_context_t *context)
79 return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
82 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
84 *os_context_register_addr(context,reg_ALLOC) |= 1;
87 /* FIXME: untested */
88 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
90 *os_context_register_addr(context,reg_ALLOC) &= ~1;
93 void arch_skip_instruction(os_context_t *context)
95 ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
96 ((char *) *os_context_npc_addr(context)) += 4;
99 unsigned int arch_install_breakpoint(void *pc)
101 unsigned int *ulpc = (unsigned int *)pc;
102 unsigned int orig_inst = *ulpc;
104 *ulpc = trap_Breakpoint;
105 os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
106 return orig_inst;
109 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
111 unsigned int *ulpc = (unsigned int *)pc;
113 *ulpc = orig_inst;
114 os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
117 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
119 /* FIXME: Fill this in */
120 #if 0
121 #ifdef hpux
122 /* We change the next-pc to point to a breakpoint instruction, restore */
123 /* the original instruction, and exit. We would like to be able to */
124 /* sigreturn, but we can't, because this is hpux. */
125 unsigned int *pc = (unsigned int *)(SC_PC(scp) & ~3);
127 NextPc = SC_NPC(scp);
128 SC_NPC(scp) = (unsigned int)SingleStepTraps | (SC_NPC(scp)&3);
130 BreakpointAddr = pc;
131 *pc = orig_inst;
132 os_flush_icache((os_vm_address_t)pc, sizeof(unsigned int));
133 #else
134 /* We set the recovery counter to cover one instruction, put the */
135 /* original instruction back in, and then resume. We will then trap */
136 /* after executing that one instruction, at which time we can put */
137 /* the breakpoint back in. */
139 ((struct hp800_thread_state *)scp->sc_ap)->cr0 = 1;
140 scp->sc_ps |= 0x10;
141 *(unsigned int *)SC_PC(scp) = orig_inst;
143 sigreturn(scp);
144 #endif
145 #endif
148 #ifdef hpux
149 static void restore_breakpoint(struct sigcontext *scp)
151 /* We just single-stepped over an instruction that we want to replace */
152 /* with a breakpoint. So we put the breakpoint back in, and tweek the */
153 /* state so that we will continue as if nothing happened. */
155 if (NextPc == NULL)
156 lose("SingleStepBreakpoint trap at strange time.\n");
158 if ((SC_PC(scp)&~3) == (unsigned int)SingleStepTraps) {
159 /* The next instruction was not nullified. */
160 SC_PC(scp) = NextPc;
161 if ((SC_NPC(scp)&~3) == (unsigned int)SingleStepTraps + 4) {
162 /* The instruction we just stepped over was not a branch, so */
163 /* we need to fix it up. If it was a branch, it will point to */
164 /* the correct place. */
165 SC_NPC(scp) = NextPc + 4;
168 else {
169 /* The next instruction was nullified, so we want to skip it. */
170 SC_PC(scp) = NextPc + 4;
171 SC_NPC(scp) = NextPc + 8;
173 NextPc = NULL;
175 if (BreakpointAddr) {
176 *BreakpointAddr = trap_Breakpoint;
177 os_flush_icache((os_vm_address_t)BreakpointAddr,
178 sizeof(unsigned int));
179 BreakpointAddr = NULL;
182 #endif
184 void
185 arch_handle_breakpoint(os_context_t *context)
187 /*sigsetmask(scp->sc_mask); */
188 handle_breakpoint(context);
191 void
192 arch_handle_fun_end_breakpoint(os_context_t *context)
194 /*sigsetmask(scp->sc_mask); */
195 unsigned long pc;
196 pc = (unsigned long)
197 handle_fun_end_breakpoint(context);
198 *os_context_pc_addr(context) = pc;
199 *os_context_npc_addr(context) = pc + 4;
202 static void
203 sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
205 os_context_t *context = arch_os_get_context(&void_context);
206 unsigned int bad_inst;
208 #if 0
209 printf("sigtrap_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
210 SC_REG(scp,reg_ALLOC));
211 #endif
213 bad_inst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
214 if (bad_inst & 0xfc001fe0)
215 interrupt_handle_now(signal, siginfo, context);
216 else {
217 int im5 = bad_inst & 0x1f;
218 handle_trap(context, trap);
222 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
224 os_context_t *context = arch_os_get_context(&void_context);
225 unsigned int badinst;
226 int opcode, r1, r2, t;
227 long op1, op2, res;
229 #if 0
230 printf("sigfpe_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
231 SC_REG(scp,reg_ALLOC));
232 #endif
234 switch (siginfo->si_code) {
235 case FPE_INTOVF: /*I_OVFLO: */
236 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
237 opcode = badinst >> 26;
239 if (opcode == 2) {
240 /* reg/reg inst. */
241 r1 = (badinst >> 16) & 0x1f;
242 op1 = fixnum_value(*os_context_register_addr(context, r1));
243 r2 = (badinst >> 21) & 0x1f;
244 op2 = fixnum_value(*os_context_register_addr(context, r2));
245 t = badinst & 0x1f;
247 switch ((badinst >> 5) & 0x7f) {
248 case 0x70:
249 /* Add and trap on overflow. */
250 res = op1 + op2;
251 break;
253 case 0x60:
254 /* Subtract and trap on overflow. */
255 res = op1 - op2;
256 break;
258 default:
259 goto not_interesting;
262 else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
263 /* Add or subtract immediate. */
264 op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
265 r2 = (badinst >> 16) & 0x1f;
266 op2 = fixnum_value(*os_context_register_addr(context, r1));
267 t = (badinst >> 21) & 0x1f;
268 if (opcode == 0x2d)
269 res = op1 + op2;
270 else
271 res = op1 - op2;
273 else
274 goto not_interesting;
276 /* ?? What happens here if we hit the end of dynamic space? */
277 dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
278 *os_context_register_addr(context, t) = alloc_number(res);
279 *os_context_register_addr(context, reg_ALLOC)
280 = (unsigned long) dynamic_space_free_pointer;
281 arch_skip_instruction(context);
283 break;
285 case 0: /* I_COND: ?? Maybe tagged add?? FIXME */
286 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
287 if ((badinst&0xfffff800) == (0xb000e000|reg_ALLOC<<21|reg_ALLOC<<16)) {
288 /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped. */
289 /* That means that it is the end of a pseudo-atomic. So do the */
290 /* add stripping off the pseudo-atomic-interrupted bit, and then */
291 /* tell the machine-independent code to process the pseudo- */
292 /* atomic. */
293 int immed = (badinst>>1)&0x3ff;
294 if (badinst & 1)
295 immed |= -1<<10;
296 *os_context_register_addr(context, reg_ALLOC) += (immed-1);
297 arch_skip_instruction(context);
298 interrupt_handle_pending(context);
299 break;
301 /* else drop-through. */
302 default:
303 not_interesting:
304 interrupt_handle_now(signal, siginfo, context);
308 /* Merrily cut'n'pasted from sigfpe_handler. On Linux, until
309 2.4.19-pa4 (hopefully), the overflow_trap wasn't implemented,
310 resulting in a SIGBUS instead. We adapt the sigfpe_handler here, in
311 the hope that it will do as a replacement until the new kernel sees
312 the light of day. Since the instructions that we need to fix up
313 tend not to be doing unaligned memory access, this should be a safe
314 workaround. -- CSR, 2002-08-17 */
315 static void sigbus_handler(int signal, siginfo_t *siginfo, void *void_context)
317 os_context_t *context = arch_os_get_context(&void_context);
318 unsigned int badinst;
319 int opcode, r1, r2, t;
320 long op1, op2, res;
322 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
323 /* First, test for the pseudo-atomic instruction */
324 if ((badinst & 0xfffff800) == (0xb000e000 |
325 reg_ALLOC<<21 |
326 reg_ALLOC<<16)) {
327 /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped.
328 That means that it is the end of a pseudo-atomic. So do
329 the add stripping off the pseudo-atomic-interrupted bit,
330 and then tell the machine-independent code to process the
331 pseudo-atomic. */
332 int immed = (badinst>>1) & 0x3ff;
333 if (badinst & 1)
334 immed |= -1<<10;
335 *os_context_register_addr(context, reg_ALLOC) += (immed-1);
336 arch_skip_instruction(context);
337 interrupt_handle_pending(context);
338 return;
339 } else {
340 opcode = badinst >> 26;
341 if (opcode == 2) {
342 /* reg/reg inst. */
343 r1 = (badinst >> 16) & 0x1f;
344 op1 = fixnum_value(*os_context_register_addr(context, r1));
345 r2 = (badinst >> 21) & 0x1f;
346 op2 = fixnum_value(*os_context_register_addr(context, r2));
347 t = badinst & 0x1f;
349 switch ((badinst >> 5) & 0x7f) {
350 case 0x70:
351 /* Add and trap on overflow. */
352 res = op1 + op2;
353 break;
355 case 0x60:
356 /* Subtract and trap on overflow. */
357 res = op1 - op2;
358 break;
360 default:
361 goto not_interesting;
363 } else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
364 /* Add or subtract immediate. */
365 op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
366 r2 = (badinst >> 16) & 0x1f;
367 op2 = fixnum_value(*os_context_register_addr(context, r1));
368 t = (badinst >> 21) & 0x1f;
369 if (opcode == 0x2d)
370 res = op1 + op2;
371 else
372 res = op1 - op2;
374 else
375 goto not_interesting;
377 /* ?? What happens here if we hit the end of dynamic space? */
378 dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
379 *os_context_register_addr(context, t) = alloc_number(res);
380 *os_context_register_addr(context, reg_ALLOC)
381 = (unsigned long) dynamic_space_free_pointer;
382 arch_skip_instruction(context);
384 return;
386 not_interesting:
387 interrupt_handle_now(signal, siginfo, context);
392 void arch_install_interrupt_handlers(void)
394 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
395 undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
396 /* FIXME: beyond 2.4.19-pa4 this shouldn't be necessary. */
397 undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);