0.7.4.18: Fixing Alpha fixes
[sbcl.git] / src / runtime / sparc-arch.c
1 /*
2
3  $Header$
4
5  This code was written as part of the CMU Common Lisp project at
6  Carnegie Mellon University, and has been placed in the public domain.
7
8 */
9
10 #include <stdio.h>
11
12 #include "runtime.h"
13 #include "arch.h"
14 #include "sbcl.h"
15 #include "globals.h"
16 #include "validate.h"
17 #include "os.h"
18 #include "lispregs.h"
19 #include "signal.h"
20 #include "alloc.h"
21 #include "interrupt.h"
22 #include "interr.h"
23 #include "breakpoint.h"
24 #include "monitor.h"
25
26 #ifdef linux
27 extern int early_kernel;
28 #endif
29
30 void arch_init(void)
31 {
32     return;
33 }
34
35 os_vm_address_t arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
36 {
37     unsigned long badinst;
38     unsigned long *pc;
39     int rs1; 
40
41     pc = (unsigned long *)(*os_context_pc_addr(context));
42
43     /* On the sparc, we have to decode the instruction. */
44
45     /* Make sure it's not the pc thats bogus, and that it was lisp code */
46     /* that caused the fault. */
47     if ((unsigned long) pc & 3) {
48       /* Unaligned */
49       return NULL;
50     }
51     if ((pc < READ_ONLY_SPACE_START || 
52          pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
53         (pc < current_dynamic_space ||
54          pc >= current_dynamic_space + DYNAMIC_SPACE_SIZE)) {
55       return NULL;
56     }
57
58     badinst = *pc;
59
60     if ((badinst >> 30) != 3)
61         /* All load/store instructions have op = 11 (binary) */
62         return 0;
63
64     rs1 = (badinst>>14)&0x1f;
65
66     if (badinst & (1<<13)) {
67         /* r[rs1] + simm(13) */
68         int simm13 = badinst & 0x1fff;
69
70         if (simm13 & (1<<12))
71             simm13 |= -1<<13;
72
73         return (os_vm_address_t)
74           (*os_context_register_addr(context, rs1)+simm13);
75     }
76     else {
77         /* r[rs1] + r[rs2] */
78         int rs2 = badinst & 0x1f;
79
80         return (os_vm_address_t)
81           (*os_context_register_addr(context, rs1) + 
82            *os_context_register_addr(context, rs2));
83     }
84 }
85
86 void arch_skip_instruction(os_context_t *context)
87 {
88   ((char *) *os_context_pc_addr(context)) = ((char *) *os_context_npc_addr(context));
89   ((char *) *os_context_npc_addr(context)) += 4;
90 }
91
92 unsigned char *arch_internal_error_arguments(os_context_t *context)
93 {
94   return (unsigned char *)(*os_context_pc_addr(context) + 4);
95 }
96
97 boolean arch_pseudo_atomic_atomic(os_context_t *context)
98 {
99   return ((*os_context_register_addr(context,reg_ALLOC)) & 4);
100 }
101
102 void arch_set_pseudo_atomic_interrupted(os_context_t *context)
103 {
104   *os_context_register_addr(context,reg_ALLOC) |=  1;
105 }
106
107 unsigned long arch_install_breakpoint(void *pc)
108 {
109   unsigned long *ptr = (unsigned long *)pc;
110   unsigned long result = *ptr;
111   *ptr = trap_Breakpoint;
112   
113   os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
114   
115   return result;
116 }
117
118 void arch_remove_breakpoint(void *pc, unsigned long orig_inst)
119 {
120   *(unsigned long *)pc = orig_inst;
121   os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
122 }
123
124 static unsigned long *skipped_break_addr, displaced_after_inst;
125 static sigset_t orig_sigmask;
126
127 void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
128 {
129   unsigned long *pc = (unsigned long *)(*os_context_pc_addr(context));
130   /* FIXME */
131   unsigned long *npc = (unsigned long *)(*os_context_npc_addr(context));
132
133   /*  orig_sigmask = context->sigmask;
134       sigemptyset(&context->sigmask); */
135   /* FIXME!!! */
136   /* FILLBLOCKSET(&context->uc_sigmask);*/
137
138   *pc = orig_inst;
139   os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
140   skipped_break_addr = pc;
141   displaced_after_inst = *npc;
142   *npc = trap_AfterBreakpoint;
143   os_flush_icache((os_vm_address_t) npc, sizeof(unsigned long));
144
145 }
146
147 static int pseudo_atomic_trap_p(os_context_t *context)
148 {
149   unsigned int* pc;
150   unsigned int badinst;
151   int result;
152   
153   
154   pc = (unsigned int*) *os_context_pc_addr(context);
155   badinst = *pc;
156   result = 0;
157
158   /* Check to see if the current instruction is a pseudo-atomic-trap */
159   if (((badinst >> 30) == 2) && (((badinst >> 19) & 0x3f) == 0x3a)
160       && (((badinst >> 13) & 1) == 1) && ((badinst & 0x7f) == PSEUDO_ATOMIC_TRAP))
161     {
162       unsigned int previnst;
163       previnst = pc[-1];
164       /*
165        * Check to see if the previous instruction was an andcc alloc-tn,
166        * 3, zero-tn instruction.
167        */
168       if (((previnst >> 30) == 2) && (((previnst >> 19) & 0x3f) == 0x11)
169           && (((previnst >> 14) & 0x1f) == reg_ALLOC)
170           && (((previnst >> 25) & 0x1f) == reg_ZERO)
171           && (((previnst >> 13) & 1) == 1)
172           && ((previnst & 0x1fff) == 3))
173         {
174           result = 1;
175         }
176       else
177         {
178           fprintf(stderr, "Oops!  Got a PSEUDO-ATOMIC-TRAP without a preceeding andcc!\n");
179         }
180     }
181   return result;
182 }
183
184 static void sigill_handler(int signal, siginfo_t *siginfo, void *void_context)
185 {
186   os_context_t *context = arch_os_get_context(&void_context);
187
188   sigprocmask(SIG_SETMASK, os_context_sigmask_addr(context), 0);
189
190   if ((siginfo->si_code) == ILL_ILLOPC
191 #ifdef linux
192       || (early_kernel && (siginfo->si_code == 2))
193 #endif
194       ) {
195     int trap;
196     unsigned int inst;
197     unsigned int* pc = (unsigned int*) siginfo->si_addr;
198
199     inst = *pc;
200     trap = inst & 0x3fffff;
201     
202     switch (trap) {
203     case trap_PendingInterrupt:
204       arch_skip_instruction(context);
205       interrupt_handle_pending(context);
206       break;
207
208     case trap_Halt:
209       fake_foreign_function_call(context);
210       lose("%%primitive halt called; the party is over.\n");
211       
212     case trap_Error:
213     case trap_Cerror:
214       interrupt_internal_error(signal, siginfo, context, trap == trap_Cerror);
215       break;
216
217     case trap_Breakpoint:
218       handle_breakpoint(signal, siginfo, context);
219       break;
220
221     case trap_FunEndBreakpoint:
222       *os_context_pc_addr(context) = (int) handle_fun_end_breakpoint(signal, siginfo, context);
223       *os_context_npc_addr(context) = *os_context_pc_addr(context) + 4;
224       break;
225
226     case trap_AfterBreakpoint:
227       *skipped_break_addr = trap_Breakpoint;
228       skipped_break_addr = NULL;
229       *(unsigned long *) os_context_pc_addr(context) = displaced_after_inst;
230       /* context->sigmask = orig_sigmask; */
231       os_flush_icache((os_vm_address_t) os_context_pc_addr(context), sizeof(unsigned long));
232       break;
233       
234     default:
235       interrupt_handle_now(signal, siginfo, context);
236       break;
237     }
238   }
239   else if ((siginfo->si_code) == ILL_ILLTRP
240 #ifdef linux
241            || (early_kernel && (siginfo->si_code) == 192)
242 #endif
243            ) {
244     if (pseudo_atomic_trap_p(context)) {
245       /* A trap instruction from a pseudo-atomic.  We just need
246          to fixup up alloc-tn to remove the interrupted flag,
247          skip over the trap instruction, and then handle the
248          pending interrupt(s). */
249       *os_context_register_addr(context, reg_ALLOC) &= ~7;
250       arch_skip_instruction(context);
251       interrupt_handle_pending(context);
252     }
253     else {
254       interrupt_internal_error(signal, siginfo, context, 0);
255     }
256   }
257   else {
258     interrupt_handle_now(signal, siginfo, context);
259   }
260 }
261
262 static void sigemt_handler(int signal, siginfo_t *siginfo, void *void_context)
263 {
264   unsigned long badinst;
265   boolean subtract, immed;
266   int rd, rs1, op1, rs2, op2, result;
267   os_context_t *context = arch_os_get_context(&void_context);
268
269   badinst = *(unsigned long *)os_context_pc_addr(context);
270   if ((badinst >> 30) != 2 || ((badinst >> 20) & 0x1f) != 0x11) {
271     /* It wasn't a tagged add.  Pass the signal into lisp. */
272     interrupt_handle_now(signal, siginfo, context);
273     return;
274   }
275
276   fprintf(stderr, "SIGEMT trap handler with tagged op instruction!\n");
277   
278   /* Extract the parts of the inst. */
279   subtract = badinst & (1<<19);
280   rs1 = (badinst>>14) & 0x1f;
281   op1 = *os_context_register_addr(context, rs1);
282   
283   /* If the first arg is $ALLOC then it is really a signal-pending note */
284   /* for the pseudo-atomic noise. */
285   if (rs1 == reg_ALLOC) {
286     /* Perform the op anyway. */
287     op2 = badinst & 0x1fff;
288     if (op2 & (1<<12))
289       op2 |= -1<<13;
290     if (subtract)
291       result = op1 - op2;
292     else
293       result = op1 + op2;
294     *os_context_register_addr(context, reg_ALLOC) = result & ~7;
295     arch_skip_instruction(context);
296     interrupt_handle_pending(context);
297     return;
298   }
299
300   if ((op1 & 3) != 0) {
301     /* The first arg wan't a fixnum. */
302     interrupt_internal_error(signal, siginfo, context, 0);
303     return;
304   }
305
306   if (immed = badinst & (1<<13)) {
307     op2 = badinst & 0x1fff;
308     if (op2 & (1<<12))
309       op2 |= -1<<13;
310   }
311   else {
312     rs2 = badinst & 0x1f;
313     op2 = *os_context_register_addr(context, rs2);
314   }
315
316   if ((op2 & 3) != 0) {
317     /* The second arg wan't a fixnum. */
318     interrupt_internal_error(signal, siginfo, context, 0);
319     return;
320   }
321
322   rd = (badinst>>25) & 0x1f;
323   if (rd != 0) {
324     /* Don't bother computing the result unless we are going to use it. */
325     if (subtract)
326       result = (op1>>2) - (op2>>2);
327     else
328       result = (op1>>2) + (op2>>2);
329     
330     dynamic_space_free_pointer =
331       (lispobj *) *os_context_register_addr(context, reg_ALLOC);
332
333     *os_context_register_addr(context, rd) = alloc_number(result);
334     
335     *os_context_register_addr(context, reg_ALLOC) =
336       (unsigned long) dynamic_space_free_pointer;
337   }
338
339   arch_skip_instruction(context);
340 }
341
342 void arch_install_interrupt_handlers()
343 {
344   undoably_install_low_level_interrupt_handler(SIGILL , sigill_handler);
345   undoably_install_low_level_interrupt_handler(SIGEMT, sigemt_handler);
346 }
347
348 \f
349 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
350
351 lispobj funcall0(lispobj function)
352 {
353     lispobj *args = current_control_stack_pointer;
354
355     return call_into_lisp(function, args, 0);
356 }
357
358 lispobj funcall1(lispobj function, lispobj arg0)
359 {
360     lispobj *args = current_control_stack_pointer;
361
362     current_control_stack_pointer += 1;
363     args[0] = arg0;
364
365     return call_into_lisp(function, args, 1);
366 }
367
368 lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
369 {
370     lispobj *args = current_control_stack_pointer;
371
372     current_control_stack_pointer += 2;
373     args[0] = arg0;
374     args[1] = arg1;
375
376     return call_into_lisp(function, args, 2);
377 }
378
379 lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
380 {
381     lispobj *args = current_control_stack_pointer;
382
383     current_control_stack_pointer += 3;
384     args[0] = arg0;
385     args[1] = arg1;
386     args[2] = arg2;
387
388     return call_into_lisp(function, args, 3);
389 }
390