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