Skip to content
interrupt.c 15.3 KiB
Newer Older
/* $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/interrupt.c,v 1.26 2001/12/06 19:15:45 pmai Exp $ */
wlott's avatar
wlott committed

/* Interrupt handing magic. */

#include <stdio.h>

#include <signal.h>
hallgren's avatar
hallgren committed
#ifdef mach
wlott's avatar
wlott committed
#ifdef mips
#include <mips/cpu.h>
#endif
hallgren's avatar
hallgren committed
#endif
wlott's avatar
wlott committed

#include "lisp.h"
ram's avatar
ram committed
#include "arch.h"
wlott's avatar
wlott committed
#include "internals.h"
#include "os.h"
ram's avatar
ram committed
#include "interrupt.h"
wlott's avatar
wlott committed
#include "globals.h"
#include "lispregs.h"
#include "validate.h"
#include "monitor.h"
#include "gc.h"
#include "alloc.h"
#include "dynbind.h"
#include "interr.h"

boolean internal_errors_enabled = 0;

struct sigcontext *lisp_interrupt_contexts[MAX_INTERRUPTS];

union interrupt_handler interrupt_handlers[NSIG];
ram's avatar
ram committed
void (*interrupt_low_level_handlers[NSIG]) (HANDLER_ARGS) = {0};

static int pending_signal = 0;

#if defined(SOLARIS) || defined(__OpenBSD__)
ram's avatar
ram committed
static siginfo_t *pending_code;
#define PASSCODE(code) ((code))
#define DEREFCODE(code) ((code))
#else
static int pending_code = 0;
#define PASSCODE(code) (code)
#define DEREFCODE(code) (code)
#endif
wlott's avatar
wlott committed

ram's avatar
ram committed
#ifdef POSIX_SIGS
static sigset_t pending_mask;
#else
static int pending_mask = 0;
#endif
wlott's avatar
wlott committed
static boolean maybe_gc_pending = FALSE;


/****************************************************************\
* Utility routines used by various signal handlers.              *
\****************************************************************/

ram's avatar
ram committed
void 
fake_foreign_function_call(struct sigcontext *context)
wlott's avatar
wlott committed
{
    int context_index;
pw's avatar
pw committed
#ifndef i386
    lispobj oldcont;
#endif

wlott's avatar
wlott committed
    /* Get current LISP state from context */
#ifdef reg_ALLOC
    current_dynamic_space_free_pointer = (lispobj *)SC_REG(context, reg_ALLOC);
hallgren's avatar
hallgren committed
#ifdef alpha
    if((long) current_dynamic_space_free_pointer & 1) {
      printf("Dead in fake_foriegn_function-call, context = %x\n",context);
      lose("");
    }
#endif
wlott's avatar
wlott committed
#endif
#ifdef reg_BSP
    current_binding_stack_pointer = (lispobj *)SC_REG(context, reg_BSP);
#endif
    
#ifndef i386
    /* Build a fake stack frame */
    current_control_frame_pointer = (lispobj *)SC_REG(context, reg_CSP);
    if ((lispobj *)SC_REG(context, reg_CFP)==current_control_frame_pointer) {
        /* There is a small window during call where the callee's frame */
        /* isn't built yet. */
        if (LowtagOf(SC_REG(context, reg_CODE)) == type_FunctionPointer) {
            /* We have called, but not built the new frame, so
               build it for them. */
            current_control_frame_pointer[0] = SC_REG(context, reg_OCFP);
            current_control_frame_pointer[1] = SC_REG(context, reg_LRA);
            current_control_frame_pointer += 8;
            /* Build our frame on top of it. */
            oldcont = (lispobj)SC_REG(context, reg_CFP);
        }
        else {
            /* We haven't yet called, build our frame as if the
               partial frame wasn't there. */
            oldcont = (lispobj)SC_REG(context, reg_OCFP);
        }
    }
    /* ### We can't tell if we are still in the caller if it had to
       reg_ALLOCate the stack frame due to stack arguments. */
    /* ### Can anything strange happen during return? */
    else
        /* Normal case. */
        oldcont = (lispobj)SC_REG(context, reg_CFP);
    
    current_control_stack_pointer = current_control_frame_pointer + 8;

    current_control_frame_pointer[0] = oldcont;
    current_control_frame_pointer[1] = NIL;
    current_control_frame_pointer[2] = (lispobj)SC_REG(context, reg_CODE);
#endif
    
    /* Do dynamic binding of the active interrupt context index
       and save the context in the context array. */
    context_index = SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX)>>2;
    
    if (context_index >= MAX_INTERRUPTS) {
        fprintf(stderr,
		"Maximum number (%d) of interrupts exceeded.  Exiting.\n",
                MAX_INTERRUPTS);
        exit(1);
    }
    
    bind_variable(FREE_INTERRUPT_CONTEXT_INDEX,
		  make_fixnum(context_index + 1));
    
    lisp_interrupt_contexts[context_index] = context;
    
    /* No longer in Lisp now. */
    foreign_function_call_active = 1;
}

