8fba41671f440a9fbed8ff09fa56b681d29bcccc
[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 static void sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
185 {
186     os_context_t *context = arch_os_get_context(&void_context);
187     unsigned int bad_inst;
188
189 #if 0
190     printf("sigtrap_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
191            SC_REG(scp,reg_ALLOC));
192 #endif
193
194     bad_inst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
195     if (bad_inst & 0xfc001fe0)
196         interrupt_handle_now(signal, siginfo, context);
197     else {
198         int im5 = bad_inst & 0x1f;
199
200         switch (im5) {
201         case trap_Halt:
202             fake_foreign_function_call(context);
203             lose("%%primitive halt called; the party is over.\n");
204
205         case trap_PendingInterrupt:
206             arch_skip_instruction(context);
207             interrupt_handle_pending(context);
208             break;
209
210         case trap_Error:
211         case trap_Cerror:
212             interrupt_internal_error(signal, siginfo, context, im5==trap_Cerror);
213             break;
214
215         case trap_Breakpoint:
216             /*sigsetmask(scp->sc_mask); */
217             handle_breakpoint(signal, siginfo, context);
218             break;
219
220         case trap_FunEndBreakpoint:
221             /*sigsetmask(scp->sc_mask); */
222             {
223                 unsigned long pc;
224                 pc = (unsigned long)
225                     handle_fun_end_breakpoint(signal, siginfo, context);
226                 *os_context_pc_addr(context) = pc;
227                 *os_context_npc_addr(context) = pc + 4;
228             }
229             break;
230
231         case trap_SingleStepBreakpoint:
232             /* Uh, FIXME */
233 #ifdef hpux
234             restore_breakpoint(context);
235 #endif
236             break;
237
238         default:
239             interrupt_handle_now(signal, siginfo, context);
240             break;
241         }
242     }
243 }
244
245 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
246 {
247     os_context_t *context = arch_os_get_context(&void_context);
248     unsigned int badinst;
249     int opcode, r1, r2, t;
250     long op1, op2, res;
251
252 #if 0
253     printf("sigfpe_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
254            SC_REG(scp,reg_ALLOC));
255 #endif
256
257     switch (siginfo->si_code) {
258     case FPE_INTOVF: /*I_OVFLO: */
259         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
260         opcode = badinst >> 26;
261
262         if (opcode == 2) {
263             /* reg/reg inst. */
264             r1 = (badinst >> 16) & 0x1f;
265             op1 = fixnum_value(*os_context_register_addr(context, r1));
266             r2 = (badinst >> 21) & 0x1f;
267             op2 = fixnum_value(*os_context_register_addr(context, r2));
268             t = badinst & 0x1f;
269
270             switch ((badinst >> 5) & 0x7f) {
271             case 0x70:
272                 /* Add and trap on overflow. */
273                 res = op1 + op2;
274                 break;
275
276             case 0x60:
277                 /* Subtract and trap on overflow. */
278                 res = op1 - op2;
279                 break;
280
281             default:
282                 goto not_interesting;
283             }
284         }
285         else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
286             /* Add or subtract immediate. */
287             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
288             r2 = (badinst >> 16) & 0x1f;
289             op2 = fixnum_value(*os_context_register_addr(context, r1));
290             t = (badinst >> 21) & 0x1f;
291             if (opcode == 0x2d)
292                 res = op1 + op2;
293             else
294                 res = op1 - op2;
295         }
296         else
297             goto not_interesting;
298
299         /* ?? What happens here if we hit the end of dynamic space? */
300         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
301         *os_context_register_addr(context, t) = alloc_number(res);
302         *os_context_register_addr(context, reg_ALLOC)
303             = (unsigned long) dynamic_space_free_pointer;
304         arch_skip_instruction(context);
305
306         break;
307
308     case 0: /* I_COND: ?? Maybe tagged add?? FIXME */
309         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
310         if ((badinst&0xfffff800) == (0xb000e000|reg_ALLOC<<21|reg_ALLOC<<16)) {
311             /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped. */
312             /* That means that it is the end of a pseudo-atomic.  So do the */
313             /* add stripping off the pseudo-atomic-interrupted bit, and then */
314             /* tell the machine-independent code to process the pseudo- */
315             /* atomic. */
316             int immed = (badinst>>1)&0x3ff;
317             if (badinst & 1)
318                 immed |= -1<<10;
319             *os_context_register_addr(context, reg_ALLOC) += (immed-1);
320             arch_skip_instruction(context);
321             interrupt_handle_pending(context);
322             break;
323         }
324         /* else drop-through. */
325     default:
326     not_interesting:
327         interrupt_handle_now(signal, siginfo, context);
328     }
329 }
330
331 /* Merrily cut'n'pasted from sigfpe_handler.  On Linux, until
332    2.4.19-pa4 (hopefully), the overflow_trap wasn't implemented,
333    resulting in a SIGBUS instead. We adapt the sigfpe_handler here, in
334    the hope that it will do as a replacement until the new kernel sees
335    the light of day. Since the instructions that we need to fix up
336    tend not to be doing unaligned memory access, this should be a safe
337    workaround.  -- CSR, 2002-08-17 */
338 static void sigbus_handler(int signal, siginfo_t *siginfo, void *void_context)
339 {
340     os_context_t *context = arch_os_get_context(&void_context);
341     unsigned int badinst;
342     int opcode, r1, r2, t;
343     long op1, op2, res;
344
345     badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
346     /* First, test for the pseudo-atomic instruction */
347     if ((badinst & 0xfffff800) == (0xb000e000 |
348                                    reg_ALLOC<<21 |
349                                    reg_ALLOC<<16)) {
350         /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped.
351            That means that it is the end of a pseudo-atomic.  So do
352            the add stripping off the pseudo-atomic-interrupted bit,
353            and then tell the machine-independent code to process the
354            pseudo-atomic. */
355         int immed = (badinst>>1) & 0x3ff;
356         if (badinst & 1)
357             immed |= -1<<10;
358         *os_context_register_addr(context, reg_ALLOC) += (immed-1);
359         arch_skip_instruction(context);
360         interrupt_handle_pending(context);
361         return;
362     } else {
363         opcode = badinst >> 26;
364         if (opcode == 2) {
365             /* reg/reg inst. */
366             r1 = (badinst >> 16) & 0x1f;
367             op1 = fixnum_value(*os_context_register_addr(context, r1));
368             r2 = (badinst >> 21) & 0x1f;
369             op2 = fixnum_value(*os_context_register_addr(context, r2));
370             t = badinst & 0x1f;
371
372             switch ((badinst >> 5) & 0x7f) {
373             case 0x70:
374                 /* Add and trap on overflow. */
375                 res = op1 + op2;
376                 break;
377
378             case 0x60:
379                 /* Subtract and trap on overflow. */
380                 res = op1 - op2;
381                 break;
382
383             default:
384                 goto not_interesting;
385             }
386         } else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
387             /* Add or subtract immediate. */
388             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
389             r2 = (badinst >> 16) & 0x1f;
390             op2 = fixnum_value(*os_context_register_addr(context, r1));
391             t = (badinst >> 21) & 0x1f;
392             if (opcode == 0x2d)
393                 res = op1 + op2;
394             else
395                 res = op1 - op2;
396         }
397         else
398             goto not_interesting;
399
400         /* ?? What happens here if we hit the end of dynamic space? */
401         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
402         *os_context_register_addr(context, t) = alloc_number(res);
403         *os_context_register_addr(context, reg_ALLOC)
404             = (unsigned long) dynamic_space_free_pointer;
405         arch_skip_instruction(context);
406
407         return;
408
409     not_interesting:
410         interrupt_handle_now(signal, siginfo, context);
411     }
412 }
413
414
415 void arch_install_interrupt_handlers(void)
416 {
417     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
418     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
419     /* FIXME: beyond 2.4.19-pa4 this shouldn't be necessary. */
420     undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);
421 }
422
423
424 lispobj funcall0(lispobj function)
425 {
426     lispobj *args = current_control_stack_pointer;
427
428     return call_into_lisp(function, args, 0);
429 }
430
431 lispobj funcall1(lispobj function, lispobj arg0)
432 {
433     lispobj *args = current_control_stack_pointer;
434
435     current_control_stack_pointer += 1;
436     args[0] = arg0;
437
438     return call_into_lisp(function, args, 1);
439 }
440
441 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
442 {
443     lispobj *args = current_control_stack_pointer;
444
445     current_control_stack_pointer += 2;
446     args[0] = arg0;
447     args[1] = arg1;
448
449     return call_into_lisp(function, args, 2);
450 }
451
452 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
453 {
454     lispobj *args = current_control_stack_pointer;
455
456     current_control_stack_pointer += 3;
457     args[0] = arg0;
458     args[1] = arg1;
459     args[2] = arg2;
460
461     return call_into_lisp(function, args, 3);
462 }