Skip to content
sparc-arch.c 6.85 KiB
Newer Older
wlott's avatar
wlott committed
#include <stdio.h>
#include <machine/trap.h>

#include "lisp.h"
#include "internals.h"
#include "globals.h"
#include "validate.h"
#include "os.h"
#include "arch.h"
#include "lispregs.h"
#include "signal.h"
#include "interrupt.h"

char *arch_init()
{
    return NULL;
}

os_vm_address_t arch_get_bad_addr(int signal, int code,
				  struct sigcontext *context)
{
    unsigned long badinst;
    int rs1;

    /* On the sparc, we have to decode the instruction. */

    /* Make sure it's not the pc thats bogus, and that it was lisp code */
    /* that caused the fault. */
    if ((context->sc_pc & 3) != 0 ||
	((context->sc_pc < READ_ONLY_SPACE_START ||
	  context->sc_pc >= READ_ONLY_SPACE_START+READ_ONLY_SPACE_SIZE) &&
	 ((lispobj *)context->sc_pc < current_dynamic_space &&
	  (lispobj *)context->sc_pc >=
	      current_dynamic_space + DYNAMIC_SPACE_SIZE)))
	return NULL;

    badinst = *(unsigned long *)context->sc_pc;

    if ((badinst >> 30) != 3)
	/* All load/store instructions have op = 11 (binary) */
	return NULL;

    rs1 = (badinst>>14)&0x1f;

    if (badinst & (1<<13)) {
	/* r[rs1] + simm(13) */
	int simm13 = badinst & 0x1fff;

	if (simm13 & (1<<12))
	    simm13 |= -1<<13;

	return (os_vm_address_t)(SC_REG(context, rs1) + simm13);
    }
    else {
	/* r[rs1] + r[rs2] */
	int rs2 = badinst & 0x1f;

	return (os_vm_address_t)(SC_REG(context, rs1) + SC_REG(context, rs2));
    }

}

void arch_skip_instruction(context)
struct sigcontext *context;
{
    /* Skip the offending instruction */
    context->sc_pc = context->sc_npc;
    context->sc_npc += 4;
}

unsigned char *arch_internal_error_arguments(struct sigcontext *scp)
{
    return (unsigned char *)(scp->sc_pc+4);
}

boolean arch_pseudo_atomic_atomic(struct sigcontext *scp)
{
    return (SC_REG(scp, reg_ALLOC) & 4);
}

void arch_set_pseudo_atomic_interrupted(struct sigcontext *scp)
{
    SC_REG(scp, reg_ALLOC) |= 1;
}

unsigned long arch_install_breakpoint(void *pc)
{
    unsigned long *ptr = (unsigned long *)pc;
    unsigned long result = *ptr;
    *ptr = trap_Breakpoint;
    return result;
}

void arch_remove_breakpoint(void *pc, unsigned long orig_inst)
{
    *(unsigned long *)pc = orig_inst;
}

static unsigned long *skipped_break_addr, displaced_after_inst;
static int orig_sigmask;

void arch_do_displaced_inst(struct sigcontext *scp,
				   unsigned long orig_inst)
{
    unsigned long *pc = (unsigned long *)scp->sc_pc;
    unsigned long *npc = (unsigned long *)scp->sc_npc;

    orig_sigmask = scp->sc_mask;
    scp->sc_mask = BLOCKABLE;

    *pc = orig_inst;
    skipped_break_addr = pc;
    displaced_after_inst = *npc;
    *npc = trap_AfterBreakpoint;

    sigreturn(scp);
}

static void sigill_handler(signal, code, scp)
int signal, code;
struct sigcontext *scp;
{
    int badinst;

    sigsetmask(scp->sc_mask);

