Coherent4.2.10/i386/ndp.c

Compare this file to the similar file:
Show the results in this format:

/* $Header: /ker/i386/RCS/ndp.c,v 2.5 93/10/29 00:57:20 nigel Exp Locker: nigel $ */
/*
 * All ndp-related functions, except for assembler routines
 *
 * $Log:	ndp.c,v $
 * Revision 2.5  93/10/29  00:57:20  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.4  93/09/02  18:12:11  nigel
 * Minor edits to use new flag system
 * 
 * Revision 2.3  93/08/19  03:40:12  nigel
 * Nigel's R83
 * 
 */

#include <common/_gregset.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <stddef.h>

#define	_KERNEL		1

#include <kernel/reg.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/ndp.h>
#include <sys/seg.h>

void	emFinit();
void	emtrap();
void	fptrap();
void	ndpConRest();
void	ndpDetach();
void	ndpEmTraps();
void	ndpEndProc();
void	ndpIrq();
void	ndpMine();
void	ndpNewOwner();
void	ndpNewProc();
char *	ndpTypeName();
int	rdEmTrapped();
int	rdNdpSaved();
int	rdNdpSavedU();
int	rdNdpUser();
void	senseNdp();
void	wrEmTrapped();
void	wrNdpSaved();
void	wrNdpSavedU();
void	wrNdpUser();

/*
 * ndp control word is 16 bits:
 * 0000 RC:2 PC:2 01 PM:1 UM:1 OM:1 ZM:1 DM:1 IM:1
 * RC - rounding control
 * PC - precision control
 * PM - precision mask
 * UM - underflow mask
 * OM - overflow mask
 * ZM - zero divide mask
 * DM - denormal operand mask
 * IM - invalid operation mask
 * for masks, 1 masks the exception
 *
 * iBCS2 page 3-46 specifies the following:
 *   0000 : 00 10 : 0 1 1 1 : 0 0 1 0 = 0x0272
 */

/* Configurable ndp-related variables. */
extern short	ndpCW;
extern short	ndpDump;
extern short	ndpType;
extern int	ndpEmSig;

/* Patchable emulator-related function pointers. */
extern int	(*ndpEmFn) ();
extern int	(*ndpKfsave) ();
extern int	(*ndpKfrstor) ();

static int	kerEm = 1;	/* RAM copy of CR0 EM bit */
static int	ndpUseg;	/* system global address of U segment */
static PROC *	ndpOwner;	/* process whose stuff is now in ndp */

/*
 * Called from trap handler the first time a process executes an ndp
 * instruction.
 */

void
ndpNewOwner ()
{
	UPROC *		up;

	/* disable further emulator traps for this process */
	wrNdpUser (1);
	ndpEmTraps (0);

	/* save old ndp status, if any process was using it */
	if (ndpOwner) {
		int work = workAlloc ();
		ptable1_v [work] = sysmem.u.pbase [btocrd (ndpUseg)] | SEG_RW;
		up = (UPROC *) (ctob (work) + U_OFFSET);
		ndpSave (& up->u_ndpCon);
		wrNdpSavedU (1, up);
		workFree (work);
	}

	/* Make current process NDP owner */
	ndpMine ();

	/* give process a clean ndp */
	ndpInit (ndpCW);
}


/*
 * NDP initialization for a new process.
 * Called at exec time.
 * Sets defaults, before it is known whether the process uses NDP or not.
 */

void
ndpNewProc()
{
	/* default for a process is to trap on NDP instructions */
	ndpEmTraps (1);
	wrNdpUser (0);
	wrNdpSaved (0);
	wrEmTrapped (0);
}


/*
 * Restore some ndp info when doing a regular conrest().
 * Called just after conrest - u area has just been restored.
 */

void
ndpConRest()
{
	UPROC *		up;

	/* make CR0 EM bit match what this process needs */
	ndpEmTraps (rdNdpUser () ? 0 : 1);

	/*
	 * If current process uses ndp, may need to fix ndp state
	 *
	 * By the nature of NDP save op's, if the NDP owner's NDP state
	 * is saved, then it's not in the NDP.
	 *
	 * So, we have to be careful (1) not to save twice, and (2) to
	 * restore, even if we are NDP owner, if NDP state is saved.
	 */

	if (rdNdpUser ()) {
		if (ndpOwner != SELF) {
			if (ndpOwner) {		/* save old ndp state */
				int work = workAlloc ();
				ptable1_v [work] =
				  sysmem.u.pbase [btocrd (ndpUseg)] | SEG_RW;
				up = (UPROC *) (ctob (work) + U_OFFSET);
				if (! rdNdpSavedU (up)) {
					ndpSave (& up->u_ndpCon);
					wrNdpSavedU (1, up);
				}
				workFree (work);
			}

			/* Make current process NDP owner and reload ndp state */
			ndpMine ();
			ndpRestore (& u.u_ndpCon);
			wrNdpSaved (0);
		} else if (rdNdpSaved ()) {
			ndpRestore (& u.u_ndpCon);
			wrNdpSaved (0);
		}
	}
}


