1.0.5.37: cleanup a small thinko from previous x86-assem.S refactoring
[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         handle_trap(context, trap);
219     }
220 }
221
222 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
223 {
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;
228
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
233
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;
238
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;
246
247             switch ((badinst >> 5) & 0x7f) {
248             case 0x70:
249                 /* Add and trap on overflow. */
250                 res = op1 + op2;
251                 break;
252
253             case 0x60:
254                 /* Subtract and trap on overflow. */
255                 res = op1 - op2;
256                 break;
257
258             default:
259                 goto not_interesting;
260             }
261         }
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;
272         }
273         else
274             goto not_interesting;
275
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);
282
283         break;
284
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;
300         }
301         /* else drop-through. */
302     default:
303     not_interesting:
304         interrupt_handle_now(signal, siginfo, context);
305     }
306 }
307
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)
316 {
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;
321
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;
348
349             switch ((badinst >> 5) & 0x7f) {
350             case 0x70:
351                 /* Add and trap on overflow. */
352                 res = op1 + op2;
353                 break;
354
355             case 0x60:
356                 /* Subtract and trap on overflow. */
357                 res = op1 - op2;
358                 break;
359
360             default:
361                 goto not_interesting;
362             }
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;
373         }
374         else
375             goto not_interesting;
376
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);
383
384         return;
385
386     not_interesting:
387         interrupt_handle_now(signal, siginfo, context);
388     }
389 }
390
391
392 void arch_install_interrupt_handlers(void)
393 {
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);
398 }