diff --git a/lisp/amd64-arch.c b/lisp/amd64-arch.c new file mode 100644 index 0000000000000000000000000000000000000000..a232fc52ee1c5f887ca81d44b04ba501ad57aa7e --- /dev/null +++ b/lisp/amd64-arch.c @@ -0,0 +1,420 @@ +/* x86-arch.c -*- Mode: C; comment-column: 40 -*- + * + * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/amd64-arch.c,v 1.1 2004/05/18 21:53:56 cwang Exp $ + * + */ + +#include <stdio.h> + +#include "lisp.h" +#include "globals.h" +#include "validate.h" +#include "os.h" +#include "internals.h" +#include "arch.h" +#include "lispregs.h" +#include "signal.h" +#include "alloc.h" +#include "interrupt.h" +#include "interr.h" +#include "breakpoint.h" + +#define DPRINTF(test, e) {if(test) fprintf e ;} + +#define BREAKPOINT_INST 0xcc /* INT3 */ + +unsigned long fast_random_state = 1; + +char * arch_init(void) +{ + return "lisp.core"; +} + + + +/* + * Assuming we get here via an INT3 xxx instruction, the PC now + * points to the interrupt code (lisp value) so we just move past + * it. Skip the code, then if the code is an error-trap or + * Cerror-trap then skip the data bytes that follow. + */ + +void arch_skip_instruction(struct sigcontext *context) +{ + int vlen,code; + + DPRINTF(0,(stderr,"[arch_skip_inst at %x>]\n", context->sc_pc)); + + /* Get and skip the lisp error code. */ + code = *(char*) context->sc_pc++; + switch (code) + { + case trap_Error: + case trap_Cerror: + /* Lisp error arg vector length */ + vlen = *(char*) context->sc_pc++; + /* Skip lisp error arg data bytes */ + while(vlen-- > 0) + ((char*) context->sc_pc)++; + break; + + case trap_Breakpoint: + case trap_FunctionEndBreakpoint: + break; + + case trap_PendingInterrupt: + case trap_Halt: + /* Only needed to skip the Code. */ + break; + + default: + fprintf(stderr, "[arch_skip_inst invalid code %d\n]\n", code); + break; + } + + DPRINTF(0,(stderr,"[arch_skip_inst resuming at %x>]\n", context->sc_pc)); +} + +unsigned char * arch_internal_error_arguments(struct sigcontext *context) +{ + return (unsigned char *) (context->sc_pc + 1); +} + +boolean arch_pseudo_atomic_atomic(struct sigcontext *context) +{ + return SymbolValue(PSEUDO_ATOMIC_ATOMIC); +} + +void arch_set_pseudo_atomic_interrupted(struct sigcontext *context) +{ + SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, make_fixnum(1)); +} + + + +unsigned long arch_install_breakpoint(void *pc) +{ + unsigned long result = *(unsigned long*)pc; + + *(char*)pc = BREAKPOINT_INST; /* x86 INT3 */ + *((char*)pc+1) = trap_Breakpoint; /* Lisp trap code */ + + return result; +} + +void arch_remove_breakpoint(void *pc, unsigned long orig_inst) +{ + *((char *) pc) = orig_inst & 0xff; + *((char *) pc + 1) = (orig_inst & 0xff00) >> 8; +} + + + +/* + * When single stepping single_stepping holds the original instruction + * pc location. + */ + +unsigned int *single_stepping = NULL; +#ifndef __linux__ +unsigned int single_step_save1; +unsigned int single_step_save2; +unsigned int single_step_save3; +#endif + +void arch_do_displaced_inst(struct sigcontext *context, + unsigned long orig_inst) +{ + unsigned int *pc = (unsigned int*) context->sc_pc; + + /* + * Put the original instruction back. + */ + + *((char *) pc) = orig_inst & 0xff; + *((char *) pc + 1) = (orig_inst & 0xff00) >> 8; + +#ifdef __linux__ + context->eflags |= 0x100; +#else + + /* + * Install helper instructions for the single step: + * pushf; or [esp],0x100; popf. + */ + + single_step_save1 = *(pc - 3); + single_step_save2 = *(pc - 2); + single_step_save3 = *(pc - 1); + *(pc - 3) = 0x9c909090; + *(pc - 2) = 0x00240c81; + *(pc - 1) = 0x9d000001; +#endif + + single_stepping = (unsigned int*) pc; + +#ifndef __linux__ + (unsigned int*) context->sc_pc = (char *) pc - 9; +#endif +} + + +void sigtrap_handler(HANDLER_ARGS) +{ + unsigned int trap; + +#ifdef __linux__ + GET_CONTEXT +#endif + +#if 0 + fprintf(stderr,"x86sigtrap: %8x %x\n", + context->sc_pc, *(unsigned char *)(context->sc_pc-1)); + fprintf(stderr,"sigtrap(%d %d %x)\n",signal,code,context); +#endif + + if (single_stepping && (signal == SIGTRAP)) + { +#if 0 + fprintf(stderr,"* Single step trap %x\n", single_stepping); +#endif + +#ifndef __linux__ + /* Un-install single step helper instructions. */ + *(single_stepping-3) = single_step_save1; + *(single_stepping-2) = single_step_save2; + *(single_stepping-1) = single_step_save3; +#else + context->eflags ^= 0x100; +#endif + + /* + * Re-install the breakpoint if possible. + */ + + if ((int) context->sc_pc == (int) single_stepping + 1) + fprintf(stderr, "* Breakpoint not re-install\n"); + else + { + char *ptr = (char *) single_stepping; + ptr[0] = BREAKPOINT_INST; /* x86 INT3 */ + ptr[1] = trap_Breakpoint; + } + + single_stepping = NULL; + return; + } + + SAVE_CONTEXT(); + + /* This is just for info in case monitor wants to print an approx */ + current_control_stack_pointer = (unsigned long*) context->sc_sp; + +#if defined(__linux__) && (defined(i386) || defined(__x86_64)) + /* + * Restore the FPU control word, setting the rounding mode to nearest. + */ + + if (contextstruct.fpstate) +#if defined(__x86_64) + setfpucw(contextstruct.fpstate->cwd & ~0xc00); +#else + setfpucw(contextstruct.fpstate->cw & ~0xc00); +#endif +#endif + + /* + * On entry %eip points just after the INT3 byte and aims at the + * 'kind' value (eg trap_Cerror). For error-trap and Cerror-trap a + * number of bytes will follow, the first is the length of the byte + * arguments to follow. + */ + + trap = *(unsigned char *) (context->sc_pc); + + switch (trap) + { + case trap_PendingInterrupt: + DPRINTF(0,(stderr,"<trap Pending Interrupt.>\n")); + arch_skip_instruction(context); + interrupt_handle_pending(context); + break; + + case trap_Halt: + { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + int fpu_state[27]; + fpu_save(fpu_state); +#endif + fake_foreign_function_call(context); + lose("%%primitive halt called; the party is over.\n"); + undo_fake_foreign_function_call(context); +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + fpu_restore(fpu_state); +#endif + arch_skip_instruction(context); + break; + } + + case trap_Error: + case trap_Cerror: + DPRINTF(0, (stderr, "<trap Error %d>\n",code)); +#ifdef __linux__ + interrupt_internal_error(signal, contextstruct, code == trap_Cerror); +#else + interrupt_internal_error(signal, code, context, code == trap_Cerror); +#endif + break; + + case trap_Breakpoint: +#if 0 + fprintf(stderr,"*C break\n"); +#endif + (char*) context->sc_pc -= 1; + handle_breakpoint(signal, code, context); +#if 0 + fprintf(stderr,"*C break return\n"); +#endif + break; + + case trap_FunctionEndBreakpoint: + (char*) context->sc_pc -= 1; + context->sc_pc = (int) handle_function_end_breakpoint(signal, code, context); + break; + +#ifdef trap_DynamicSpaceOverflowWarning + case trap_DynamicSpaceOverflowWarning: + interrupt_handle_space_overflow(SymbolFunction(DYNAMIC_SPACE_OVERFLOW_WARNING_HIT), + context); + break; +#endif +#ifdef trap_DynamicSpaceOverflowError + case trap_DynamicSpaceOverflowError: + interrupt_handle_space_overflow(SymbolFunction(DYNAMIC_SPACE_OVERFLOW_ERROR_HIT), + context); + break; +#endif + default: + DPRINTF(0,(stderr,"[C--trap default %d %d %x]\n", signal, code,context)); +#ifdef __linux__ + interrupt_handle_now(signal, contextstruct); +#else + interrupt_handle_now(signal, code, context); +#endif + break; + } +} + +#define FIXNUM_VALUE(lispobj) (((int) lispobj) >> 2) + +void arch_install_interrupt_handlers() +{ + interrupt_install_low_level_handler(SIGILL, sigtrap_handler); + interrupt_install_low_level_handler(SIGTRAP, sigtrap_handler); +} + + +extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs); + +/* These next four functions are an interface to the + * Lisp call-in facility. Since this is C we can know + * nothing about the calling environment. The control + * stack might be the C stack if called from the monitor + * or the Lisp stack if called as a result of an interrupt + * or maybe even a separate stack. The args are most likely + * on that stack but could be in registers depending on + * what the compiler likes. So I try to package up the + * args into a portable vector and let the assembly language + * call-in function figure it out. + */ + +lispobj funcall0(lispobj function) +{ + lispobj *args = NULL; + + return call_into_lisp(function, args, 0); +} + +lispobj funcall1(lispobj function, lispobj arg0) +{ + lispobj args[1]; + args[0] = arg0; + return call_into_lisp(function, args, 1); +} + +lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1) +{ + lispobj args[2]; + args[0] = arg0; + args[1] = arg1; + return call_into_lisp(function, args, 2); +} + +lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2) +{ + lispobj args[3]; + args[0] = arg0; + args[1] = arg1; + args[2] = arg2; + return call_into_lisp(function, args, 3); +} + +#ifdef LINKAGE_TABLE + +#ifndef LinkageEntrySize +#define LinkageEntrySize 16 +#endif + +void arch_make_linkage_entry(long linkage_entry, void *target_addr, long type) +{ + char *reloc_addr = (char *)(FOREIGN_LINKAGE_SPACE_START + + linkage_entry * LinkageEntrySize); + + if (type == 1) { /* code reference */ + /* Make JMP to function entry. */ + long offset = (char *)target_addr; + int i; + + *reloc_addr++ = 0x49; /* opcode for MOV */ + *reloc_addr++ = 0xbf; /* %r15 */ + for (i = 0; i < 8; i++) { + *reloc_addr++ = offset & 0xff; + offset >>= 8; + } + *reloc_addr++ = 0x41; /* jmpq */ + *reloc_addr++ = 0xff; + *reloc_addr++ = 0xe7; /* %r15 */ + /* write a nop for good measure. */ + *reloc_addr = 0x90; + } else if (type == 2) { + *(unsigned long *)reloc_addr = (unsigned long)target_addr; + } +} + +/* Make a call to the first function in the linkage table, which is + resolve_linkage_tramp. */ +void arch_make_lazy_linkage(long linkage_entry) +{ + char *reloc_addr = (char *)(FOREIGN_LINKAGE_SPACE_START + + linkage_entry * LinkageEntrySize); + long offset = (char *)(FOREIGN_LINKAGE_SPACE_START) - (reloc_addr + 5); + int i; + + *reloc_addr++ = 0xe8; /* opcode for CALL rel32 */ + for (i = 0; i < 4; i++) { + *reloc_addr++ = offset & 0xff; + offset >>= 8; + } + /* write a nop for good measure. */ + *reloc_addr = 0x90; +} + +/* Get linkage entry. The initial instruction in the linkage + entry is a CALL; the return address we're passed points to the next + instruction. */ + +long arch_linkage_entry(unsigned long retaddr) +{ + return ((retaddr - 5) - FOREIGN_LINKAGE_SPACE_START) / LinkageEntrySize; +} +#endif /* LINKAGE_TABLE */