/*
 * When a process exits, it relinquishes the ndp.
 */

void
ndpEndProc()
{
	if (SELF == ndpOwner)
		ndpDetach ();
}


/*
 * Factor out fp state dump.
 */

static void
fpdump (fsp, status, str)
struct _fpstate * fsp;
int		status;
char	      *	str;
{
	printf ("\nfcs=%x  fip=%x  fos=%x  foo=%x\n",
		fsp->cssel & 0xffff, fsp->ipoff,
		fsp->datasel & 0xffff, fsp->dataoff);
	printf("User %s Trap: ", str);

	if (status & 1)
		printf("Invalid Operation");
	else if (status & 2)
		printf("Denormalized Operand");
	else if (status & 4)
		printf("Divide by Zero");
	else if (status & 8)
		printf("Overflow");
	else if (status & 0x10)
		printf("Underflow");
	else if (status & 0x20)
		printf("Precision");
	else
		printf("???");
}


/*
 * fptrap()
 *
 * Entered when NDP generates a CPU error.
 * err is either SIFP or 0x0D40
 */

void
fptrap (regset)
gregset_t	regset;
{
	unsigned short	sw;		/* ndp status word */
	struct _fpstate * fsp = & u.u_ndpCon.fpstate;

	/*
	 * Send user a signal.
	 */

	ndpSave (fsp);

	/* Clear exception flag in NDP to prevent runaway trap. */
	sw = fsp->status = fsp->sw;
	fsp->sw &= 0x7f00;
	wrNdpSaved (1);

	if (ndpDump) {
		curr_register_dump (& regset, _PRIVILEGE_RING_1);
		fpdump (fsp, sw, "Floating Point");
	}
	sendsig (SIGFPE, SELF);
}


/*
 * emtrap()
 *
 * Entered when NDP opcode is executed and EM bit of CR0 is 1.
 * err is SIXNP (Device Not Available Fault)
 */

void
emtrap (regset)
gregset_t	regset;
{
	switch (ndpType) {
	case NDP_TYPE_287:
	case NDP_TYPE_387:
	case NDP_TYPE_486:
		ndpNewOwner ();
		break;

	default:
		if (ndpDump) {
			curr_register_dump (& regset, _PRIVILEGE_RING_1);
			printf ("emulation trap\n");
		}
		if (! rdEmTrapped ()) {
			wrEmTrapped (1);
			emFinit (& u.u_ndpCon);
		}
		if (ndpEmFn) {
			int looker = 1;

			/*
			 * No emulator lookahead if ptraced or
			 * single step process.
			 */
			if ((SELF->p_flags & PFTRAC) != 0 ||
			    __FLAG_GET_FLAG (__FLAG_REG (& regset), __TRAP))
				looker = 0;
			(* ndpEmFn) (& regset, & u.u_ndpCon, looker);
		} else
			sendsig (ndpEmSig, SELF);
	}
}


/*
 * IRQ 13 handler.  Not used with 486.
 */
void
ndpIrq ()
{
	struct _fpstate * fsp = & u.u_ndpCon.fpstate;
	unsigned short sw;

	outb(NDP_PORT, 0);

	/*
	 * Send user a signal.
	 */

	ndpSave (fsp);

	/* Clear exception flag in NDP to prevent runaway trap. */
	sw = fsp->status = fsp->sw;
	fsp->sw &= 0x7f00;
	wrNdpSaved (1);

	if (ndpDump)
		fpdump (fsp, sw, "387");

	sendsig (SIGFPE, SELF);
}


/*
 * ----------------------------------------------------------------------
 * Routines concerned with whether current process has used the ndp.
 */

int
rdNdpUser()
{
	return (u.u_ndpFlags & NF_NDP_USER) ? 1 : 0;
}


void
wrNdpUser(n)
int n;
{
	if (n)
		u.u_ndpFlags |= NF_NDP_USER;
	else
		u.u_ndpFlags &= ~NF_NDP_USER;
}


/*
 * Since saving NDP state is destructive, we need to keep track
 * of where the current NDP state is - u area, or NDP?
 */

int
rdNdpSaved ()
{
	return (u.u_ndpFlags & NF_NDP_SAVED) ? 1 : 0;
}


