Clean up RCS ids
[projects/cmucl/cmucl.git] / src / lisp / x86-arch.c
CommitLineData
eeab7066
RT
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*/
5ced0fdf 7
8#include <stdio.h>
cb786538 9#include <stdlib.h>
5ced0fdf 10
11#include "lisp.h"
12#include "globals.h"
13#include "validate.h"
14#include "os.h"
15#include "internals.h"
16#include "arch.h"
17#include "lispregs.h"
18#include "signal.h"
19#include "alloc.h"
20#include "interrupt.h"
21#include "interr.h"
22#include "breakpoint.h"
23
5ced0fdf 24#define BREAKPOINT_INST 0xcc /* INT3 */
25
9a8c1c2f 26unsigned long fast_random_state = 1;
725ab9ee 27
6438e048 28#if defined(SOLARIS)
29/*
30 * Use the /dev/cpu/self/cpuid interface on Solaris. We could use the
31 * same method below, but the Sun C compiler miscompiles the inline
32 * assembly.
33 */
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#include <unistd.h>
39#include <string.h>
40#include <errno.h>
41
42void cpuid(int level, unsigned int* a, unsigned int* b,
43 unsigned int* c, unsigned int* d)
44{
45 int device;
46 uint32_t regs[4];
47 static const char devname[] = "/dev/cpu/self/cpuid";
48
49 *a = *b = *c = *d = 0;
50 if ((device = open(devname, O_RDONLY)) == -1) {
51 perror(devname);
52 goto exit;
53 }
54
55 if (pread(device, regs, sizeof(regs), 1) != sizeof(regs)) {
56 perror(devname);
57 goto exit;
58 }
59
60 *a = regs[0];
61 *b = regs[1];
62 *c = regs[2];
63 *d = regs[3];
64
65 exit:
66 (void) close(device);
67
68 return;
69}
70
71#else
cb786538 72#define __cpuid(level, a, b, c, d) \
73 __asm__ ("xchgl\t%%ebx, %1\n\t" \
74 "cpuid\n\t" \
75 "xchgl\t%%ebx, %1\n\t" \
76 : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
77 : "0" (level))
78
423bb6de 79void cpuid(int level, unsigned int* a, unsigned int* b,
80 unsigned int* c, unsigned int* d)
81{
82 unsigned int eax, ebx, ecx, edx;
83
84 __cpuid(level, eax, ebx, ecx, edx);
85
86 *a = eax;
87 *b = ebx;
88 *c = ecx;
89 *d = edx;
90}
6438e048 91#endif
423bb6de 92
cb786538 93int
94arch_support_sse2(void)
95{
96 unsigned int eax, ebx, ecx, edx;
97
423bb6de 98 cpuid(1, &eax, &ebx, &ecx, &edx);
cb786538 99
100 /* Return non-zero if SSE2 is supported */
101 return edx & 0x4000000;
102}
103
9a8c1c2f 104char *
cb786538 105arch_init(fpu_mode_t mode)
5ced0fdf 106{
cb786538 107 int have_sse2;
108
aa9c2237 109 have_sse2 = arch_support_sse2() && os_support_sse2();
cb786538 110
111 switch (mode) {
112 case AUTO:
113 if (have_sse2) {
114 return "lisp-sse2.core";
115 } else {
116 return "lisp-x87.core";
117 }
118 break;
119 case X87:
120 return "lisp-x87.core";
121 break;
122 case SSE2:
123 return "lisp-sse2.core";
124 break;
125 default:
126 abort();
127 }
5ced0fdf 128}
5ced0fdf 129\f
cbdde084 130
9a8c1c2f 131
cbdde084 132/*
133 * Assuming we get here via an INT3 xxx instruction, the PC now
134 * points to the interrupt code (lisp value) so we just move past
617e53e5 135 * it. Skip the code, then if the code is an error-trap or
cbdde084 136 * Cerror-trap then skip the data bytes that follow.
137 */
138
9a8c1c2f 139void
140arch_skip_instruction(os_context_t * context)
5ced0fdf 141{
9a8c1c2f 142 int vlen, code;
143
144 DPRINTF(0, (stderr, "[arch_skip_inst at %lx>]\n", SC_PC(context)));
145
146 /* Get and skip the lisp error code. */
147 code = *(char *) SC_PC(context)++;
148 switch (code) {
149 case trap_Error:
150 case trap_Cerror:
151 /* Lisp error arg vector length */
152 vlen = *(char *) SC_PC(context)++;
153 /* Skip lisp error arg data bytes */
154 while (vlen-- > 0)
15454dd9 155 SC_PC(context)++;
9a8c1c2f 156 break;
157
158 case trap_Breakpoint:
159 case trap_FunctionEndBreakpoint:
160 break;
161
162 case trap_PendingInterrupt:
163 case trap_Halt:
164 /* Only needed to skip the Code. */
165 break;
166
167 default:
168 fprintf(stderr, "[arch_skip_inst invalid code %d\n]\n", code);
169 break;
5ced0fdf 170 }
171
9a8c1c2f 172 DPRINTF(0, (stderr, "[arch_skip_inst resuming at %lx>]\n", SC_PC(context)));
5ced0fdf 173}
174
9a8c1c2f 175unsigned char *
176arch_internal_error_arguments(os_context_t * context)
5ced0fdf 177{
9a8c1c2f 178 return (unsigned char *) (SC_PC(context) + 1);
5ced0fdf 179}
180
9a8c1c2f 181boolean
182arch_pseudo_atomic_atomic(os_context_t * context)
5ced0fdf 183{
9a8c1c2f 184 return SymbolValue(PSEUDO_ATOMIC_ATOMIC);
5ced0fdf 185}
186
9a8c1c2f 187void
188arch_set_pseudo_atomic_interrupted(os_context_t * context)
5ced0fdf 189{
9a8c1c2f 190 SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, make_fixnum(1));
5ced0fdf 191}
192\f
cbdde084 193
9a8c1c2f 194
195unsigned long
196arch_install_breakpoint(void *pc)
5ced0fdf 197{
9a8c1c2f 198 unsigned long result = *(unsigned long *) pc;
5ced0fdf 199
9a8c1c2f 200 *(char *) pc = BREAKPOINT_INST; /* x86 INT3 */
201 *((char *) pc + 1) = trap_Breakpoint; /* Lisp trap code */
202
203 return result;
5ced0fdf 204}
205
9a8c1c2f 206void
207arch_remove_breakpoint(void *pc, unsigned long orig_inst)
5ced0fdf 208{
9a8c1c2f 209 *((char *) pc) = orig_inst & 0xff;
210 *((char *) pc + 1) = (orig_inst & 0xff00) >> 8;
5ced0fdf 211}
5ced0fdf 212\f
5ced0fdf 213
9a8c1c2f 214
cbdde084 215/*
216 * When single stepping single_stepping holds the original instruction
217 * pc location.
218 */
5ced0fdf 219
cbdde084 220unsigned int *single_stepping = NULL;
9a8c1c2f 221
3edae40b 222#ifndef __linux__
9a8c1c2f 223unsigned int single_step_save1;
224unsigned int single_step_save2;
225unsigned int single_step_save3;
3edae40b 226#endif
227
9a8c1c2f 228void
229arch_do_displaced_inst(os_context_t * context, unsigned long orig_inst)
5ced0fdf 230{
9a8c1c2f 231 unsigned int *pc = (unsigned int *) SC_PC(context);
cbdde084 232
9a8c1c2f 233 /*
234 * Put the original instruction back.
235 */
3edae40b 236
9a8c1c2f 237 *((char *) pc) = orig_inst & 0xff;
238 *((char *) pc + 1) = (orig_inst & 0xff00) >> 8;
3edae40b 239
5c6f325d 240#ifdef SC_EFLAGS
7d7c3c3e 241 /* Enable single-stepping */
5c6f325d 242 SC_EFLAGS(context) |= 0x100;
3edae40b 243#else
cbdde084 244
9a8c1c2f 245 /*
246 * Install helper instructions for the single step:
7d7c3c3e 247 * nop; nop; nop; pushf; or [esp],0x100; popf.
248 *
249 * The or instruction enables the trap flag which enables
250 * single-stepping. So when the popf instruction is run, we start
251 * single-stepping and stop on the next instruction.
9a8c1c2f 252 */
253
ac087c01 254 DPRINTF(0, (stderr, "Installing helper instructions\n"));
78eb5a7b 255
9a8c1c2f 256 single_step_save1 = *(pc - 3);
257 single_step_save2 = *(pc - 2);
258 single_step_save3 = *(pc - 1);
259 *(pc - 3) = 0x9c909090;
260 *(pc - 2) = 0x00240c81;
261 *(pc - 1) = 0x9d000001;
3edae40b 262#endif
263
9a8c1c2f 264 single_stepping = (unsigned int *) pc;
3edae40b 265
78eb5a7b 266#ifndef SC_EFLAGS
7d7c3c3e 267 /*
268 * pc - 9 points to the pushf instruction that we installed for
269 * the helper.
270 */
271
ac087c01 272 DPRINTF(0, (stderr, " Setting pc to pushf instruction at %p\n", (void*) ((char*) pc - 9)));
caa5a031 273 SC_PC(context) = (int)((char *) pc - 9);
3edae40b 274#endif
5ced0fdf 275}
276\f
5ced0fdf 277
9a8c1c2f 278void
279sigtrap_handler(HANDLER_ARGS)
5ced0fdf 280{
9a8c1c2f 281 unsigned int trap;
aa96ed2c 282 os_context_t* os_context = (os_context_t *) context;
cbdde084 283#if 0
78eb5a7b 284 fprintf(stderr, "x86sigtrap: %8x %x\n",
aa96ed2c
RT
285 SC_PC(os_os_context), *(unsigned char *) (SC_PC(os_context) - 1));
286 fprintf(stderr, "sigtrap(%d %d %x)\n", signal, CODE(code), os_context);
cbdde084 287#endif
288
9a8c1c2f 289 if (single_stepping && (signal == SIGTRAP)) {
cbdde084 290#if 0
7d7c3c3e 291 fprintf(stderr, "* Single step trap %p\n", single_stepping);
cbdde084 292#endif
3edae40b 293
5c6f325d 294#ifdef SC_EFLAGS
7d7c3c3e 295 /* Disable single-stepping */
aa96ed2c 296 SC_EFLAGS(os_context) ^= 0x100;
7d7c3c3e 297#else
9a8c1c2f 298 /* Un-install single step helper instructions. */
299 *(single_stepping - 3) = single_step_save1;
300 *(single_stepping - 2) = single_step_save2;
301 *(single_stepping - 1) = single_step_save3;
ac087c01 302 DPRINTF(0, (stderr, "Uninstalling helper instructions\n"));
a3a119cd 303#endif
cbdde084 304
9a8c1c2f 305 /*
306 * Re-install the breakpoint if possible.
307 */
aa96ed2c 308 if ((int) SC_PC(os_context) == (int) single_stepping + 1)
9a8c1c2f 309 fprintf(stderr, "* Breakpoint not re-install\n");
310 else {
311 char *ptr = (char *) single_stepping;
312
313 ptr[0] = BREAKPOINT_INST; /* x86 INT3 */
314 ptr[1] = trap_Breakpoint;
3edae40b 315 }
316
9a8c1c2f 317 single_stepping = NULL;
318 return;
3edae40b 319 }
a3a119cd 320
9a8c1c2f 321 /* This is just for info in case monitor wants to print an approx */
aa96ed2c 322 current_control_stack_pointer = (unsigned long *) SC_SP(os_context);
3edae40b 323
aa96ed2c 324 RESTORE_FPU(os_context);
44eba57f 325
9a8c1c2f 326 /*
327 * On entry %eip points just after the INT3 byte and aims at the
328 * 'kind' value (eg trap_Cerror). For error-trap and Cerror-trap a
329 * number of bytes will follow, the first is the length of the byte
330 * arguments to follow.
331 */
332
aa96ed2c 333 trap = *(unsigned char *) SC_PC(os_context);
9a8c1c2f 334
335 switch (trap) {
336 case trap_PendingInterrupt:
337 DPRINTF(0, (stderr, "<trap Pending Interrupt.>\n"));
aa96ed2c
RT
338 arch_skip_instruction(os_context);
339 interrupt_handle_pending(os_context);
9a8c1c2f 340 break;
341
342 case trap_Halt:
343 {
c07cc020
RT
344 FPU_STATE(fpu_state);
345 save_fpu_state(fpu_state);
9a8c1c2f 346
aa96ed2c 347 fake_foreign_function_call(os_context);
9a8c1c2f 348 lose("%%primitive halt called; the party is over.\n");
aa96ed2c 349 undo_fake_foreign_function_call(os_context);
c07cc020
RT
350
351 restore_fpu_state(fpu_state);
aa96ed2c 352 arch_skip_instruction(os_context);
9a8c1c2f 353 break;
354 }
355
356 case trap_Error:
357 case trap_Cerror:
342beebb 358 DPRINTF(0, (stderr, "<trap Error %x>\n", CODE(code)));
aa96ed2c 359 interrupt_internal_error(signal, code, os_context, CODE(code) == trap_Cerror);
9a8c1c2f 360 break;
cbdde084 361
9a8c1c2f 362 case trap_Breakpoint:
cbdde084 363#if 0
9a8c1c2f 364 fprintf(stderr, "*C break\n");
cbdde084 365#endif
aa96ed2c 366 SC_PC(os_context) -= 1;
5d2cd5df 367
aa96ed2c 368 handle_breakpoint(signal, CODE(code), os_context);
cbdde084 369#if 0
9a8c1c2f 370 fprintf(stderr, "*C break return\n");
cbdde084 371#endif
9a8c1c2f 372 break;
373
374 case trap_FunctionEndBreakpoint:
aa96ed2c
RT
375 SC_PC(os_context) -= 1;
376 SC_PC(os_context) =
377 (int) handle_function_end_breakpoint(signal, CODE(code), os_context);
9a8c1c2f 378 break;
379
10d10ac0 380#ifdef trap_DynamicSpaceOverflowWarning
9a8c1c2f 381 case trap_DynamicSpaceOverflowWarning:
382 interrupt_handle_space_overflow(SymbolFunction
383 (DYNAMIC_SPACE_OVERFLOW_WARNING_HIT),
aa96ed2c 384 os_context);
9a8c1c2f 385 break;
10d10ac0 386#endif
387#ifdef trap_DynamicSpaceOverflowError
9a8c1c2f 388 case trap_DynamicSpaceOverflowError:
389 interrupt_handle_space_overflow(SymbolFunction
390 (DYNAMIC_SPACE_OVERFLOW_ERROR_HIT),
aa96ed2c 391 os_context);
9a8c1c2f 392 break;
10d10ac0 393#endif
9a8c1c2f 394 default:
395 DPRINTF(0,
f3e5780e 396 (stderr, "[C--trap default %d %d %p]\n", signal, CODE(code),
aa96ed2c
RT
397 os_context));
398 interrupt_handle_now(signal, code, os_context);
9a8c1c2f 399 break;
5ced0fdf 400 }
401}
402
9a8c1c2f 403void
b8d0dfaf 404arch_install_interrupt_handlers(void)
5ced0fdf 405{
cbdde084 406 interrupt_install_low_level_handler(SIGILL, sigtrap_handler);
407 interrupt_install_low_level_handler(SIGTRAP, sigtrap_handler);
5ced0fdf 408}
409\f
410
9a8c1c2f 411extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
5ced0fdf 412
413/* These next four functions are an interface to the
414 * Lisp call-in facility. Since this is C we can know
415 * nothing about the calling environment. The control
416 * stack might be the C stack if called from the monitor
417 * or the Lisp stack if called as a result of an interrupt
418 * or maybe even a separate stack. The args are most likely
419 * on that stack but could be in registers depending on
420 * what the compiler likes. So I try to package up the
421 * args into a portable vector and let the assembly language
422 * call-in function figure it out.
423 */
cbdde084 424
9a8c1c2f 425lispobj
426funcall0(lispobj function)
5ced0fdf 427{
428 lispobj *args = NULL;
429
430 return call_into_lisp(function, args, 0);
431}
432
9a8c1c2f 433lispobj
434funcall1(lispobj function, lispobj arg0)
5ced0fdf 435{
436 lispobj args[1];
9a8c1c2f 437
5ced0fdf 438 args[0] = arg0;
439 return call_into_lisp(function, args, 1);
440}
441
9a8c1c2f 442lispobj
443funcall2(lispobj function, lispobj arg0, lispobj arg1)
5ced0fdf 444{
445 lispobj args[2];
9a8c1c2f 446
5ced0fdf 447 args[0] = arg0;
448 args[1] = arg1;
449 return call_into_lisp(function, args, 2);
450}
451
9a8c1c2f 452lispobj
453funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
5ced0fdf 454{
455 lispobj args[3];
9a8c1c2f 456
5ced0fdf 457 args[0] = arg0;
458 args[1] = arg1;
459 args[2] = arg2;
460 return call_into_lisp(function, args, 3);
461}
bf84be07 462
cf055d22 463#ifdef LINKAGE_TABLE
464
f055faed 465#ifndef LinkageEntrySize
466#define LinkageEntrySize 8
467#endif
bf84be07 468
9a8c1c2f 469void
470arch_make_linkage_entry(long linkage_entry, void *target_addr, long type)
bf84be07 471{
9a8c1c2f 472 char *reloc_addr = (char *) (FOREIGN_LINKAGE_SPACE_START
bf84be07 473
9a8c1c2f 474 + linkage_entry * LinkageEntrySize);
475
476 if (type == 1) { /* code reference */
477 /* Make JMP to function entry. */
bf84be07 478 /* JMP offset is calculated from next instruction. */
9a8c1c2f 479 long offset = (char *) target_addr - (reloc_addr + 5);
bf84be07 480 int i;
9a8c1c2f 481
482 *reloc_addr++ = 0xe9; /* opcode for JMP rel32 */
bf84be07 483 for (i = 0; i < 4; i++) {
484 *reloc_addr++ = offset & 0xff;
485 offset >>= 8;
486 }
487 /* write a nop for good measure. */
488 *reloc_addr = 0x90;
489 } else if (type == 2) {
9a8c1c2f 490 *(unsigned long *) reloc_addr = (unsigned long) target_addr;
bf84be07 491 }
492}
493
494/* Make a call to the first function in the linkage table, which is
495 resolve_linkage_tramp. */
9a8c1c2f 496void
497arch_make_lazy_linkage(long linkage_entry)
bf84be07 498{
9a8c1c2f 499 char *reloc_addr = (char *) (FOREIGN_LINKAGE_SPACE_START
500
501 + linkage_entry * LinkageEntrySize);
502 long offset = (char *) (FOREIGN_LINKAGE_SPACE_START) - (reloc_addr + 5);
bf84be07 503 int i;
504
9a8c1c2f 505 *reloc_addr++ = 0xe8; /* opcode for CALL rel32 */
bf84be07 506 for (i = 0; i < 4; i++) {
507 *reloc_addr++ = offset & 0xff;
508 offset >>= 8;
509 }
510 /* write a nop for good measure. */
511 *reloc_addr = 0x90;
512}
513
514/* Get linkage entry. The initial instruction in the linkage
515 entry is a CALL; the return address we're passed points to the next
516 instruction. */
517
9a8c1c2f 518long
519arch_linkage_entry(unsigned long retaddr)
bf84be07 520{
f055faed 521 return ((retaddr - 5) - FOREIGN_LINKAGE_SPACE_START) / LinkageEntrySize;
bf84be07 522}
523#endif /* LINKAGE_TABLE */
a459a234 524
525int ieee754_rem_pio2(double x, double *y0, double *y1)
526{
527 extern int __ieee754_rem_pio2(double x, double *y);
528
529 double y[2];
530 int n;
531
532 n = __ieee754_rem_pio2(x, y);
533 *y0 = y[0];
534 *y1 = y[1];
535
536 return n;
537}
538
539