head	1.1;
access;
symbols;
locks; strict;
comment	@ * @;


1.1
date	94.08.27.23.44.04;	author rluebbert;	state Exp;
branches;
next	;


desc
@Machdep for 68000
@


1.1
log
@Initial revision
@
text
@/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *  Portions Copyright (C) 1994 Rafael W. Luebbert
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  $Id: machdep.c,v 1.6 1994/06/19 15:13:50 rluebbert Exp $
 *
 *  $Log: machdep.c,v $
 *  Revision 1.6  1994/06/19  15:13:50  rluebbert
 *  *** empty log message ***
 *
 *  Revision 1.4  1992/10/20  16:25:24  mwild
 *  no nasty 'c' polling in DEF-signalhandler...
 *
 *  Revision 1.3  1992/08/09  20:57:59  amiga
 *  add volatile to sysbase access, or the optimizer takes illegal shortcuts...
 *
 *  Revision 1.2  1992/07/04  19:20:20  mwild
 *  add yet another state in which not to force a context switch.
 *  Probably unnecessary paranoia...
 *
 * Revision 1.1  1992/05/14  19:55:40  mwild
 * Initial revision
 *
 */

#define KERNEL
#include "ixemul.h"

#include <exec/execbase.h>
#undef DEBUG
#ifdef DEBUG
#define DP(a) KPrintF a
#else
#define DP(a)
#endif

extern struct ExecBase *SysBase;

/* jump to pc in supervisor mode, usp is set to USP before */
extern void volatile supervisor (u_int pc, u_int usp);

/* context restore functions for 68000 and 68020 rsp */

/* takes the sigcontext * from the usp and restores it 
 * Assumes it's called by Supervisor(), ie. with an exception frame
 */
extern void volatile do_sigreturn (void);

/*
 * These two are callable with jsr from supervisor mode, and then
 * set up a fake exception frame and call do_sigreturn().
 */
extern void volatile sup00_do_sigreturn_ssp (u_int ssp);
extern void volatile sup00_do_sigreturn (void);
extern void volatile restore_00 ();
extern void volatile sup20_do_sigreturn_ssp (u_int ssp);
extern void volatile sup20_do_sigreturn (void);
extern void volatile restore_20 ();
/*
 * Either one of sup{00,20}_do_sigreturn, set by configure_context_switch ();
 */
static void volatile (*sup_do_sigreturn) (void);
static void volatile (*sup_do_sigreturn_ssp) (u_int ssp);

void setrun (struct Task *t);
void sendsig(struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr);
void sig_exit ();

/*
 * NOTE: this variable belongs to ixemulbase, not to the current task. But
 *       due to the nature of launch/switch, we can access them here without
 *       locking. It quite a natural thing to do actually ;-)
 */
struct user *curproc;

struct sigframe {
  int			sf_signum;	/* signo for handler */
  int			sf_code;	/* additional info for handler */
  void			*sf_addr;	/* yet another info for handler ;-)) */
  sig_t			sf_handler;	/* handler addr for u_sigc */
  struct sigcontext 	sf_sc;		/* actual context */
};


void
configure_context_switch ()
{
if(betterthan68000())
	{
      sup_do_sigreturn = sup20_do_sigreturn;
      sup_do_sigreturn_ssp = sup20_do_sigreturn_ssp;
	}
else
	{
      sup_do_sigreturn = sup00_do_sigreturn;
      sup_do_sigreturn_ssp = sup00_do_sigreturn_ssp;
	}
}

void volatile
sigreturn (struct sigcontext *sc)
{
  supervisor ((u_int) do_sigreturn, (u_int) sc);
}

void volatile
sig_trampoline (struct sigframe sf)
{
  if (u.u_a4)
    asm ("movel %0,a4" : : "g" (u.u_a4));
  sf.sf_handler (sf.sf_signum, sf.sf_code, sf.sf_addr, & sf.sf_sc);

  sigreturn (& sf.sf_sc);
}

