1.0.4.13: refactor trap handling
[sbcl.git] / src / runtime / hppa-arch.c
1 /*
2  * This software is part of the SBCL system. See the README file for
3  * more information.
4  *
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.
10  */
11 #include <stdio.h>
12
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"
26
27 void arch_init(void)
28 {
29     return;
30 }
31
32 os_vm_address_t arch_get_bad_addr(int signal, siginfo_t *siginfo, os_context_t *context)
33 {
34     return siginfo->si_addr;
35 #if 0
36 #ifdef hpux
37     struct save_state *state;
38     os_vm_address_t addr;
39
40     state = (struct save_state *)(&(scp->sc_sl.sl_ss));
41
42     if (state == NULL)
43         return NULL;
44
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;
49
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;
55
56     state = (struct hp800_thread_state *)(scp->sc_ap);
57
58     if (state == NULL)
59         return NULL;
60
61     /* Check the instruction address first. */
62     addr = scp->sc_pcoqh & ~3;
63     if (addr < 0x1000)
64         return addr;
65
66     /* Otherwise, it must have been a data fault. */
67     return state->cr21;
68 #endif
69 #endif
70 }
71
72 unsigned char *arch_internal_error_arguments(os_context_t *context)
73 {
74     return (unsigned char *)((*os_context_pc_addr(context) & ~3) + 4);
75 }
76
77 boolean arch_pseudo_atomic_atomic(os_context_t *context)
78 {
79     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
80 }
81
82 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
83 {
84     *os_context_register_addr(context,reg_ALLOC) |=  1;
85 }
86
87 /* FIXME: untested */
88 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
89 {
90     *os_context_register_addr(context,reg_ALLOC) &= ~1;
91 }
92
93 void arch_skip_instruction(os_context_t *context)
94 {
95     ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
96     ((char *) *os_context_npc_addr(context)) += 4;
97 }
98
99 unsigned int arch_install_breakpoint(void *pc)
100 {
101     unsigned int *ulpc = (unsigned int *)pc;
102     unsigned int orig_inst = *ulpc;
103
104     *ulpc = trap_Breakpoint;
105     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
106     return orig_inst;
107 }
108
109 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
110 {
111     unsigned int *ulpc = (unsigned int *)pc;
112
113     *ulpc = orig_inst;
114     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
115 }
116
117 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
118 {
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);
126
127     NextPc = SC_NPC(scp);
128     SC_NPC(scp) = (unsigned int)SingleStepTraps | (SC_NPC(scp)&3);
129
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. */
138
139     ((struct hp800_thread_state *)scp->sc_ap)->cr0 = 1;
140     scp->sc_ps |= 0x10;
141     *(unsigned int *)SC_PC(scp) = orig_inst;
142
143     sigreturn(scp);
144 #endif
145 #endif
146 }
147
148 #ifdef hpux
149 static void restore_breakpoint(struct sigcontext *scp)
150 {
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. */
154
155     if (NextPc == NULL)
156         lose("SingleStepBreakpoint trap at strange time.\n");
157
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;
166         }
167     }
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;
172     }
173     NextPc = NULL;
174
175     if (BreakpointAddr) {
176         *BreakpointAddr = trap_Breakpoint;
177         os_flush_icache((os_vm_address_t)BreakpointAddr,
178                         sizeof(unsigned int));
179         BreakpointAddr = NULL;
180     }
181 }
182 #endif
183
184 void
185 arch_handle_breakpoint(os_context_t *context)
186 {
187     /*sigsetmask(scp->sc_mask); */
188     handle_breakpoint(context);
189 }
190
191 void
192 arch_handle_fun_end_breakpoint(os_context_t *context)
193 {
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;
200 }
201
202 static void
203 sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
204 {
205     os_context_t *context = arch_os_get_context(&void_context);
206     unsigned int bad_inst;
207
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
212
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         if (!maybe_handle_trap(context, trap))
219             interrupt_handle_now(signal, sigingo, context);
220     }
221 }
222
223 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
224 {
225     os_context_t *context = arch_os_get_context(&void_context);
226     unsigned int badinst;
227     int opcode, r1, r2, t;
228     long op1, op2, res;
229
230 #if 0
231     printf("sigfpe_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
232            SC_REG(scp,reg_ALLOC));
233 #endif
234
235     switch (siginfo->si_code) {
236     case FPE_INTOVF: /*I_OVFLO: */
237         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
238         opcode = badinst >> 26;
239
240         if (opcode == 2) {
241             /* reg/reg inst. */
242             r1 = (badinst >> 16) & 0x1f;
243             op1 = fixnum_value(*os_context_register_addr(context, r1));
244             r2 = (badinst >> 21) & 0x1f;
245             op2 = fixnum_value(*os_context_register_addr(context, r2));
246             t = badinst & 0x1f;
247
248             switch ((badinst >> 5) & 0x7f) {
249             case 0x70:
250                 /* Add and trap on overflow. */
251                 res = op1 + op2;
252                 break;
253
254             case 0x60:
255                 /* Subtract and trap on overflow. */
256                 res = op1 - op2;
257                 break;
258
259             default:
260                 goto not_interesting;
261             }
262         }
263         else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
264             /* Add or subtract immediate. */
265             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
266             r2 = (badinst >> 16) & 0x1f;
267             op2 = fixnum_value(*os_context_register_addr(context, r1));
268             t = (badinst >> 21) & 0x1f;
269             if (opcode == 0x2d)
270                 res = op1 + op2;
271             else
272                 res = op1 - op2;
273         }
274         else
275             goto not_interesting;
276
277         /* ?? What happens here if we hit the end of dynamic space? */
278         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
279         *os_context_register_addr(context, t) = alloc_number(res);
280         *os_context_register_addr(context, reg_ALLOC)
281             = (unsigned long) dynamic_space_free_pointer;
282         arch_skip_instruction(context);
283
284         break;
285
286     case 0: /* I_COND: ?? Maybe tagged add?? FIXME */
287         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
288         if ((badinst&0xfffff800) == (0xb000e000|reg_ALLOC<<21|reg_ALLOC<<16)) {
289             /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped. */
290             /* That means that it is the end of a pseudo-atomic.  So do the */
291             /* add stripping off the pseudo-atomic-interrupted bit, and then */
292             /* tell the machine-independent code to process the pseudo- */
293             /* atomic. */
294             int immed = (badinst>>1)&0x3ff;
295             if (badinst & 1)
296                 immed |= -1<<10;
297             *os_context_register_addr(context, reg_ALLOC) += (immed-1);
298             arch_skip_instruction(context);
299             interrupt_handle_pending(context);
300             break;
301         }
302         /* else drop-through. */
303     default:
304     not_interesting:
305         interrupt_handle_now(signal, siginfo, context);
306     }
307 }
308
309 /* Merrily cut'n'pasted from sigfpe_handler.  On Linux, until
310    2.4.19-pa4 (hopefully), the overflow_trap wasn't implemented,
311    resulting in a SIGBUS instead. We adapt the sigfpe_handler here, in
312    the hope that it will do as a replacement until the new kernel sees
313    the light of day. Since the instructions that we need to fix up
314    tend not to be doing unaligned memory access, this should be a safe
315    workaround.  -- CSR, 2002-08-17 */
316 static void sigbus_handler(int signal, siginfo_t *siginfo, void *void_context)
317 {
318     os_context_t *context = arch_os_get_context(&void_context);
319     unsigned int badinst;
320     int opcode, r1, r2, t;
321     long op1, op2, res;
322
323     badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
324     /* First, test for the pseudo-atomic instruction */
325     if ((badinst & 0xfffff800) == (0xb000e000 |
326                                    reg_ALLOC<<21 |
327                                    reg_ALLOC<<16)) {
328         /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped.
329            That means that it is the end of a pseudo-atomic.  So do
330            the add stripping off the pseudo-atomic-interrupted bit,
331            and then tell the machine-independent code to process the
332            pseudo-atomic. */
333         int immed = (badinst>>1) & 0x3ff;
334         if (badinst & 1)
335             immed |= -1<<10;
336         *os_context_register_addr(context, reg_ALLOC) += (immed-1);
337         arch_skip_instruction(context);
338         interrupt_handle_pending(context);
339         return;
340     } else {
341         opcode = badinst >> 26;
342         if (opcode == 2) {
343             /* reg/reg inst. */
344             r1 = (badinst >> 16) & 0x1f;
345             op1 = fixnum_value(*os_context_register_addr(context, r1));
346             r2 = (badinst >> 21) & 0x1f;
347             op2 = fixnum_value(*os_context_register_addr(context, r2));
348             t = badinst & 0x1f;
349
350             switch ((badinst >> 5) & 0x7f) {
351             case 0x70:
352                 /* Add and trap on overflow. */
353                 res = op1 + op2;
354                 break;
355
356             case 0x60:
357                 /* Subtract and trap on overflow. */
358                 res = op1 - op2;
359                 break;
360
361             default:
362                 goto not_interesting;
363             }
364         } else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
365             /* Add or subtract immediate. */
366             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
367             r2 = (badinst >> 16) & 0x1f;
368             op2 = fixnum_value(*os_context_register_addr(context, r1));
369             t = (badinst >> 21) & 0x1f;
370             if (opcode == 0x2d)
371                 res = op1 + op2;
372             else
373                 res = op1 - op2;
374         }
375         else
376             goto not_interesting;
377
378         /* ?? What happens here if we hit the end of dynamic space? */
379         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
380         *os_context_register_addr(context, t) = alloc_number(res);
381         *os_context_register_addr(context, reg_ALLOC)
382             = (unsigned long) dynamic_space_free_pointer;
383         arch_skip_instruction(context);
384
385         return;
386
387     not_interesting:
388         interrupt_handle_now(signal, siginfo, context);
389     }
390 }
391
392
393 void arch_install_interrupt_handlers(void)
394 {
395     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
396     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
397     /* FIXME: beyond 2.4.19-pa4 this shouldn't be necessary. */
398     undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);
399 }
400
401
402 lispobj funcall0(lispobj function)
403 {
404     lispobj *args = current_control_stack_pointer;
405
406     return call_into_lisp(function, args, 0);
407 }
408
409 lispobj funcall1(lispobj function, lispobj arg0)
410 {
411     lispobj *args = current_control_stack_pointer;
412
413     current_control_stack_pointer += 1;
414     args[0] = arg0;
415
416     return call_into_lisp(function, args, 1);
417 }
418
419 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
420 {
421     lispobj *args = current_control_stack_pointer;
422
423     current_control_stack_pointer += 2;
424     args[0] = arg0;
425     args[1] = arg1;
426
427     return call_into_lisp(function, args, 2);
428 }
429
430 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
431 {
432     lispobj *args = current_control_stack_pointer;
433
434     current_control_stack_pointer += 3;
435     args[0] = arg0;
436     args[1] = arg1;
437     args[2] = arg2;
438
439     return call_into_lisp(function, args, 3);
440 }