ram's avatar
ram committed
void 
undo_fake_foreign_function_call(struct sigcontext *context)
wlott's avatar
wlott committed
{
    /* Block all blockable signals */
ram's avatar
ram committed
#ifdef POSIX_SIGS
    sigset_t block;
    sigemptyset(&block);
    FILLBLOCKSET(&block);
    sigprocmask(SIG_BLOCK, &block, 0);
#else
wlott's avatar
wlott committed
    sigblock(BLOCKABLE);
ram's avatar
ram committed
#endif
wlott's avatar
wlott committed
    
    /* Going back into lisp. */
    foreign_function_call_active = 0;
    
    /* Undo dynamic binding. */
    /* ### Do I really need to unbind_to_here()? */
    unbind();
    
#ifdef reg_ALLOC
    /* Put the dynamic space free pointer back into the context. */
    SC_REG(context, reg_ALLOC) =
        (unsigned long) current_dynamic_space_free_pointer;
#endif
}

ram's avatar
ram committed
void 
interrupt_internal_error(HANDLER_ARGS, boolean continuable)
wlott's avatar
wlott committed
{
pw's avatar
pw committed
#if ( defined( __linux__ ) && defined( i386 ) )
ram's avatar
ram committed
#endif

    fake_foreign_function_call(context);

    /* Allocate the SAP object while the interrupts are still disabled. */
    if (internal_errors_enabled)
	context_sap = alloc_sap(context);
ram's avatar
ram committed
#ifdef POSIX_SIGS
#if !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
    sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
#else
    {
      sigset_t temp;
      sigemptyset(&temp);
      temp.__val[0] = context->uc_sigmask;
      sigprocmask(SIG_SETMASK, &temp, 0);
ram's avatar
ram committed
#else
wlott's avatar
wlott committed
    sigsetmask(context->sc_mask);
#endif /* POSIX_SIGS */
    if (internal_errors_enabled)
	funcall2(SymbolFunction(INTERNAL_ERROR), context_sap,
wlott's avatar
wlott committed
		 continuable ? T : NIL);
    else
	internal_error(context);
    undo_fake_foreign_function_call(context);
    if (continuable)
	arch_skip_instruction(context);
}

ram's avatar
ram committed
void 
interrupt_handle_pending(struct sigcontext *context)
wlott's avatar
wlott committed
{
    boolean were_in_lisp = !foreign_function_call_active;

    SetSymbolValue(INTERRUPT_PENDING, NIL);

    if (maybe_gc_pending) {
	maybe_gc_pending = FALSE;
wlott's avatar
wlott committed
	if (were_in_lisp)
wlott's avatar
wlott committed
	    fake_foreign_function_call(context);
	funcall0(SymbolFunction(MAYBE_GC));
wlott's avatar
wlott committed
	if (were_in_lisp)
wlott's avatar
wlott committed
	    undo_fake_foreign_function_call(context);
    }
#if  !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
    context->uc_sigmask = pending_mask;
#else
    context->uc_sigmask = pending_mask.__val[0];
#endif
    sigemptyset(&pending_mask);
#else
    context->sc_mask = pending_mask;
    pending_mask = 0;
#endif /* POSIX_SIGS */
wlott's avatar
wlott committed
    if (pending_signal) {
ram's avatar
ram committed
	int signal;
#if defined(SOLARIS) || defined(__OpenBSD__)
ram's avatar
ram committed
	siginfo_t *code;
#else
	int code;
#endif
wlott's avatar
wlott committed
	signal = pending_signal;
	code = pending_code;
	pending_signal = 0;
ram's avatar
ram committed
	/* pending_code = 0; */
pw's avatar
pw committed
#if ( defined( __linux__ ) && defined( i386 ) )
        interrupt_handle_now(signal, *context);
ram's avatar
ram committed
#else
ram's avatar
ram committed
	interrupt_handle_now(signal, PASSCODE(code), context);
ram's avatar
ram committed
#endif
wlott's avatar
wlott committed
    }
}


/****************************************************************\
* interrupt_handle_now, maybe_now_maybe_later                    *
*    the two main signal handlers.                               *
\****************************************************************/

ram's avatar
ram committed
void 
interrupt_handle_now(HANDLER_ARGS)
wlott's avatar
wlott committed
{
#if defined(__linux__) && defined(i386)
    GET_CONTEXT
ram's avatar
ram committed
#endif

wlott's avatar
wlott committed
    int were_in_lisp;
    union interrupt_handler handler;
ram's avatar
ram committed

#if defined(__linux__) && defined(i386)
    /*
     * Restore the FPU control word, setting the rounding mode to nearest.
     */

    if (contextstruct.fpstate)
      setfpucw(contextstruct.fpstate->cw & ~0xc00);
wlott's avatar
wlott committed
    handler = interrupt_handlers[signal];

    if(handler.c == (void (*)(HANDLER_ARGS)) SIG_IGN)
wlott's avatar
wlott committed
	return;

ram's avatar
ram committed
    SAVE_CONTEXT(); /**/

wlott's avatar
wlott committed
    were_in_lisp = !foreign_function_call_active;
wlott's avatar
wlott committed
    if (were_in_lisp)
wlott's avatar
wlott committed
        fake_foreign_function_call(context);
    
    if (handler.c == (void (*)(HANDLER_ARGS)) SIG_DFL)
wlott's avatar
wlott committed
	/* This can happen if someone tries to ignore or default on of the */
	/* signals we need for runtime support, and the runtime support */
	/* decides to pass on it.  */
	lose("interrupt_handle_now: No handler for signal %d?\n", signal);
    else if (LowtagOf(handler.lisp) == type_FunctionPointer) {
        /* Allocate the SAP object while the interrupts are still
           disabled. */
        lispobj context_sap = alloc_sap(context);

        /* Allow signals again. */
#ifdef POSIX_SIGS
#if  !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
        sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
#else
	{
	  sigset_t temp;
	  sigemptyset(&temp);
	  temp.__val[0] = context->uc_sigmask;
pw's avatar
pw committed
	  sigprocmask(SIG_SETMASK, &temp, 0);
#else
        sigsetmask(context->sc_mask);
#endif /* POSIX_SIGS */
ram's avatar
ram committed
#if 1
        funcall3(handler.lisp, make_fixnum(signal), make_fixnum(CODE(code)),
		 context_sap);
ram's avatar
ram committed
#else
        funcall3(handler.lisp, make_fixnum(signal), alloc_sap(code),
		 alloc_sap(context));
ram's avatar
ram committed
#endif
    } else {
        /* Allow signals again. */
#ifdef POSIX_SIGS
#if !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
        sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
#else
	{
	  sigset_t temp;
	  sigemptyset(&temp);
	  temp.__val[0] = context->uc_sigmask;
pw's avatar
pw committed
	  sigprocmask(SIG_SETMASK, &temp, 0);
#else
        sigsetmask(context->sc_mask);
#endif /* POSIX_SIGS */
pw's avatar
pw committed
#if ( defined( __linux__ ) && defined( i386 ) )
ram's avatar
ram committed
        (*handler.c)(signal, contextstruct);
#else
wlott's avatar
wlott committed
        (*handler.c)(signal, code, context);
ram's avatar
ram committed
#endif
wlott's avatar
wlott committed
    
wlott's avatar
wlott committed
    if (were_in_lisp)
wlott's avatar
wlott committed
        undo_fake_foreign_function_call(context);
}

ram's avatar
ram committed
static void 
maybe_now_maybe_later(HANDLER_ARGS)
wlott's avatar
wlott committed
{
#if defined(__linux__) && defined(i386)
    GET_CONTEXT
ram's avatar
ram committed
#endif

ram's avatar
ram committed
    SAVE_CONTEXT(); /**/

wlott's avatar
wlott committed
    if (SymbolValue(INTERRUPTS_ENABLED) == NIL) {
        pending_signal = signal;
ram's avatar
ram committed
        pending_code = DEREFCODE(code);
#ifdef POSIX_SIGS
#if !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
ram's avatar
ram committed
        pending_mask = context->uc_sigmask;
	FILLBLOCKSET(&context->uc_sigmask);
#else
	{
	  sigset_t temp;
	  sigemptyset(&temp);
	  pending_mask.__val[0] = context->uc_sigmask;
	  temp.__val[0] = context->uc_sigmask;
	  FILLBLOCKSET(&temp);
	  
	  context->uc_sigmask = temp.__val[0];
	}
#endif

ram's avatar
ram committed
#else
wlott's avatar
wlott committed
        pending_mask = context->sc_mask;
        context->sc_mask |= BLOCKABLE;
#endif /* POSIX_SIGS */

wlott's avatar
wlott committed
        SetSymbolValue(INTERRUPT_PENDING, T);
    } else if (
#ifndef i386
	       (!foreign_function_call_active) &&
#endif
	       arch_pseudo_atomic_atomic(context)) {
wlott's avatar
wlott committed
        pending_signal = signal;
ram's avatar
ram committed
        pending_code = DEREFCODE(code);
#ifdef POSIX_SIGS
#if !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
ram's avatar
ram committed
        pending_mask = context->uc_sigmask;
	FILLBLOCKSET(&context->uc_sigmask);
#else
	{
	  sigset_t temp;
	  sigemptyset(&temp);
	  pending_mask.__val[0] = context->uc_sigmask;
	  temp.__val[0] = context->uc_sigmask;
	  FILLBLOCKSET(&temp);
	  context->uc_sigmask = temp.__val[0];
	}
#endif
ram's avatar
ram committed
#else
wlott's avatar
wlott committed
        pending_mask = context->sc_mask;
        context->sc_mask |= BLOCKABLE;
#endif /* POSIX_SIGS */

wlott's avatar
wlott committed
	arch_set_pseudo_atomic_interrupted(context);
    } else {
#if defined(__linux__) && defined(i386)
      /*
       * Restore the FPU control word, setting the rounding mode to nearest.
       */

      if (contextstruct.fpstate)
	setfpucw(contextstruct.fpstate->cw & ~0xc00);
#endif

pw's avatar
pw committed
#if ( defined( __linux__ ) && defined( i386 ) )
        interrupt_handle_now(signal, contextstruct);
ram's avatar
ram committed
#else
wlott's avatar
wlott committed
        interrupt_handle_now(signal, code, context);
ram's avatar
ram committed
#endif
wlott's avatar
wlott committed
}

/****************************************************************\
* Stuff to detect and handle hitting the gc trigger.             *
\****************************************************************/

#ifndef INTERNAL_GC_TRIGGER
ram's avatar
ram committed
static boolean gc_trigger_hit(HANDLER_ARGS)
wlott's avatar
wlott committed
{
    if (current_auto_gc_trigger == NULL)
	return FALSE;
    else{
	lispobj *badaddr=(lispobj *)arch_get_bad_addr(signal, code, context);
wlott's avatar
wlott committed

	return (badaddr >= current_auto_gc_trigger &&
		badaddr < current_dynamic_space + dynamic_space_size);
wlott's avatar
wlott committed
    }
}
#endif

ram's avatar
ram committed
boolean interrupt_maybe_gc(HANDLER_ARGS)
wlott's avatar
wlott committed
{
    if (!foreign_function_call_active
#ifndef INTERNAL_GC_TRIGGER
		  && gc_trigger_hit(signal, code, context)
wlott's avatar
wlott committed
#endif
	) {
#ifndef INTERNAL_GC_TRIGGER
	clear_auto_gc_trigger();
#endif

	if (arch_pseudo_atomic_atomic(context)) {
	    maybe_gc_pending = TRUE;
	    if (pending_signal == 0) {
ram's avatar
ram committed
#ifdef POSIX_SIGS
pw's avatar
pw committed
#if !defined(__linux__) || (defined(__linux__) && (__GNU_LIBRARY__ < 6))
	        pending_mask = context->uc_sigmask;
ram's avatar
ram committed
		FILLBLOCKSET(&context->uc_sigmask);
pw's avatar
pw committed
#else
		{
		  sigset_t temp;
		  sigemptyset(&temp);
		  pending_mask.__val[0] = context->uc_sigmask;
		  temp.__val[0] = context->uc_sigmask;
		  FILLBLOCKSET(&temp);
pw's avatar
pw committed
		  context->uc_sigmask = temp.__val[0];
		}
#endif
ram's avatar
ram committed
#else
wlott's avatar
wlott committed
		pending_mask = context->sc_mask;
		context->sc_mask |= BLOCKABLE;
#endif /* POSIX_SIGS */
wlott's avatar
wlott committed
	    }
	    arch_set_pseudo_atomic_interrupted(context);
	}
	else {
	    fake_foreign_function_call(context);
	    funcall0(SymbolFunction(MAYBE_GC));
	    undo_fake_foreign_function_call(context);
	}

	return TRUE;
    } else
wlott's avatar
wlott committed
	return FALSE;
}
wlott's avatar
wlott committed
/****************************************************************\
* Noise to install handlers.                                     *
\****************************************************************/

void interrupt_install_low_level_handler
    (int signal,
ram's avatar
ram committed
     void handler(HANDLER_ARGS))
wlott's avatar
wlott committed
{
ram's avatar
ram committed
#ifdef POSIX_SIGS
    struct sigaction sa;

    sa.sa_sigaction = handler;
ram's avatar
ram committed
    sigemptyset(&sa.sa_mask);
    FILLBLOCKSET(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | USE_SA_SIGINFO;
ram's avatar
ram committed

    sigaction(signal, &sa, NULL);
#else
wlott's avatar
wlott committed
    struct sigvec sv;

    sv.sv_handler = handler;
    sv.sv_mask = BLOCKABLE;
    sv.sv_flags = 0;
    sigvec(signal, &sv, NULL);
ram's avatar
ram committed
#endif    
    interrupt_low_level_handlers[signal] =
      (handler == (void (*)(HANDLER_ARGS)) SIG_DFL) ? 0 : handler;
wlott's avatar
wlott committed
}

unsigned long install_handler(int signal,
ram's avatar
ram committed
			      void handler(HANDLER_ARGS))
#ifdef POSIX_SIGS
{
    struct sigaction sa;
    sigset_t old,new;
    union interrupt_handler oldhandler;

    sigemptyset(&new);
    sigaddset(&new, signal);
    sigprocmask(SIG_BLOCK, &new, &old);

    sigemptyset(&new);
    FILLBLOCKSET(&new);

    if(interrupt_low_level_handlers[signal]==0){
	if(handler == (void (*)(HANDLER_ARGS)) SIG_DFL
	   || handler == (void (*)(HANDLER_ARGS)) SIG_IGN)
	    sa.sa_sigaction = handler;
ram's avatar
ram committed
	else if (sigismember(&new, signal))
	    sa.sa_sigaction = maybe_now_maybe_later;
ram's avatar
ram committed
	else
	    sa.sa_sigaction = interrupt_handle_now;
ram's avatar
ram committed

	sigemptyset(&sa.sa_mask);
	FILLBLOCKSET(&sa.sa_mask);
	sa.sa_flags = USE_SA_SIGINFO | SA_RESTART;
ram's avatar
ram committed

	sigaction(signal, &sa, NULL);
    }

    oldhandler = interrupt_handlers[signal];
    interrupt_handlers[signal].c = handler;

    sigprocmask(SIG_SETMASK, &old, 0);

    return (unsigned long) oldhandler.lisp;
ram's avatar
ram committed
}
#else
wlott's avatar
wlott committed
{
    struct sigvec sv;
    int oldmask;
    union interrupt_handler oldhandler;

    oldmask = sigblock(sigmask(signal));

    if(interrupt_low_level_handlers[signal]==0){
	if(handler == (void (*)(HANDLER_ARGS)) SIG_DFL
	   || handler == (void (*)(HANDLER_ARGS)) SIG_IGN)
wlott's avatar
wlott committed
	    sv.sv_handler = handler;
	else if (sigmask(signal) & BLOCKABLE)
wlott's avatar
wlott committed
	    sv.sv_handler = maybe_now_maybe_later;
	else
	    sv.sv_handler = interrupt_handle_now;

	sv.sv_mask = BLOCKABLE;
	sv.sv_flags = 0;
	sigvec(signal, &sv, NULL);
    }

    oldhandler = interrupt_handlers[signal];
    interrupt_handlers[signal].c = handler;

    sigsetmask(oldmask);

    return (unsigned long) oldhandler.lisp;
wlott's avatar
wlott committed
}
ram's avatar
ram committed
#endif
wlott's avatar
wlott committed

void interrupt_init(void)
{
    int i;

    for (i = 0; i < NSIG; i++)
        interrupt_handlers[i].c = (void (*)(HANDLER_ARGS)) SIG_DFL;
wlott's avatar
wlott committed
}