/*
 * This one is executed in Supervisor mode, just before dispatching this
 * task, so be as quick as possible here !
 */
void
sig_launch () 
{
  struct ExecBase	*SysBase 	= *(struct ExecBase **) 4;
  struct Task 		*me 		= SysBase->ThisTask;
  /* precalculate struct user, so we don't have to go thru SysBase all the time */
  struct user 		*p 		= (struct user *) me->tc_TrapData;
  sigset_t 		sigmsg 		= sigmask (SIGMSG);
  sigset_t		sigint 		= sigmask (SIGINT);
  sigset_t 		newsigs;
  int 			i;
  u_int			usp, orig_usp;
  struct sigcontext 	*sc;
  u_int			ret_pc, ret_ssp;

  usp = orig_usp = get_usp () + 8; 	/* set up by our glue_launch() stub */
  
  /* remember who we are */
  curproc = p;
  /* if we're inside ix_sleep, no signal processing is done to break the
     Wait there as soon as possible. Signals resume on return of ix_sleep */
  if (p->p_stat == SSLEEP)
    return;

  /* special processing for Wait()ing in Commodore inet.library. They
     do reasonable interrupt checking, but only on SIGBREAKF_CTRL_C. So
     whenever we have a signal to deliver, send ^C.. */
  if (p->p_stat == SWAIT)
    {
#if 0
/* not working clean yet, have to think of a more elaborate solution later */

      /* handle ^C here, since we're going to unconditionally raise it 
	 afterwards. */
      if (!(p->p_sigignore & sigmask(SIGINT)) && SetSignal (0, SIGBREAKF_CTRL_C))
        p->p_sig |= sigmask (SIGINT);
#endif

      if (CURSIG (p))
        Signal (me, SIGBREAKF_CTRL_C);
      return;
    }


  /* smells kludgy I know...... */
  if (me->tc_TDNestCnt >= 0 || me->tc_IDNestCnt >= 0)
    return;

  /* the glue passes us the values of the pc and ssp to restore, if we should
   * decide to sup_do_sigreturn_ssp() out of here, instead of leaving harmlessly..
   */
  ret_pc  = ((u_int *)usp)[-2];
  ret_ssp = ((u_int *)usp)[-1];
  
  /* push a sigcontext that will get us back if no other signals
   * were produced */
  usp -= sizeof (struct sigcontext);
  sc = (struct sigcontext *) usp;
  set_usp (usp);
  
  sc->sc_onstack = p->u_onstack;
  sc->sc_mask	 = p->p_sigmask;
  sc->sc_sp	 = orig_usp;
  /* the OS context restore function expects a5 to contain the usp, so
   * we have to obey.. */
  sc->sc_fp	 = orig_usp;
  sc->sc_ap	 = *(u_int *)&me->tc_Flags;
  sc->sc_pc	 = ret_pc;
  sc->sc_ps	 = get_sr ();
  
  /*
   * first check amigados signals. If SIGMSG is set to SIG_IGN or SIG_DFL, 
   * we do our default mapping of SIGBREAKF_CTRL_C into SIGINT.
   */
  newsigs	  = me->tc_SigRecvd &~ p->u_lastrcvsig;
  p->u_lastrcvsig = me->tc_SigRecvd;
#ifdef SOCKET
  if (p->u_InetBase)
    {
      int urgmask = 1<<p->u_sigurg;
      int iomask  = 1<<p->u_sigio;
      
      if (newsigs & urgmask)
        {
          if (! (p->p_sigignore & sigmask (SIGURG)))
            _psignal (me, SIGURG);
            
          me->tc_SigRecvd &= ~urgmask;
          p->u_lastrcvsig &= ~urgmask;
        }
        
      if (newsigs & iomask)
        {
          if (! (p->p_sigignore & sigmask (SIGIO)))
            _psignal (me, SIGIO);
            
          me->tc_SigRecvd &= ~iomask;
          p->u_lastrcvsig &= ~iomask;
        }
    }

#endif
  if (((p->p_sigignore & sigmsg) || !(p->p_sigcatch & sigmsg)) 
      && (newsigs & SIGBREAKF_CTRL_C))
    {
      /* in that case send us a SIGINT, if it's not ignored */
      if (!(p->p_sigignore & sigint))
        _psignal (me, SIGINT);
        
      /* in this mode we fully handle and use SIGBREAKF_CTRL_C, so remove it
       * from the Exec signal mask */
       
      me->tc_SigRecvd &= ~SIGBREAKF_CTRL_C;
      p->u_lastrcvsig &= ~SIGBREAKF_CTRL_C;
    }
  else if (newsigs && (p->p_sigcatch & sigmsg))
    {
      /* if possible, deliver the signal directly to get a code argument */
      if (!(p->p_flag & STRC) && !(p->p_sigmask & sigmsg))
        {
          p->u_ru.ru_nsignals++;
          sendsig(p, p->u_signal[SIGMSG], SIGMSG, p->p_sigmask, newsigs, 0);
          p->p_sigmask |= p->u_sigmask[SIGMSG] | sigmsg;
          setrun (me);
        }
      else
        _psignal (me, SIGMSG);
    }

  /* stack watch.. NEVER do this when vforked, the sp is out of bounds then.. */
  if (ix.ix_watch_stack && p->u_red_zone && ! p->p_vfork_msg && ! p->u_onstack)
    {
      if (usp < p->u_red_zone)
        _psignal (me, SIGSEGV);
    }

  if (i = CURSIG(p))
      psig (p, i);

  /* now try to optimize. We could always call sup_do_sigreturn here, but if no
   * signals generated frames, we can just as well simply return, after having
   * restored our usp */
  if (usp == get_usp ())
    {
      /* this is probably not even necessary, since after processing sig_launch
       * the OS reinstalls the usp as me->tc_SPReg, but I guess it's cleaner to
       * do it explicitly here, to show that we reset usp to what it was before
       */
      set_usp (orig_usp);
      return;
    }

  sup_do_sigreturn_ssp (ret_ssp);
}