    if (code == T_UNIMP_INSTR) {
	int trap;

	trap = *(unsigned long *)(scp->sc_pc) & 0x3fffff;

	switch (trap) {
	  case trap_PendingInterrupt:
wlott's avatar
wlott committed
	    interrupt_handle_pending(scp);
	    break;

	  case trap_Halt:
	    fake_foreign_function_call(scp);
	    lose("%%primitive halt called; the party is over.\n");

	  case trap_Error:
	  case trap_Cerror:
	    interrupt_internal_error(signal, code, scp, trap == trap_Cerror);
	    break;

	  case trap_Breakpoint:
	    handle_breakpoint(signal, code, scp);
	    break;

	  case trap_FunctionEndBreakpoint:
	    scp->sc_pc=(int)handle_function_end_breakpoint(signal, code, scp);
	    scp->sc_npc=scp->sc_pc + 4;
	    break;

	  case trap_AfterBreakpoint:
	    *skipped_break_addr = trap_Breakpoint;
	    skipped_break_addr = NULL;
	    *(unsigned long *)scp->sc_pc = displaced_after_inst;
	    scp->sc_mask = orig_sigmask;
	    break;

	  default:
	    interrupt_handle_now(signal, code, scp);
	    break;
	}
    }
    else if (code >= T_SOFTWARE_TRAP + 16 & code < T_SOFTWARE_TRAP + 32)
	interrupt_internal_error(signal, code, scp, FALSE);
    else
	interrupt_handle_now(signal, code, scp);
}

static void sigemt_handler(signal, code, scp)
     int signal, code;
     struct sigcontext *scp;
{
    unsigned long badinst;
    boolean subtract, immed;
    int rd, rs1, op1, rs2, op2, result;

    badinst = *(unsigned long *)scp->sc_pc;
    if ((badinst >> 30) != 2 || ((badinst >> 20) & 0x1f) != 0x11) {
	/* It wasn't a tagged add.  Pass the signal into lisp. */
	interrupt_handle_now(signal, code, scp);
	return;
    }
	
    /* Extract the parts of the inst. */
    subtract = badinst & (1<<19);
    rs1 = (badinst>>14) & 0x1f;
    op1 = SC_REG(scp, rs1);

    /* If the first arg is $ALLOC then it is really a signal-pending note */
    /* for the pseudo-atomic noise. */
    if (rs1 == reg_ALLOC) {
	/* Perform the op anyway. */
	op2 = badinst & 0x1fff;
	if (op2 & (1<<12))
	    op2 |= -1<<13;
	if (subtract)
	    result = op1 - op2;
	else
	    result = op1 + op2;
	SC_REG(scp, reg_ALLOC) = result & ~7;
	arch_skip_instruction(scp);
wlott's avatar
wlott committed
	interrupt_handle_pending(scp);
	return;
    }

    if ((op1 & 3) != 0) {
	/* The first arg wan't a fixnum. */
	interrupt_internal_error(signal, code, scp, FALSE);
	return;
    }

    if (immed = badinst & (1<<13)) {
	op2 = badinst & 0x1fff;
	if (op2 & (1<<12))
	    op2 |= -1<<13;
    }
    else {
	rs2 = badinst & 0x1f;
	op2 = SC_REG(scp, rs2);
    }

    if ((op2 & 3) != 0) {
	/* The second arg wan't a fixnum. */
	interrupt_internal_error(signal, code, scp, FALSE);
	return;
    }

    rd = (badinst>>25) & 0x1f;
    if (rd != 0) {
	/* Don't bother computing the result unless we are going to use it. */
	if (subtract)
	    result = (op1>>2) - (op2>>2);
	else
	    result = (op1>>2) + (op2>>2);

        current_dynamic_space_free_pointer =
            (lispobj *) SC_REG(scp, reg_ALLOC);

	SC_REG(scp, rd) = alloc_number(result);

	SC_REG(scp, reg_ALLOC) =
	    (unsigned long) current_dynamic_space_free_pointer;
    }

    arch_skip_instruction(scp);
}

void arch_install_interrupt_handlers()
{
    interrupt_install_low_level_handler(SIGILL,sigill_handler);
    interrupt_install_low_level_handler(SIGEMT,sigemt_handler);
}


extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);

lispobj funcall0(lispobj function)
{
    lispobj *args = current_control_stack_pointer;

    return call_into_lisp(function, args, 0);
}

lispobj funcall1(lispobj function, lispobj arg0)
{
    lispobj *args = current_control_stack_pointer;

    current_control_stack_pointer += 1;
    args[0] = arg0;

    return call_into_lisp(function, args, 1);
}

lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
{
    lispobj *args = current_control_stack_pointer;

    current_control_stack_pointer += 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 = current_control_stack_pointer;

    current_control_stack_pointer += 3;
    args[0] = arg0;
    args[1] = arg1;
    args[2] = arg2;

    return call_into_lisp(function, args, 3);
}