1.0.18.29: documentation tweaks
[sbcl/tcr.git] / src / runtime / hppa-arch.c
blobbf9a4645461ecab6f21186a996255cb238047866
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 /* FIXME: this foreign_function_call_active test is dubious at
80 * best. If a foreign call is made in a pseudo atomic section
81 * (?) or more likely a pseudo atomic section is in a foreign
82 * call then an interrupt is executed immediately. Maybe it
83 * has to do with C code not maintaining pseudo atomic
84 * properly. MG - 2005-08-10
86 * The foreign_function_call_active used to live at each call-site
87 * to arch_pseudo_atomic_atomic, but this seems clearer.
88 * --NS 2007-05-15 */
89 return (!foreign_function_call_active)
90 && ((*os_context_register_addr(context,reg_ALLOC)) & 4);
93 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
95 *os_context_register_addr(context,reg_ALLOC) |= 1;
98 /* FIXME: untested */
99 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
101 *os_context_register_addr(context,reg_ALLOC) &= ~1;
104 void arch_skip_instruction(os_context_t *context)
106 ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
107 ((char *) *os_context_npc_addr(context)) += 4;
110 unsigned int arch_install_breakpoint(void *pc)
112 unsigned int *ulpc = (unsigned int *)pc;
113 unsigned int orig_inst = *ulpc;
115 *ulpc = trap_Breakpoint;
116 os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
117 return orig_inst;
120 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
122 unsigned int *ulpc = (unsigned int *)pc;
124 *ulpc = orig_inst;
125 os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
128 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
130 /* FIXME: Fill this in */
131 #if 0
132 #ifdef hpux
133 /* We change the next-pc to point to a breakpoint instruction, restore */
134 /* the original instruction, and exit. We would like to be able to */
135 /* sigreturn, but we can't, because this is hpux. */
136 unsigned int *pc = (unsigned int *)(SC_PC(scp) & ~3);
138 NextPc = SC_NPC(scp);
139 SC_NPC(scp) = (unsigned int)SingleStepTraps | (SC_NPC(scp)&3);
141 BreakpointAddr = pc;
142 *pc = orig_inst;
143 os_flush_icache((os_vm_address_t)pc, sizeof(unsigned int));
144 #else
145 /* We set the recovery counter to cover one instruction, put the */
146 /* original instruction back in, and then resume. We will then trap */
147 /* after executing that one instruction, at which time we can put */
148 /* the breakpoint back in. */
150 ((struct hp800_thread_state *)scp->sc_ap)->cr0 = 1;
151 scp->sc_ps |= 0x10;
152 *(unsigned int *)SC_PC(scp) = orig_inst;
154 sigreturn(scp);
155 #endif
156 #endif
159 #ifdef hpux
160 static void restore_breakpoint(struct sigcontext *scp)
162 /* We just single-stepped over an instruction that we want to replace */
163 /* with a breakpoint. So we put the breakpoint back in, and tweek the */
164 /* state so that we will continue as if nothing happened. */
166 if (NextPc == NULL)
167 lose("SingleStepBreakpoint trap at strange time.\n");
169 if ((SC_PC(scp)&~3) == (unsigned int)SingleStepTraps) {
170 /* The next instruction was not nullified. */
171 SC_PC(scp) = NextPc;
172 if ((SC_NPC(scp)&~3) == (unsigned int)SingleStepTraps + 4) {
173 /* The instruction we just stepped over was not a branch, so */
174 /* we need to fix it up. If it was a branch, it will point to */
175 /* the correct place. */
176 SC_NPC(scp) = NextPc + 4;
179 else {
180 /* The next instruction was nullified, so we want to skip it. */
181 SC_PC(scp) = NextPc + 4;
182 SC_NPC(scp) = NextPc + 8;
184 NextPc = NULL;
186 if (BreakpointAddr) {
187 *BreakpointAddr = trap_Breakpoint;
188 os_flush_icache((os_vm_address_t)BreakpointAddr,
189 sizeof(unsigned int));
190 BreakpointAddr = NULL;
193 #endif
195 void
196 arch_handle_breakpoint(os_context_t *context)
198 /*sigsetmask(scp->sc_mask); */
199 handle_breakpoint(context);
202 void
203 arch_handle_fun_end_breakpoint(os_context_t *context)
205 /*sigsetmask(scp->sc_mask); */
206 unsigned long pc;
207 pc = (unsigned long)
208 handle_fun_end_breakpoint(context);
209 *os_context_pc_addr(context) = pc;
210 *os_context_npc_addr(context) = pc + 4;
213 static void
214 sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
216 os_context_t *context = arch_os_get_context(&void_context);
217 unsigned int bad_inst;
219 #if 0
220 printf("sigtrap_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
221 SC_REG(scp,reg_ALLOC));
222 #endif
224 bad_inst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
225 if (bad_inst & 0xfc001fe0)
226 interrupt_handle_now(signal, siginfo, context);
227 else {
228 int im5 = bad_inst & 0x1f;
229 handle_trap(context, trap);
233 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
235 os_context_t *context = arch_os_get_context(&void_context);
236 unsigned int badinst;
237 int opcode, r1, r2, t;
238 long op1, op2, res;
240 #if 0
241 printf("sigfpe_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
242 SC_REG(scp,reg_ALLOC));
243 #endif
245 switch (siginfo->si_code) {
246 case FPE_INTOVF: /*I_OVFLO: */
247 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
248 opcode = badinst >> 26;
250 if (opcode == 2) {
251 /* reg/reg inst. */
252 r1 = (badinst >> 16) & 0x1f;
253 op1 = fixnum_value(*os_context_register_addr(context, r1));
254 r2 = (badinst >> 21) & 0x1f;
255 op2 = fixnum_value(*os_context_register_addr(context, r2));
256 t = badinst & 0x1f;
258 switch ((badinst >> 5) & 0x7f) {
259 case 0x70:
260 /* Add and trap on overflow. */
261 res = op1 + op2;
262 break;
264 case 0x60:
265 /* Subtract and trap on overflow. */
266 res = op1 - op2;
267 break;
269 default:
270 goto not_interesting;
273 else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
274 /* Add or subtract immediate. */
275 op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
276 r2 = (badinst >> 16) & 0x1f;
277 op2 = fixnum_value(*os_context_register_addr(context, r1));
278 t = (badinst >> 21) & 0x1f;
279 if (opcode == 0x2d)
280 res = op1 + op2;
281 else
282 res = op1 - op2;
284 else
285 goto not_interesting;
287 /* ?? What happens here if we hit the end of dynamic space? */
288 dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
289 *os_context_register_addr(context, t) = alloc_number(res);
290 *os_context_register_addr(context, reg_ALLOC)
291 = (unsigned long) dynamic_space_free_pointer;
292 arch_skip_instruction(context);
294 break;
296 case 0: /* I_COND: ?? Maybe tagged add?? FIXME */
297 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
298 if ((badinst&0xfffff800) == (0xb000e000|reg_ALLOC<<21|reg_ALLOC<<16)) {
299 /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped. */
300 /* That means that it is the end of a pseudo-atomic. So do the */
301 /* add stripping off the pseudo-atomic-interrupted bit, and then */
302 /* tell the machine-independent code to process the pseudo- */
303 /* atomic. */
304 int immed = (badinst>>1)&0x3ff;
305 if (badinst & 1)
306 immed |= -1<<10;
307 *os_context_register_addr(context, reg_ALLOC) += (immed-1);
308 arch_skip_instruction(context);
309 interrupt_handle_pending(context);
310 break;
312 /* else drop-through. */
313 default:
314 not_interesting:
315 interrupt_handle_now(signal, siginfo, context);
319 /* Merrily cut'n'pasted from sigfpe_handler. On Linux, until
320 2.4.19-pa4 (hopefully), the overflow_trap wasn't implemented,
321 resulting in a SIGBUS instead. We adapt the sigfpe_handler here, in
322 the hope that it will do as a replacement until the new kernel sees
323 the light of day. Since the instructions that we need to fix up
324 tend not to be doing unaligned memory access, this should be a safe
325 workaround. -- CSR, 2002-08-17 */
326 static void sigbus_handler(int signal, siginfo_t *siginfo, void *void_context)
328 os_context_t *context = arch_os_get_context(&void_context);
329 unsigned int badinst;
330 int opcode, r1, r2, t;
331 long op1, op2, res;
333 badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
334 /* First, test for the pseudo-atomic instruction */
335 if ((badinst & 0xfffff800) == (0xb000e000 |
336 reg_ALLOC<<21 |
337 reg_ALLOC<<16)) {
338 /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped.
339 That means that it is the end of a pseudo-atomic. So do
340 the add stripping off the pseudo-atomic-interrupted bit,
341 and then tell the machine-independent code to process the
342 pseudo-atomic. */
343 int immed = (badinst>>1) & 0x3ff;
344 if (badinst & 1)
345 immed |= -1<<10;
346 *os_context_register_addr(context, reg_ALLOC) += (immed-1);
347 arch_skip_instruction(context);
348 interrupt_handle_pending(context);
349 return;
350 } else {
351 opcode = badinst >> 26;
352 if (opcode == 2) {
353 /* reg/reg inst. */
354 r1 = (badinst >> 16) & 0x1f;
355 op1 = fixnum_value(*os_context_register_addr(context, r1));
356 r2 = (badinst >> 21) & 0x1f;
357 op2 = fixnum_value(*os_context_register_addr(context, r2));
358 t = badinst & 0x1f;
360 switch ((badinst >> 5) & 0x7f) {
361 case 0x70:
362 /* Add and trap on overflow. */
363 res = op1 + op2;
364 break;
366 case 0x60:
367 /* Subtract and trap on overflow. */
368 res = op1 - op2;
369 break;
371 default:
372 goto not_interesting;
374 } else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
375 /* Add or subtract immediate. */
376 op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
377 r2 = (badinst >> 16) & 0x1f;
378 op2 = fixnum_value(*os_context_register_addr(context, r1));
379 t = (badinst >> 21) & 0x1f;
380 if (opcode == 0x2d)
381 res = op1 + op2;
382 else
383 res = op1 - op2;
385 else
386 goto not_interesting;
388 /* ?? What happens here if we hit the end of dynamic space? */
389 dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
390 *os_context_register_addr(context, t) = alloc_number(res);
391 *os_context_register_addr(context, reg_ALLOC)
392 = (unsigned long) dynamic_space_free_pointer;
393 arch_skip_instruction(context);
395 return;
397 not_interesting:
398 interrupt_handle_now(signal, siginfo, context);
403 void arch_install_interrupt_handlers(void)
405 undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
406 undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
407 /* FIXME: beyond 2.4.19-pa4 this shouldn't be necessary. */
408 undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);