1.0.5.49: interrupt & GC & PA 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     /* 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
85      *
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);
91 }
92
93 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
94 {
95     *os_context_register_addr(context,reg_ALLOC) |=  1;
96 }
97
98 /* FIXME: untested */
99 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
100 {
101     *os_context_register_addr(context,reg_ALLOC) &= ~1;
102 }
103
104 void arch_skip_instruction(os_context_t *context)
105 {
106     ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
107     ((char *) *os_context_npc_addr(context)) += 4;
108 }
109
110 unsigned int arch_install_breakpoint(void *pc)
111 {
112     unsigned int *ulpc = (unsigned int *)pc;
113     unsigned int orig_inst = *ulpc;
114
115     *ulpc = trap_Breakpoint;
116     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
117     return orig_inst;
118 }
119
120 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
121 {
122     unsigned int *ulpc = (unsigned int *)pc;
123
124     *ulpc = orig_inst;
125     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
126 }
127
128 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
129 {
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);
137
138     NextPc = SC_NPC(scp);
139     SC_NPC(scp) = (unsigned int)SingleStepTraps | (SC_NPC(scp)&3);
140
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. */
149
150     ((struct hp800_thread_state *)scp->sc_ap)->cr0 = 1;
151     scp->sc_ps |= 0x10;
152     *(unsigned int *)SC_PC(scp) = orig_inst;
153
154     sigreturn(scp);
155 #endif
156 #endif
157 }
158
159 #ifdef hpux
160 static void restore_breakpoint(struct sigcontext *scp)
161 {
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. */
165
166     if (NextPc == NULL)
167         lose("SingleStepBreakpoint trap at strange time.\n");
168
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;
177         }
178     }
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;
183     }
184     NextPc = NULL;
185
186     if (BreakpointAddr) {
187         *BreakpointAddr = trap_Breakpoint;
188         os_flush_icache((os_vm_address_t)BreakpointAddr,
189                         sizeof(unsigned int));
190         BreakpointAddr = NULL;
191     }
192 }
193 #endif
194
195 void
196 arch_handle_breakpoint(os_context_t *context)
197 {
198     /*sigsetmask(scp->sc_mask); */
199     handle_breakpoint(context);
200 }
201
202 void
203 arch_handle_fun_end_breakpoint(os_context_t *context)
204 {
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;
211 }
212
213 static void
214 sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
215 {
216     os_context_t *context = arch_os_get_context(&void_context);
217     unsigned int bad_inst;
218
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
223
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);
230     }
231 }
232
233 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
234 {
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;
239
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
244
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;
249
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;
257
258             switch ((badinst >> 5) & 0x7f) {
259             case 0x70:
260                 /* Add and trap on overflow. */
261                 res = op1 + op2;
262                 break;
263
264             case 0x60:
265                 /* Subtract and trap on overflow. */
266                 res = op1 - op2;
267                 break;
268
269             default:
270                 goto not_interesting;
271             }
272         }
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;
283         }
284         else
285             goto not_interesting;
286
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);
293
294         break;
295
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;
311         }
312         /* else drop-through. */
313     default:
314     not_interesting:
315         interrupt_handle_now(signal, siginfo, context);
316     }
317 }
318
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)
327 {
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;
332
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;
359
360             switch ((badinst >> 5) & 0x7f) {
361             case 0x70:
362                 /* Add and trap on overflow. */
363                 res = op1 + op2;
364                 break;
365
366             case 0x60:
367                 /* Subtract and trap on overflow. */
368                 res = op1 - op2;
369                 break;
370
371             default:
372                 goto not_interesting;
373             }
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;
384         }
385         else
386             goto not_interesting;
387
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);
394
395         return;
396
397     not_interesting:
398         interrupt_handle_now(signal, siginfo, context);
399     }
400 }
401
402
403 void arch_install_interrupt_handlers(void)
404 {
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);
409 }