/*
 * nothing magic about this... when leaving, we note that no process is currently
 * executing (at least none under our control ;-))
 */

void
switch_glue ()
{
  curproc = 0;
}


/*
 * Send an interrupt to process.
 * Called from psig() which is called from sig_launch, thus we are in
 * SUPERVISOR .
 */
void
sendsig (struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr)
{
  struct ExecBase	*SysBase 	= *(struct ExecBase **) 4;
  struct Task		*me		= SysBase->ThisTask;
  u_int 		usp, orig_usp;
  struct sigframe 	*sf;
  struct sigcontext	*sc;
  int			oonstack;

  orig_usp = get_usp();	/* get value to restore later */

  oonstack = p->u_onstack;

  if (!p->u_onstack && (p->u_sigonstack & sigmask(sig)))
    {
      p->u_onstack = 1;
      usp = (u_int) p->u_sigsp;
    }
  else
    usp = orig_usp;
  
  /* push signal frame */
  usp -= sizeof (struct sigframe);
  sf = (struct sigframe *) usp;
  
  /* fill out the frame */
  sf->sf_signum        = sig;
  sf->sf_code          = code;
  sf->sf_addr	       = addr;
  sf->sf_handler       = catcher;
  sf->sf_sc.sc_onstack = oonstack;
  sf->sf_sc.sc_mask    = mask;
  sf->sf_sc.sc_sp      = (int) orig_usp;	/* previous sigcontext */
  sf->sf_sc.sc_fp      = 0;
  sf->sf_sc.sc_ap      = *(u_int *)&me->tc_Flags;
  sf->sf_sc.sc_ps      = get_sr ();		/* we're in supervisor then */
  sf->sf_sc.sc_pc      = (int) sup_do_sigreturn;/* this pc will restore it */

  /* push a signal context to call sig_trampoline */
  usp -= sizeof (struct sigcontext);
  sc = (struct sigcontext *) usp;

  /*
   * NOTE: we set the default of a handler to Permit(), Enable(). I guess this
   *       makes sense, since if either Forbid() or Disable() is active, it
   *	   shouldn't be possible to invoke a signal anyway, EXCEPT if the
   *	   task is Wait()ing, then the OS calls Switch() directly while
   *	   either Disable() or Forbid() is active (depends on OS version).
   */

  sc->sc_onstack = p->u_onstack;
  sc->sc_mask    = p->p_sigmask;
  sc->sc_sp	 = ((int) sf) - 4; /* so that sp@@(4) is the argument */
  sc->sc_fp	 = 0;
  sc->sc_ap	 = (me->tc_Flags << 24) | (me->tc_State << 16) |
  		   ((u_char)(-1) << 8) | (u_char)(-1);
  sc->sc_ps	 = 0;	/* thus we switch into user-mode now! */
  sc->sc_pc	 = (int) sig_trampoline;
  
  set_usp (usp);
}


