/* $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); }