int
rdNdpSavedU (up)
UPROC * up;
{
	return (up->u_ndpFlags & NF_NDP_SAVED) ? 1 : 0;
}


void
wrNdpSaved (n)
int n;
{
	if (n)
		u.u_ndpFlags |= NF_NDP_SAVED;
	else
		u.u_ndpFlags &= ~NF_NDP_SAVED;
}


void
wrNdpSavedU (n, up)
int n;
UPROC * up;
{
	if (n)
		up->u_ndpFlags |= NF_NDP_SAVED;
	else
		up->u_ndpFlags &= ~NF_NDP_SAVED;
}


/*
 * Enable (1) or disable (0) emulator traps.
 */

void
ndpEmTraps (n)
int n;
{
	if (kerEm != n) {
		kerEm = n;
		setEm (n);
	}
}


/*
 * Make ndp owned by no one.
 */

void
ndpDetach()
{
	ndpOwner = 0;
	ndpUseg = 0;
}


/*
 * Make ndp owned by the current process.
 */

void
ndpMine()
{
	SR *		srp = & SELF->p_segl [SIUSERP];
	SEG *		sp = srp->sr_segp;

	ndpOwner = SELF;
	ndpUseg = MAPIO (sp->s_vmem, U_OFFSET);
}


/*
 * ----------------------------------------------------------------------
 * Code concerned with identifying coprocessor type, and taking specialized
 * action depending on the type.
 */

/*
 * Using usual algorithms, determine existence and type of NDP.
 * If interrupt vector needs to be set for FP exception, do it.
 *
 * If 2's bit of int11 is on, NDP is present.
 */

void
senseNdp()
{
	if (ndpType == NDP_TYPE_AUTO) {
		ndpEmTraps (0);		/* Will need to do some FP code. */
		ndpType = ndpSense ();	/* Rely on assembler tricks now. */
		ndpEmTraps (1);
	}
	if (ndpType == NDP_TYPE_387 || ndpType == NDP_TYPE_287)
		setivec (NDP_IRQ, ndpIrq);
}


/*
 * Called from main().
 * Return name string for the type of coprocessor detected.
 */

char *
ndpTypeName()
{
	switch(ndpType) {
	case NDP_TYPE_NONE:
		return "No NDP.  ";
		break;

	case NDP_TYPE_287:
		return "NDP=287.  ";
		break;

	case NDP_TYPE_387:
		return "NDP=387.  ";
		break;

	case NDP_TYPE_486:
		return "NDP=486.  ";
		break;

	default:
		return "**ERROR: Bad ndp type**";
	}
}


/*
 * ----------------------------------------------------------------------
 * Little routines for tracking emulator state.
 */

int
rdEmTrapped()
{
	return (u.u_ndpFlags & NF_EM_TRAPPED) ? 1 : 0;
}


void
wrEmTrapped(n)
int n;
{
	if (n)
		u.u_ndpFlags |= NF_EM_TRAPPED;
	else
		u.u_ndpFlags &= ~NF_EM_TRAPPED;
}


/*
 * Provide the emulator with a fresh context.
 */

void
emFinit (fpsp)
struct _fpemstate * fpsp;
{
	int r;

	memset (fpsp, '\0', sizeof (struct _fpemstate));
	fpsp->cw = ndpCW;
	for(r = 0 ; r < 8 ; r ++)
		fpsp->regs [r].tag = 7;		/* Empty */
}


/*
 * ----------------------------------------------------------------------
 * Functions to interface with the emulator.
 */

int
get_fs_byte(cp)
char *cp;
{
	char getubd();

	return getubd (cp);
}

int
get_fs_word(sp)
short *sp;
{
	short getusd();

	return getusd (sp);
}

int
get_fs_long(lp)
long *lp;
{
	long getuwd();

	return getuwd (lp);
}


void
put_fs_byte(data, cp)
char *cp;
char data;
{
	putubd (cp, data);
}


void
put_fs_word(data, sp)
short *sp;
short data;
{
	putusd (sp, data);
}


void
put_fs_long(data, lp)
long *lp;
long data;
{
	putuwd (lp, data);
}


/*
 * Return zero if out of bounds for write.
 */

int
verify_area(cp, len)
int * cp;
int len;
{
	int ret = useracc(cp, len, 1);

	if (! ret) {
#if 0
		printf("Bad Em write, base=%x, len=%x",
			cp, len);
#endif
		sendsig (SIGSEGV, SELF);
	}
	return ret;
}


/*
 * print kernel message.
 */

void
printk (s)
char *s;
{
	puts (s);
}

void
emSendsig()
{
	sendsig (SIGFPE, SELF);
}