/*
 * called as the default action of a signal that terminates the process
 */
void
sig_exit (unsigned int code)
{
#ifndef QUIET_SIGEXIT
  /* the whole purpose of this code inside `ifndef QUIET_SIGEXIT' is to
   * prettyprint and identify the job that just terminates
   * This stuff should be handled by a shell, but since there's (yet) no
   * shell that knows how to interpret a signal-exit code (ored with 0x80)
   * I have to do it here myself...
   */

  extern char *sys_siglist[NSIG];
  struct Process *me = (struct Process *)((*(struct ExecBase **)4)->ThisTask);
  char err_buf[255];
  struct CommandLineInterface *cli;
  char process_name[255];
  int is_fg;

  /* output differs depending on
   *  o  whether we're a CLI or a WB process (stderr or requester)
   *  o  whether this is a foreground or background process
   *  o  whether this is SIGINT or not
   */

  /* make sure we're not interrupted in this last step.. */
  syscall (SYS_sigsetmask, ~0);
  
  if (cli = BTOCPTR (me->pr_CLI))
    {
      char *tmp = BTOCPTR (cli->cli_CommandName);
      int   len = *tmp++;
      
      if (len > sizeof (process_name) - 1)
        len = sizeof (process_name) - 1;
      
      bcopy (tmp, process_name, len);
      process_name[len] = 0;
      
      is_fg = cli->cli_Interactive && !cli->cli_Background;

    }
  else
    {
      process_name[0] = 0;
      if (me->pr_Task.tc_Node.ln_Name)
        strncpy (process_name, me->pr_Task.tc_Node.ln_Name, sizeof (process_name) - 1);
        
      /* no WB process is ever considered fg */
      is_fg = 0;

    }

  /* if is_fg and SIGINT, simulate tty-driver and display ^C */
  if (!(is_fg && (code == SIGINT)))
    {
      strcpy (err_buf, (code < NSIG) ? sys_siglist[code] : "Unknown signal");


      /* if is_fg, don't display the job */
      if (! is_fg)
        {
          strcat (err_buf, " - ");
	  strcat (err_buf, process_name);
          /* if we're a CLI we have an argument line saved, that we can print
           * as well */
	  if (cli)
      	    {
	      int line_len;
	      char *cp;
	      
	      /* we can display upto column 77, this should be save on all normal
	       * amiga CLI windows */
	      line_len = 77 - strlen (err_buf) - 1;
	      if (line_len > u.u_arglinelen)
	        line_len = u.u_arglinelen;

	      if (line_len > 0 && u.u_argline)
	        {
	          strcat (err_buf, " ");
		  strncat (err_buf, u.u_argline, line_len);
		}

	      /* now get rid of possible terminating line feeds/cr's */
	      for (cp = err_buf; *cp && *cp != '\n' && *cp != '\r'; cp++) ;
	      *cp = 0;
	    }
	}

      if (cli)
        {
          /* uniformly append ONE line feed */
	  strcat (err_buf, "\n");
          syscall (SYS_write, 2, err_buf, strlen (err_buf));
        }
      else
        ix_panic (err_buf);
    }
  else
    syscall (SYS_write, 2, "^C\n", 3);
#endif  

  syscall (SYS_exit, code | 0x80);
  /* not reached */
}

