0.9.9.36:
[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 #include "monitor.h"
27
28 void arch_init(void)
29 {
30     return;
31 }
32
33 os_vm_address_t arch_get_bad_addr(int signal, siginfo_t *siginfo, os_context_t *context)
34 {
35     return siginfo->si_addr;
36 #if 0
37 #ifdef hpux
38     struct save_state *state;
39     os_vm_address_t addr;
40
41     state = (struct save_state *)(&(scp->sc_sl.sl_ss));
42
43     if (state == NULL)
44         return NULL;
45
46     /* Check the instruction address first. */
47     addr = (os_vm_address_t)((unsigned long)scp->sc_pcoq_head & ~3);
48     if (addr < (os_vm_address_t)0x1000)
49         return addr;
50
51     /* Otherwise, it must have been a data fault. */
52     return (os_vm_address_t)state->ss_cr21;
53 #else
54     struct hp800_thread_state *state;
55     os_vm_address_t addr;
56
57     state = (struct hp800_thread_state *)(scp->sc_ap);
58
59     if (state == NULL)
60         return NULL;
61
62     /* Check the instruction address first. */
63     addr = scp->sc_pcoqh & ~3;
64     if (addr < 0x1000)
65         return addr;
66
67     /* Otherwise, it must have been a data fault. */
68     return state->cr21;
69 #endif
70 #endif
71 }
72
73 unsigned char *arch_internal_error_arguments(os_context_t *context)
74 {
75     return (unsigned char *)((*os_context_pc_addr(context) & ~3) + 4);
76 }
77
78 boolean arch_pseudo_atomic_atomic(os_context_t *context)
79 {
80     return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
81 }
82
83 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
84 {
85     *os_context_register_addr(context,reg_ALLOC) |=  1;
86 }
87
88 /* FIXME: untested */
89 void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
90 {
91     *os_context_register_addr(context,reg_ALLOC) &= ~1;
92 }
93
94 void arch_skip_instruction(os_context_t *context)
95 {
96     ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
97     ((char *) *os_context_npc_addr(context)) += 4;
98 }
99
100 unsigned int arch_install_breakpoint(void *pc)
101 {
102     unsigned int *ulpc = (unsigned int *)pc;
103     unsigned int orig_inst = *ulpc;
104
105     *ulpc = trap_Breakpoint;
106     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
107     return orig_inst;
108 }
109
110 void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
111 {
112     unsigned int *ulpc = (unsigned int *)pc;
113
114     *ulpc = orig_inst;
115     os_flush_icache((os_vm_address_t)pc, sizeof(*ulpc));
116 }
117
118 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
119 {
120     /* FIXME: Fill this in */
121 #if 0
122 #ifdef hpux
123     /* We change the next-pc to point to a breakpoint instruction, restore */
124     /* the original instruction, and exit.  We would like to be able to */
125     /* sigreturn, but we can't, because this is hpux. */
126     unsigned int *pc = (unsigned int *)(SC_PC(scp) & ~3);
127
128     NextPc = SC_NPC(scp);
129     SC_NPC(scp) = (unsigned int)SingleStepTraps | (SC_NPC(scp)&3);
130
131     BreakpointAddr = pc;
132     *pc = orig_inst;
133     os_flush_icache((os_vm_address_t)pc, sizeof(unsigned int));
134 #else
135     /* We set the recovery counter to cover one instruction, put the */
136     /* original instruction back in, and then resume.  We will then trap */
137     /* after executing that one instruction, at which time we can put */
138     /* the breakpoint back in. */
139
140     ((struct hp800_thread_state *)scp->sc_ap)->cr0 = 1;
141     scp->sc_ps |= 0x10;
142     *(unsigned int *)SC_PC(scp) = orig_inst;
143
144     sigreturn(scp);
145 #endif
146 #endif
147 }
148
149 #ifdef hpux
150 static void restore_breakpoint(struct sigcontext *scp)
151 {
152     /* We just single-stepped over an instruction that we want to replace */
153     /* with a breakpoint.  So we put the breakpoint back in, and tweek the */
154     /* state so that we will continue as if nothing happened. */
155
156     if (NextPc == NULL)
157         lose("SingleStepBreakpoint trap at strange time.\n");
158
159     if ((SC_PC(scp)&~3) == (unsigned int)SingleStepTraps) {
160         /* The next instruction was not nullified. */
161         SC_PC(scp) = NextPc;
162         if ((SC_NPC(scp)&~3) == (unsigned int)SingleStepTraps + 4) {
163             /* The instruction we just stepped over was not a branch, so */
164             /* we need to fix it up.  If it was a branch, it will point to */
165             /* the correct place. */
166             SC_NPC(scp) = NextPc + 4;
167         }
168     }
169     else {
170         /* The next instruction was nullified, so we want to skip it. */
171         SC_PC(scp) = NextPc + 4;
172         SC_NPC(scp) = NextPc + 8;
173     }
174     NextPc = NULL;
175
176     if (BreakpointAddr) {
177         *BreakpointAddr = trap_Breakpoint;
178         os_flush_icache((os_vm_address_t)BreakpointAddr,
179                         sizeof(unsigned int));
180         BreakpointAddr = NULL;
181     }
182 }
183 #endif
184
185 static void sigtrap_handler(int signal, siginfo_t *siginfo, void *void_context)
186 {
187     os_context_t *context = arch_os_get_context(&void_context);
188     unsigned int bad_inst;
189
190 #if 0
191     printf("sigtrap_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
192            SC_REG(scp,reg_ALLOC));
193 #endif
194
195     bad_inst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
196     if (bad_inst & 0xfc001fe0)
197         interrupt_handle_now(signal, siginfo, context);
198     else {
199         int im5 = bad_inst & 0x1f;
200
201         switch (im5) {
202         case trap_Halt:
203             fake_foreign_function_call(context);
204             lose("%%primitive halt called; the party is over.\n");
205
206         case trap_PendingInterrupt:
207             arch_skip_instruction(context);
208             interrupt_handle_pending(context);
209             break;
210
211         case trap_Error:
212         case trap_Cerror:
213             interrupt_internal_error(signal, siginfo, context, im5==trap_Cerror);
214             break;
215
216         case trap_Breakpoint:
217             /*sigsetmask(scp->sc_mask); */
218             handle_breakpoint(signal, siginfo, context);
219             break;
220
221         case trap_FunEndBreakpoint:
222             /*sigsetmask(scp->sc_mask); */
223             {
224                 unsigned long pc;
225                 pc = (unsigned long)
226                     handle_fun_end_breakpoint(signal, siginfo, context);
227                 *os_context_pc_addr(context) = pc;
228                 *os_context_npc_addr(context) = pc + 4;
229             }
230             break;
231
232         case trap_SingleStepBreakpoint:
233             /* Uh, FIXME */
234 #ifdef hpux
235             restore_breakpoint(context);
236 #endif
237             break;
238
239         default:
240             interrupt_handle_now(signal, siginfo, context);
241             break;
242         }
243     }
244 }
245
246 static void sigfpe_handler(int signal, siginfo_t *siginfo, void *void_context)
247 {
248     os_context_t *context = arch_os_get_context(&void_context);
249     unsigned int badinst;
250     int opcode, r1, r2, t;
251     long op1, op2, res;
252
253 #if 0
254     printf("sigfpe_handler, pc=0x%08x, alloc=0x%08x\n", scp->sc_pcoqh,
255            SC_REG(scp,reg_ALLOC));
256 #endif
257
258     switch (siginfo->si_code) {
259     case FPE_INTOVF: /*I_OVFLO: */
260         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
261         opcode = badinst >> 26;
262
263         if (opcode == 2) {
264             /* reg/reg inst. */
265             r1 = (badinst >> 16) & 0x1f;
266             op1 = fixnum_value(*os_context_register_addr(context, r1));
267             r2 = (badinst >> 21) & 0x1f;
268             op2 = fixnum_value(*os_context_register_addr(context, r2));
269             t = badinst & 0x1f;
270
271             switch ((badinst >> 5) & 0x7f) {
272             case 0x70:
273                 /* Add and trap on overflow. */
274                 res = op1 + op2;
275                 break;
276
277             case 0x60:
278                 /* Subtract and trap on overflow. */
279                 res = op1 - op2;
280                 break;
281
282             default:
283                 goto not_interesting;
284             }
285         }
286         else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
287             /* Add or subtract immediate. */
288             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
289             r2 = (badinst >> 16) & 0x1f;
290             op2 = fixnum_value(*os_context_register_addr(context, r1));
291             t = (badinst >> 21) & 0x1f;
292             if (opcode == 0x2d)
293                 res = op1 + op2;
294             else
295                 res = op1 - op2;
296         }
297         else
298             goto not_interesting;
299
300         /* ?? What happens here if we hit the end of dynamic space? */
301         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
302         *os_context_register_addr(context, t) = alloc_number(res);
303         *os_context_register_addr(context, reg_ALLOC)
304             = (unsigned long) dynamic_space_free_pointer;
305         arch_skip_instruction(context);
306
307         break;
308
309     case 0: /* I_COND: ?? Maybe tagged add?? FIXME */
310         badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
311         if ((badinst&0xfffff800) == (0xb000e000|reg_ALLOC<<21|reg_ALLOC<<16)) {
312             /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped. */
313             /* That means that it is the end of a pseudo-atomic.  So do the */
314             /* add stripping off the pseudo-atomic-interrupted bit, and then */
315             /* tell the machine-independent code to process the pseudo- */
316             /* atomic. */
317             int immed = (badinst>>1)&0x3ff;
318             if (badinst & 1)
319                 immed |= -1<<10;
320             *os_context_register_addr(context, reg_ALLOC) += (immed-1);
321             arch_skip_instruction(context);
322             interrupt_handle_pending(context);
323             break;
324         }
325         /* else drop-through. */
326     default:
327     not_interesting:
328         interrupt_handle_now(signal, siginfo, context);
329     }
330 }
331
332 /* Merrily cut'n'pasted from sigfpe_handler.  On Linux, until
333    2.4.19-pa4 (hopefully), the overflow_trap wasn't implemented,
334    resulting in a SIGBUS instead. We adapt the sigfpe_handler here, in
335    the hope that it will do as a replacement until the new kernel sees
336    the light of day. Since the instructions that we need to fix up
337    tend not to be doing unaligned memory access, this should be a safe
338    workaround.  -- CSR, 2002-08-17 */
339 static void sigbus_handler(int signal, siginfo_t *siginfo, void *void_context)
340 {
341     os_context_t *context = arch_os_get_context(&void_context);
342     unsigned int badinst;
343     int opcode, r1, r2, t;
344     long op1, op2, res;
345
346     badinst = *(unsigned int *)(*os_context_pc_addr(context) & ~3);
347     /* First, test for the pseudo-atomic instruction */
348     if ((badinst & 0xfffff800) == (0xb000e000 |
349                                    reg_ALLOC<<21 |
350                                    reg_ALLOC<<16)) {
351         /* It is an ADDIT,OD i,ALLOC,ALLOC instruction that trapped.
352            That means that it is the end of a pseudo-atomic.  So do
353            the add stripping off the pseudo-atomic-interrupted bit,
354            and then tell the machine-independent code to process the
355            pseudo-atomic. */
356         int immed = (badinst>>1) & 0x3ff;
357         if (badinst & 1)
358             immed |= -1<<10;
359         *os_context_register_addr(context, reg_ALLOC) += (immed-1);
360         arch_skip_instruction(context);
361         interrupt_handle_pending(context);
362         return;
363     } else {
364         opcode = badinst >> 26;
365         if (opcode == 2) {
366             /* reg/reg inst. */
367             r1 = (badinst >> 16) & 0x1f;
368             op1 = fixnum_value(*os_context_register_addr(context, r1));
369             r2 = (badinst >> 21) & 0x1f;
370             op2 = fixnum_value(*os_context_register_addr(context, r2));
371             t = badinst & 0x1f;
372
373             switch ((badinst >> 5) & 0x7f) {
374             case 0x70:
375                 /* Add and trap on overflow. */
376                 res = op1 + op2;
377                 break;
378
379             case 0x60:
380                 /* Subtract and trap on overflow. */
381                 res = op1 - op2;
382                 break;
383
384             default:
385                 goto not_interesting;
386             }
387         } else if ((opcode & 0x37) == 0x25 && (badinst & (1<<11))) {
388             /* Add or subtract immediate. */
389             op1 = ((badinst >> 3) & 0xff) | ((-badinst&1)<<8);
390             r2 = (badinst >> 16) & 0x1f;
391             op2 = fixnum_value(*os_context_register_addr(context, r1));
392             t = (badinst >> 21) & 0x1f;
393             if (opcode == 0x2d)
394                 res = op1 + op2;
395             else
396                 res = op1 - op2;
397         }
398         else
399             goto not_interesting;
400
401         /* ?? What happens here if we hit the end of dynamic space? */
402         dynamic_space_free_pointer = (lispobj *) *os_context_register_addr(context, reg_ALLOC);
403         *os_context_register_addr(context, t) = alloc_number(res);
404         *os_context_register_addr(context, reg_ALLOC)
405             = (unsigned long) dynamic_space_free_pointer;
406         arch_skip_instruction(context);
407
408         return;
409
410     not_interesting:
411         interrupt_handle_now(signal, siginfo, context);
412     }
413 }
414
415
416 void arch_install_interrupt_handlers(void)
417 {
418     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
419     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
420     /* FIXME: beyond 2.4.19-pa4 this shouldn't be necessary. */
421     undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);
422 }
423
424
425 lispobj funcall0(lispobj function)
426 {
427     lispobj *args = current_control_stack_pointer;
428
429     return call_into_lisp(function, args, 0);
430 }
431
432 lispobj funcall1(lispobj function, lispobj arg0)
433 {
434     lispobj *args = current_control_stack_pointer;
435
436     current_control_stack_pointer += 1;
437     args[0] = arg0;
438
439     return call_into_lisp(function, args, 1);
440 }
441
442 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
443 {
444     lispobj *args = current_control_stack_pointer;
445
446     current_control_stack_pointer += 2;
447     args[0] = arg0;
448     args[1] = arg1;
449
450     return call_into_lisp(function, args, 2);
451 }
452
453 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
454 {
455     lispobj *args = current_control_stack_pointer;
456
457     current_control_stack_pointer += 3;
458     args[0] = arg0;
459     args[1] = arg1;
460     args[2] = arg2;
461
462     return call_into_lisp(function, args, 3);
463 }