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