/*
 * This is used to awaken a possibly sleeping sigsuspend()
 * and to force a context switch, if we send a signal to ourselves
 */
void
setrun (struct Task *t)
{
  struct user *p = (struct user *) t->tc_TrapData;
  volatile struct ExecBase *SysBase = *(volatile struct ExecBase **)4;
  u_int curr_disp;
  u_int	sr;

  /* NOTE: the context switch is done to make sure sig_launch() is called as
   *       soon as possible in the respective task. It's not nice if you can
   *       return from a kill() to yourself, before the signal handler had a
   *       chance to react accordingly to the signal..
   */
  asm volatile (" 
    movel a5,a0
    lea	  pc@@(Lget_sr-.+2),a5
    movel 4:w,a6
    jsr	  a6@@(-0x1e)
    movel a1,%0
    bra	  Lskip
Lget_sr:
    movew sp@@,a1	| get sr register from the calling function
    rte
Lskip:
    movel a0,a5
	" : "=g" (sr) : : "a0", "a1", "a6");

  /* Don't force context switch if:
     o  running in Supervisor mode
     o  we setrun() some other process
     o  running under either Forbid() or Disable() */
  if ((sr & 0x2000) || SysBase->ThisTask != t || p->p_stat == SSLEEP ||
      p->p_stat == SWAIT || SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0)
    {
      extern int select();

      /* make testing of p_stat and reaction atomic */
      Disable ();

      if (p->p_stat == SWAIT)
        Signal (t, SIGBREAKF_CTRL_C);
      else if (p->p_wchan == (caddr_t) p)
	{
	  DP (("setrun $%lx\n", p));
          ix_wakeup (p);
        }
      else if (p->p_wchan == (caddr_t) select)
        Signal (t, 1<<p->u_sleep_sig);
        
      Enable ();
      return;
    }
  for (curr_disp = SysBase->DispCount; curr_disp == SysBase->DispCount; ) ;
  /* this quite brute-force method, but the only thing I could think of that
   * really guarantees that there was a context switch.. */
}

/*
 * Mapping from vector numbers into signals
 */
const static int hwtraptable[256] = {
  SIGILL, /* Reset initial stack pointer */
  SIGILL, /* Reset initial program counter */
  SIGBUS, /* Bus Error */
  SIGBUS, /* Address Error */
  SIGILL, /* Illegal Instruction */
  SIGFPE, /* Zero Divide */
  SIGFPE, /* CHK, CHK2 Instruction */
  SIGFPE, /* cpTRAPcc, TRAPcc, TRAPV Instruction */
  SIGILL, /* Privilege Violation */
  SIGTRAP,/* Trace */
  SIGEMT, /* Line 1010 Emulator */
  SIGEMT, /* Line 1111 Emulator */
  SIGILL,
  SIGILL, /* Coprocessor Protocol Violation */
  SIGILL, /* Format Error */
  SIGILL, /* Uninitialized Interrupt */
  SIGILL, /* 16 */
  SIGILL, /* 17 */
  SIGILL, /* 18 */
  SIGILL, /* 19 */		/* unimplemented, reserved */
  SIGILL, /* 20 */
  SIGILL, /* 21 */
  SIGILL, /* 22 */
  SIGILL, /* 23 */
  SIGILL, /* spurious Interrupt */
  SIGILL, /* Level 1 Interrupt Autovector */
  SIGILL, /* Level 2 Interrupt Autovector */
  SIGILL, /* Level 3 Interrupt Autovector */
  SIGILL, /* Level 4 Interrupt Autovector */
  SIGILL, /* Level 5 Interrupt Autovector */
  SIGILL, /* Level 6 Interrupt Autovector */
  SIGILL, /* Level 7 Interrupt Autovector */
  SIGILL, /* Trap #0 (not available on Unix) */
  SIGILL, /* Trap #1 */
  SIGILL, /* Trap #2 */
  SIGILL, /* Trap #3 */
  SIGILL, /* Trap #4 */
  SIGILL, /* Trap #5 */
  SIGILL, /* Trap #6 */
  SIGILL, /* Trap #7 */
  SIGILL, /* Trap #8 */
  SIGILL, /* Trap #9 */
  SIGILL, /* Trap #10 */
  SIGILL, /* Trap #11 */
  SIGILL, /* Trap #12 */
  SIGILL, /* Trap #13 */
  SIGILL, /* Trap #14 */
  SIGILL, /* Trap #15 (not available on Unix) */
  SIGFPE, /* FPCP Branch or Set on Unordererd Condition */
  SIGFPE, /* FPCP Inexact Result */
  SIGFPE, /* FPCP Divide by Zero */
  SIGFPE, /* FPCP Underflow */
  SIGFPE, /* FPCP Operand Error */
  SIGFPE, /* FPCP Overflow */
  SIGFPE, /* FPCP Signaling NAN */
  SIGILL,
  SIGBUS, /* MMU Configuration Error */
  SIGILL, /* MMU Illegal Operation (only 68851) */
  SIGILL, /* MMU Privilege Violation (only 68851) */
  /* rest undefined or free user-settable.. */
};

/*
 * handle traps handled over from the lowlevel trap handlers
 */
void
trap (u_int format, void *addr)
{
  struct ExecBase	*SysBase 	= *(struct ExecBase **) 4;
  struct Task 		*me 		= SysBase->ThisTask;
  /* precalculate struct user, so we don't have to go thru SysBase all the time */
  struct user 		*p 		= (struct user *) me->tc_TrapData;
  int 			sig;
  u_int			usp, orig_usp;
  struct sigcontext 	*sc;
#if __GNUC__ != 2 || defined(BROKEN_GCC20)
  int volatile 		been_here;
#endif
  u_int			ret_pc, ret_ssp;


  usp = orig_usp = get_usp () + 8;	/* skip argument parameters */
  ret_pc  = ((u_int *)usp)[-2];
  ret_ssp = ((u_int *)usp)[-1];
  
  /* push a sigcontext that will get us back here if no other signals
   * were produced */
  usp -= sizeof (struct sigcontext);
  sc = (struct sigcontext *) usp;
  set_usp (usp);

  sc->sc_onstack = p->u_onstack;
  sc->sc_mask	 = p->p_sigmask;
  sc->sc_sp	 = orig_usp;
  sc->sc_fp	 = get_fp ();
  sc->sc_ap	 = *(u_int *)&me->tc_Flags;
  sc->sc_pc	 = ret_pc;
  sc->sc_ps	 = get_sr ();

  /* format contains the vector * 4, in the lower 12 bits */
  sig = *(int *)((u_char *)hwtraptable + (format & 0x0fff));

  trapsignal (me, sig, format, addr);

  if (sig = CURSIG(p))
    psig (p, sig);

  /* now try to optimize. We could always call sup_do_sigreturn here, but if no
   * signals generated frames, we can just as well simply return, after having
   * restored our usp */
  if (usp == get_usp ())
    {
      set_usp (orig_usp);
      return;
    }

  sup_do_sigreturn_ssp (ret_ssp);
}
@
