Clean up RCS ids
[projects/cmucl/cmucl.git] / src / lisp / ppc-arch.c
1 /*
2
3  This code was written as part of the CMU Common Lisp project at
4  Carnegie Mellon University, and has been placed in the public domain.
5
6 */
7
8 #include <stdio.h>
9
10 #include "arch.h"
11 #include "lisp.h"
12 #include "internals.h"
13 #include "globals.h"
14 #include "validate.h"
15 #include "os.h"
16 #include "lispregs.h"
17 #include "signal.h"
18 #include "interrupt.h"
19 #include "interr.h"
20
21   /* The header files may not define PT_DAR/PT_DSISR.  This definition
22      is correct for all versions of ppc linux >= 2.0.30
23
24      As of DR2.1u4, MkLinux doesn't pass these registers to signal
25      handlers correctly; a patch is necessary in order to (partially)
26      correct this.
27
28      Even with the patch, the DSISR may not have its 'write' bit set
29      correctly (it tends not to be set if the fault was caused by
30      something other than a protection violation.)
31
32      Caveat callers.  */
33
34 #ifndef PT_DAR
35 #define PT_DAR          41
36 #endif
37
38 #ifndef PT_DSISR
39 #define PT_DSISR        42
40 #endif
41
42 /* 
43  * A macro to generate the instruction
44  *
45  * twllei r0, code
46  *
47  * This is what the ppc port uses to signal various traps like
48  * breakpoints and stuff.
49  */
50 #define TWLLEI_R0(code) ((3<<26) | (6 << 21) | code)
51
52 char *
53 arch_init(fpu_mode_t mode)
54 {
55     return "lisp.core";
56 }
57
58 os_vm_address_t arch_get_bad_addr(HANDLER_ARGS)
59 {
60     os_context_t *os_context = (os_context_t *) context;
61     os_vm_address_t addr;
62
63     addr = (os_vm_address_t) SC_REG(os_context, PT_DAR);
64     return addr;
65 }
66
67
68 void
69 arch_skip_instruction(os_context_t * context)
70 {
71     /* Skip the offending instruction */
72     SC_PC(context) += 4;
73 }
74
75 unsigned char *
76 arch_internal_error_arguments(os_context_t * scp)
77 {
78     return (unsigned char *) (SC_PC(scp) + 4);
79 }
80
81 boolean arch_pseudo_atomic_atomic(os_context_t * scp)
82 {
83     return (SC_REG(scp, reg_ALLOC) & 4);
84 }
85
86 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
87
88 void
89 arch_set_pseudo_atomic_interrupted(os_context_t * scp)
90 {
91 #if 0
92     SC_REG(scp, reg_NL3) += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
93 #else
94     SC_REG(scp, reg_ALLOC) |= 1;
95 #endif
96 }
97
98 unsigned long
99 arch_install_breakpoint(void *pc)
100 {
101     unsigned long *ptr = (unsigned long *) pc;
102     unsigned long result = *ptr;
103
104     /* 
105      * Insert a twllei r0, trap_Breakpoint instruction.
106      */
107     *ptr = TWLLEI_R0(trap_Breakpoint);
108     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
109
110     return result;
111 }
112
113 void
114 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
115 {
116     *(unsigned long *) pc = orig_inst;
117     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
118 }
119
120 static unsigned long *skipped_break_addr, displaced_after_inst;
121 static sigset_t orig_sigmask;
122
123 void
124 arch_do_displaced_inst(os_context_t * scp, unsigned long orig_inst)
125 {
126     unsigned int *pc = (unsigned int *) SC_PC(scp);
127
128     orig_sigmask = scp->uc_sigmask;
129     sigemptyset(&scp->uc_sigmask);
130     FILLBLOCKSET(&scp->uc_sigmask);
131
132     /* Put the original instruction back */
133     *pc = orig_inst;
134     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
135
136     skipped_break_addr = (unsigned long) pc;
137
138     /*
139      * Replace the next instruction with a 
140      * twllei r0, trap_AfterBreakpoint 
141      */
142     displaced_after_inst = *++pc;
143     *pc = TWLLEI_R0(trap_AfterBreakpoint);
144     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
145
146     sigreturn(scp);
147 }
148
149 #ifdef GENCGC
150 /*
151  * Return non-zero if the current instruction is an allocation trap
152  */
153 static int
154 allocation_trap_p(os_context_t * context)
155 {
156     int result;
157     unsigned int *pc;
158     unsigned inst;
159     unsigned opcode;
160     unsigned src;
161     unsigned dst;
162
163     result = 0;
164
165     /*
166      * First, the instruction has to be a TWLGE temp, NL3, which as the
167      * format.
168      * | 6| 5| 5 | 5 | 10|1|  width
169      * |31|5 |dst|src|  4|0|  field
170      */
171     pc = (unsigned int *) SC_PC(context);
172     inst = *pc;
173
174 #if 0
175     fprintf(stderr, "allocation_trap_p at %p:  inst = 0x%08x\n", pc, inst);
176 #endif
177
178     opcode = inst >> 26;
179     src = (inst >> 11) & 0x1f;
180     dst = (inst >> 16) & 0x1f;
181     if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
182         && (4 == ((inst >> 1) & 0x3ff))) {
183         /*
184          * We got the instruction.  Now, look back to make sure it was
185          * proceeded by what we expected.  2 instructions back should be
186          * an ADD or ADDI instruction.
187          */
188         unsigned int add_inst;
189
190         add_inst = pc[-2];
191 #if 0
192         fprintf(stderr, "   add inst at %p:  inst = 0x%08x\n",
193                 pc - 2, add_inst);
194 #endif
195         opcode = add_inst >> 26;
196         if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
197             return 1;
198         } else if ((opcode == 14)) {
199             return 1;
200         } else {
201             fprintf(stderr,
202                     "Whoa! Got allocation trap not preceeded by an ADD or ADDI instruction: 0x%08x\n",
203                     add_inst);
204         }
205     }
206     return 0;
207 }
208
209 /*
210  * Use this function to enable the minimum number of signals we need
211  * when our trap handler needs to call Lisp code that might cons.  For
212  * consing to work with gencgc, we need to be able to trap the SIGILL
213  * signal to perform allocation.
214  */
215 void
216 enable_some_signals(void)
217 {
218     sigset_t sigs;
219
220 #if 0
221     fprintf(stderr, "Enabling some signals\n");
222 #endif
223 #if 0
224     sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
225 #else
226     sigemptyset(&sigs);
227     sigaddset(&sigs, SIGILL);
228     sigaddset(&sigs, SIGBUS);
229     sigprocmask(SIG_UNBLOCK, &sigs, NULL);
230 #endif
231 #if 0
232     fprintf(stderr, "Some signals enabled\n");
233 #endif
234 }
235
236 void
237 handle_allocation_trap(os_context_t * context)
238 {
239     unsigned int *pc;
240     unsigned int inst;
241     unsigned int or_inst;
242     unsigned int target;
243     unsigned int opcode;
244     int size;
245     int immed;
246     boolean were_in_lisp;
247     char *memory;
248     sigset_t block;
249
250     target = 0;
251     size = 0;
252
253 #if 0
254     fprintf(stderr, "In handle_allocation_trap\n");
255 #endif
256     /*
257      * I don't think it's possible for us NOT to be in lisp when we get
258      * here.  Remove this later?
259      */
260     were_in_lisp = !foreign_function_call_active;
261
262     if (were_in_lisp) {
263         fake_foreign_function_call(context);
264     } else {
265         fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
266     }
267
268     /*
269      * Look at current instruction: TWNE temp, NL3. We're here because
270      * temp > NL3 and temp is the end of the allocation, and NL3 is
271      * current-region-end-addr.
272      *
273      * We need to adjust temp and alloc-tn.
274      */
275
276     pc = (unsigned int *) SC_PC(context);
277     inst = pc[0];
278     target = (inst >> 16) & 0x1f;
279
280 #if 0
281     fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
282     fprintf(stderr, "  trap inst = 0x%08x\n", inst);
283     fprintf(stderr, "  target reg = %s\n", lisp_register_names[target]);
284 #endif
285     /*
286      * Go back and look at the add/addi instruction.  The second src arg
287      * is the size of the allocation.  Get it and call alloc to allocate
288      * new space.
289      */
290     inst = pc[-2];
291     opcode = inst >> 26;
292 #if 0
293     fprintf(stderr, "  add inst  = 0x%08x, opcode = %d\n", inst, opcode);
294 #endif
295     if (opcode == 14) {
296         /*
297          * ADDI temp-tn, alloc-tn, size 
298          *
299          * Extract the size
300          */
301         size = (inst & 0xffff);
302     } else if (opcode == 31) {
303         /*
304          * ADD temp-tn, alloc-tn, size-tn
305          *
306          * Extract the size
307          */
308         int reg;
309
310         reg = (inst >> 11) & 0x1f;
311 #if 0
312         fprintf(stderr, "  add, reg = %s\n", lisp_register_names[reg]);
313 #endif
314         size = SC_REG(context, reg);
315     }
316 #if 0
317     fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
318 #endif
319
320     /*
321      * Well, maybe not.  sigill_handler probably shouldn't be unblocking
322      * all signals.  So, let's enable just the signals we need.  Since
323      * alloc might call GC, we need to have SIGILL enabled so we can do
324      * allocation.  Do we need more?
325      */
326     enable_some_signals();
327
328 #if 0
329     fprintf(stderr, "Ready to alloc\n");
330     fprintf(stderr, "free_pointer = 0x%08x\n",
331             current_dynamic_space_free_pointer);
332 #endif
333     /*
334      * alloc-tn was incremented by size.  Need to decrement it by size to restore it's original value.
335      */
336     current_dynamic_space_free_pointer =
337         (lispobj *) ((long) current_dynamic_space_free_pointer - size);
338 #if 0
339     fprintf(stderr, "free_pointer = 0x%08x new\n",
340             current_dynamic_space_free_pointer);
341 #endif
342
343     memory = (char *) alloc(size);
344
345 #if 0
346     fprintf(stderr, "alloc returned %p\n", memory);
347     fprintf(stderr, "free_pointer = 0x%08x\n",
348             current_dynamic_space_free_pointer);
349 #endif
350
351     /* 
352      * The allocation macro wants the result to point to the end of the
353      * object!
354      */
355     memory += size;
356 #if 0
357     fprintf(stderr, "object end at %p\n", memory);
358 #endif
359     SC_REG(context, target) = (unsigned long) memory;
360     SC_REG(context, reg_ALLOC) =
361         (unsigned long) current_dynamic_space_free_pointer;
362
363     if (were_in_lisp) {
364         undo_fake_foreign_function_call(context);
365     }
366
367
368 }
369 #endif
370
371 static void
372 sigill_handler(HANDLER_ARGS)
373 {
374     os_context_t *os_context = (os_context_t *) context;
375     int badinst;
376     int opcode;
377
378     sigprocmask(SIG_SETMASK, &os_context->uc_sigmask, 0);
379     opcode = *((int *) SC_PC(os_context));
380
381 #if 0
382     printf("SIGILL entry:  opcode = 0x%08x\n", opcode);
383     fflush(stdout);
384 #endif
385
386     if (opcode == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
387         /* Got a twnei reg_NL3,0 - check for deferred interrupt */
388 #if 1
389         /* Clear the pseudo-atomic-interrupted bit */
390         SC_REG(os_context, reg_ALLOC) &= ~1;
391 #else
392         (SC_REG(os_context, reg_ALLOC) -= PSEUDO_ATOMIC_INTERRUPTED_BIAS);
393 #endif
394         arch_skip_instruction(os_context);
395         interrupt_handle_pending(os_context);
396 #ifdef DARWIN
397         /* Work around G5 bug; fix courtesy gbyers via chandler */
398         sigreturn(os_context);
399 #endif
400         return;
401     }
402
403     /* Is this an allocation trap? */
404 #ifdef GENCGC
405     if (allocation_trap_p(os_context)) {
406         handle_allocation_trap(os_context);
407         arch_skip_instruction(os_context);
408 #ifdef DARWIN
409         sigreturn(os_context);
410 #endif
411         return;
412     }
413 #endif
414
415     if ((opcode >> 16) == ((3 << 10) | (6 << 5))) {
416         /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
417         int trap = opcode & 0x1f, extra = (opcode >> 5) & 0x1f;
418
419 #if 0
420         printf("SIGILL:  TWLLEI, code = %d\n", trap);
421         fflush(stdout);
422 #endif
423
424         switch (trap) {
425           case trap_Halt:
426               fake_foreign_function_call(os_context);
427               lose("%%primitive halt called; the party is over.\n");
428
429           case trap_Error:
430           case trap_Cerror:
431               interrupt_internal_error(signal, code, os_context,
432                                        trap == trap_Cerror);
433               break;
434
435           case trap_PendingInterrupt:
436               arch_skip_instruction(os_context);
437               interrupt_handle_pending(os_context);
438               break;
439
440           case trap_Breakpoint:
441 #if 0
442               printf("trap_Breakpoint\n");
443               fflush(stdout);
444 #endif
445               handle_breakpoint(signal, code, os_context);
446               break;
447
448           case trap_FunctionEndBreakpoint:
449 #if 0
450               printf("trap_FunctionEndBreakpoint\n");
451               fflush(stdout);
452 #endif
453               SC_PC(os_context) =
454                   (int) handle_function_end_breakpoint(signal, code, os_context);
455               break;
456
457           case trap_AfterBreakpoint:
458 #if 0
459               fprintf(stderr, "trap_AfterBreakpoint: break_addr = %p\n",
460                       skipped_break_addr);
461               fprintf(stderr, " CSP  = %p\n",
462                       (void *) SC_REG(os_context, reg_CSP));
463               fprintf(stderr, " CFP  = %p\n",
464                       (void *) SC_REG(os_context, reg_CFP));
465               fprintf(stderr, " OCFP = %p\n",
466                       (void *) SC_REG(os_context, reg_OCFP));
467 #endif
468               /* Put our breakpoint instruction back in */
469               *skipped_break_addr = TWLLEI_R0(trap_Breakpoint);
470               skipped_break_addr = NULL;
471               *(unsigned long *) SC_PC(os_context) = displaced_after_inst;
472               os_context->uc_sigmask = orig_sigmask;
473
474               os_flush_icache((os_vm_address_t) SC_PC(os_context),
475                               sizeof(unsigned long));
476               break;
477
478           default:
479               interrupt_handle_now(signal, code, os_context);
480               break;
481         }
482 #ifdef DARWIN
483         /* Work around G5 bug; fix courtesy gbyers via chandler */
484         sigreturn(os_context);
485 #endif
486         return;
487     }
488     if (((opcode >> 26) == 3) && (((opcode >> 21) & 31) == 24)) {
489         interrupt_internal_error(signal, code, os_context, 0);
490 #ifdef DARWIN
491         /* Work around G5 bug; fix courtesy gbyers via chandler */
492         sigreturn(os_context);
493 #endif
494         return;
495     }
496
497     interrupt_handle_now(signal, code, os_context);
498 #ifdef DARWIN
499     /* Work around G5 bug; fix courtesy gbyers via chandler */
500     sigreturn(os_context);
501 #endif
502 }
503
504
505 void
506 arch_install_interrupt_handlers(void)
507 {
508     interrupt_install_low_level_handler(SIGILL, sigill_handler);
509     interrupt_install_low_level_handler(SIGTRAP, sigill_handler);
510 }
511
512
513 extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
514
515 lispobj
516 funcall0(lispobj function)
517 {
518     lispobj *args = current_control_stack_pointer;
519
520     return call_into_lisp(function, args, 0);
521 }
522
523 lispobj
524 funcall1(lispobj function, lispobj arg0)
525 {
526     lispobj *args = current_control_stack_pointer;
527
528     current_control_stack_pointer += 1;
529     args[0] = arg0;
530
531     return call_into_lisp(function, args, 1);
532 }
533
534 lispobj
535 funcall2(lispobj function, lispobj arg0, lispobj arg1)
536 {
537     lispobj *args = current_control_stack_pointer;
538
539     current_control_stack_pointer += 2;
540     args[0] = arg0;
541     args[1] = arg1;
542
543     return call_into_lisp(function, args, 2);
544 }
545
546 lispobj
547 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
548 {
549     lispobj *args = current_control_stack_pointer;
550
551     current_control_stack_pointer += 3;
552     args[0] = arg0;
553     args[1] = arg1;
554     args[2] = arg2;
555
556     return call_into_lisp(function, args, 3);
557 }
558
559 void
560 ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
561 {
562     os_vm_address_t end =
563         (os_vm_address_t) ((int) (address + length + (32 - 1)) & ~(32 - 1));
564     extern void ppc_flush_cache_line(os_vm_address_t);
565
566     while (address < end) {
567         ppc_flush_cache_line(address);
568         address += 32;
569     }
570 }
571
572 #ifdef LINKAGE_TABLE
573 /* Linkage tables for PowerPC
574  *
575  * Linkage entry size is 16, because we need at least 4 instructions to
576  * implement a jump.
577  */
578
579 /*
580  * This had better match lisp::target-foreign-linkage-entry-size in
581  * ppco/parms.lisp!  Each entry is 6 instructions long, so at least
582  * 24 bytes.
583  */
584 #ifndef LinkageEntrySize
585 #define LinkageEntrySize (8*4)
586 #endif
587
588 /*
589  * Define the registers to use in the linkage jump table. Can be the
590  * same. Some care must be exercised when choosing these. It has to be
591  * a register that is not otherwise being used. reg_NFP is a good
592  * choice. call_into_c trashes reg_NFP without preserving it, so we can
593  * trash it in the linkage jump table.
594  */
595 #define LINKAGE_TEMP_REG        reg_NFP
596 #define LINKAGE_ADDR_REG        reg_A0
597
598 /*
599  * Insert the necessary jump instructions at the given address.
600  */
601 void
602 arch_make_jump_entry(void *reloc_addr, void *target_addr)
603 {
604     /*
605      * Make JMP to function entry.
606      *
607      * The instruction sequence is:
608      *
609      *        addis temp, 0, (hi part of reloc)
610      *        ori   temp, temp, (lo part of reloc)
611      *        addis addr, 0, (hi part of addr)
612      *        ori   addr, addr, (low part of addr)
613      *        mtctr addr
614      *        bctr
615      *        
616      */
617     int *inst_ptr;
618     unsigned long hi;           /* Top 16 bits of address */
619     unsigned long lo;           /* Low 16 bits of address */
620     unsigned int inst;
621
622     inst_ptr = (int *) reloc_addr;
623
624     /*
625      * Split the target address into hi and lo parts for the addis/ori
626      * instructions.
627      */
628     hi = (unsigned long) reloc_addr;
629     lo = hi & 0xffff;
630     hi >>= 16;
631
632     /*
633      * addis 3, 0, (hi part)
634      */
635     inst = (15 << 26) | (LINKAGE_ADDR_REG << 21) | (0 << 16) | hi;
636     *inst_ptr++ = inst;
637
638     /*
639      * ori 3, 3, (lo part)
640      */
641
642     inst =
643         (24 << 26) | (LINKAGE_ADDR_REG << 21) | (LINKAGE_ADDR_REG << 16) | lo;
644     *inst_ptr++ = inst;
645
646     /*
647      * Split the target address into hi and lo parts for the addis/ori
648      * instructions.
649      */
650
651     hi = (unsigned long) target_addr;
652     lo = hi & 0xffff;
653     hi >>= 16;
654
655     /*
656      * addis 13, 0, (hi part)
657      */
658
659     inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
660     *inst_ptr++ = inst;
661
662     /*
663      * ori 13, 13, (lo part)
664      */
665
666     inst =
667         (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
668     *inst_ptr++ = inst;
669
670     /*
671      * mtctr 13
672      */
673
674     inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
675     *inst_ptr++ = inst;
676
677     /*
678      * bctr
679      */
680
681     inst = (19 << 26) | (20 << 21) | (528 << 1);
682     *inst_ptr++ = inst;
683
684
685     *inst_ptr++ = inst;
686
687     os_flush_icache((os_vm_address_t) reloc_addr,
688                     (char *) inst_ptr - (char *) reloc_addr);
689 }
690
691 void
692 arch_make_linkage_entry(long linkage_entry, void *target_addr, long type)
693 {
694     int *reloc_addr = (int *) (FOREIGN_LINKAGE_SPACE_START
695
696                                + linkage_entry * LinkageEntrySize);
697
698     if (type == 1) {            /* code reference */
699         arch_make_jump_entry(reloc_addr, target_addr);
700     } else if (type == 2) {
701         *(unsigned long *) reloc_addr = (unsigned long) target_addr;
702     }
703 }
704
705 /* Make a the entry a jump to resolve_linkage_tramp. */
706
707 extern void resolve_linkage_tramp(void);
708
709 void
710 arch_make_lazy_linkage(long linkage_entry)
711 {
712     arch_make_linkage_entry(linkage_entry, (void *) resolve_linkage_tramp, 1);
713 }
714
715 /* Get linkage entry.  We're given the return address which should be
716    the address of the jmpl instruction (2nd word) of the linkage
717    entry.  Figure out which entry this address belong to. */
718
719 long
720 arch_linkage_entry(unsigned long retaddr)
721 {
722     return (retaddr - (FOREIGN_LINKAGE_SPACE_START))
723         / LinkageEntrySize;
724 }
725 #endif
726
727 int ieee754_rem_pio2(double x, double *y0, double *y1)
728 {
729   extern int __ieee754_rem_pio2(double x, double *y);
730
731   double y[2];
732   int n;
733
734   n = __ieee754_rem_pio2(x, y);
735   *y0 = y[0];
736   *y1 = y[1];
737
738   return n;
739 }