/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
/*
* Copyright (c) 2009, Intel Corporation.
* All rights reserved.
*/
#include <sys/privregs.h>
#include <sys/archsystm.h>
#include <sys/x86_archext.h>
#include <sys/sysmacros.h>
/* Legacy fxsave layout + xsave header + ymm */
/*CSTYLED*/
/*
*/
FPU_CW_INIT, /* fx_fcw */
0, /* fx_fsw */
0, /* fx_fctw */
0, /* fx_fop */
#if defined(__amd64)
0, /* fx_rip */
0, /* fx_rdp */
#else
0, /* fx_eip */
0, /* fx_cs */
0, /* __fx_ign0 */
0, /* fx_dp */
0, /* fx_ds */
0, /* __fx_ign1 */
#endif /* __amd64 */
SSE_MXCSR_INIT /* fx_mxcsr */
/* rest of structure is zero */
};
/*CSTYLED*/
/*
* Initial kfpu state for AVX used by fpinit()
*/
/*
* The definition below needs to be identical with sse_initial
* defined above.
*/
{
FPU_CW_INIT, /* fx_fcw */
0, /* fx_fsw */
0, /* fx_fctw */
0, /* fx_fop */
#if defined(__amd64)
0, /* fx_rip */
0, /* fx_rdp */
#else
0, /* fx_eip */
0, /* fx_cs */
0, /* __fx_ign0 */
0, /* fx_dp */
0, /* fx_ds */
0, /* __fx_ign1 */
#endif /* __amd64 */
SSE_MXCSR_INIT /* fx_mxcsr */
/* rest of structure is zero */
},
/*
* bit0 = 1 for XSTATE_BV to indicate that legacy fields are valid,
*/
1,
{0, 0} /* These 2 bytes must be zero */
/* rest of structure is zero */
};
/*
* mxcsr_mask value (possibly reset in fpu_probe); used to avoid
* the #gp exception caused by setting unsupported bits in the
* MXCSR register
*/
/*
* Initial kfpu state for x87 used by fpinit()
*/
FPU_CW_INIT, /* f_fcw */
0, /* __f_ign0 */
0, /* f_fsw */
0, /* __f_ign1 */
0xffff, /* f_ftw */
/* rest of structure is zero */
};
#if defined(__amd64)
/*
* This vector is patched to xsave_ctxt() if we discover we have an
* XSAVE-capable chip in fpu_probe.
*/
/*
* This vector is patched to fpxsave_ctxt() if we discover we have an
* SSE-capable chip in fpu_probe(). It is patched to xsave_ctxt
* if we discover we have an XSAVE-capable chip in fpu_probe.
*/
#endif
static int fpe_sicode(uint_t);
static int fpe_simd_sicode(uint_t);
/*
* Copy the state of parent lwp's floating point context into the new lwp.
* Invoked for both fork() and lwp_create().
*
* Note that we inherit -only- the control state (e.g. exception masks,
* rounding, precision control, etc.); the FPU registers are otherwise
* reset to their initial state.
*/
static void
{
#if defined(__i386)
#endif
/*
* If the parent FPU state is still in the FPU hw then save it;
* conveniently, fp_save() already does this for us nicely.
*/
switch (fp_save_mech) {
#if defined(__i386)
case FP_FNSAVE:
break;
#endif
case FP_FXSAVE:
break;
case FP_XSAVE:
break;
default:
panic("Invalid fp_save_mech");
/*NOTREACHED*/
}
/*
* Now, when the new lwp starts running, it will take a trap
* that will be handled inline in the trap table to cause
* the appropriate f*rstor instruction to load the save area we
* constructed above directly into the hardware.
*/
}
/*
* Free any state associated with floating point context.
* Fp_free can be called in three cases:
* 1) from reaper -> thread_free -> ctxfree -> fp_free
* fp context belongs to a thread on deathrow
* nothing to do, thread will never be resumed
* thread calling ctxfree is reaper
*
* 2) from exec -> ctxfree -> fp_free
* fp context belongs to the current thread
* must disable fpu, thread calling ctxfree is curthread
*
* 3) from restorecontext -> setfpregs -> fp_free
* we have a modified context in the memory (lwp->pcb_fpu)
* disable fpu and release the fp context for the CPU
*
*/
/*ARGSUSED*/
void
{
return;
/*
* We want to do fpsave rather than fpdisable so that we can
* keep the fpu_flags as FPU_VALID tracking the CR0_TS bit
*/
/* If for current thread disable FP to track FPU_VALID */
/* Clear errors if any to prevent frstor from complaining */
(void) fperr_reset();
(void) fpxerr_reset();
fpdisable();
}
}
/*
* Store the floating point state and disable the floating point unit.
*/
void
{
return;
}
switch (fp_save_mech) {
#if defined(__i386)
case FP_FNSAVE:
break;
#endif
case FP_FXSAVE:
break;
case FP_XSAVE:
break;
default:
panic("Invalid fp_save_mech");
/*NOTREACHED*/
}
}
/*
* Restore the FPU context for the thread:
* The possibilities are:
* 1. No active FPU context: Load the new context into the FPU hw
* and enable the FPU.
*/
void
{
switch (fp_save_mech) {
#if defined(__i386)
case FP_FNSAVE:
break;
#endif
case FP_FXSAVE:
break;
case FP_XSAVE:
break;
default:
panic("Invalid fp_save_mech");
/*NOTREACHED*/
}
}
/*
* Seeds the initial state for the current thread. The possibilities are:
* 1. Another process has modified the FPU state before we have done any
* initialization: Load the FPU state from the LWP state.
* 2. The FPU state has not been externally modified: Load a clean state.
*/
static void
fp_seed(void)
{
/*
* Always initialize a new context and initialize the hardware.
*/
if (fp_save_mech == FP_XSAVE) {
}
fpinit();
/*
* If FPU_VALID is set, it means someone has modified registers via
* /proc. In this case, restore the current lwp's state.
*/
fp_restore(fp);
}
/*
* This routine is called from trap() when User thread takes No Extension
* Fault. The possiblities are:
* 1. User thread has executed a FP instruction for the first time.
* Save current FPU context if any. Initialize FPU, setup FPU
* context for the thread and enable FP hw.
* 2. Thread's pcb has a valid FPU state: Restore the FPU state and
* enable FP hw.
*
* Note that case #2 is inlined in the trap table.
*/
int
{
#if !defined(__lint)
sizeof (struct fnsave_state) == 108);
#if defined(__i386)
#endif /* __i386 */
#endif /* !__lint */
/*
* save area MUST be 16-byte aligned, else will page fault
*/
/*
* Now we can enable the interrupts.
* (NOTE: fp-no-coprocessor comes thru interrupt gate)
*/
sti();
if (!fpu_exists) { /* check for FPU hw exists */
/*
* When the system has no floating point support,
* i.e. no FP hardware and no emulator, skip the
* two kinds of FP instruction that occur in
* fpstart. Allows processes that do no real FP
* to run normally.
*/
return (0);
}
}
/*
* If we have neither a processor extension nor
* an emulator, kill the process OR panic the kernel.
*/
return (1); /* error */
}
#if !defined(__xpv) /* XXPV Is this ifdef needed now? */
/*
* A paranoid cross-check: for the SSE case, ensure that %cr4 is
* this CPU. For the non-SSE case, ensure that it isn't.
*/
#endif
/* case 2 */
fp_restore(fp);
} else {
/* case 1 */
fp_seed();
}
return (0);
}
/*
* Handle a processor extension overrun fault
* Returns non zero for error.
*
* XXX Shouldn't this just be abolished given that we're not supporting
* anything prior to Pentium?
*/
/* ARGSUSED */
int
{
#if !defined(__xpv) /* XXPV Do we need this ifdef either */
fpinit(); /* initialize the FPU hardware */
#endif
sti();
return (1); /* error, send SIGSEGV signal to the thread */
}
/*
* Handle a processor extension error fault
* Returns non zero for error.
*/
/*ARGSUSED*/
int
{
/*
* Now we can enable the interrupts.
* (NOTE: x87 fp exceptions come thru interrupt gate)
*/
sti();
if (!fpu_exists)
return (FPE_FLTINV);
/*
* Do an unconditional save of the FP state. If it's dirty (TS=0),
* it'll be saved into the fpu context area passed in (that of the
* current thread). If it's not dirty (it may not be, due to
* an intervening save due to a context switch between the sti(),
* above and here, then it's safe to just use the stored values in
* the context save area to determine the cause of the fault.
*/
/* clear exception flags in saved state, as if by fnclex */
switch (fp_save_mech) {
#if defined(__i386)
case FP_FNSAVE:
break;
#endif
case FP_FXSAVE:
break;
case FP_XSAVE:
/*
* Always set LEGACY_FP as it may have been cleared by XSAVE
* instruction
*/
break;
default:
panic("Invalid fp_save_mech");
/*NOTREACHED*/
}
return (0); /* No exception */
/*
* "and" the exception flags with the complement of the mask
* bits to determine which exception occurred
*/
}
/*
* Returns a non-zero sicode for error.
*/
/*ARGSUSED*/
int
{
/*
* NOTE: Interrupts are disabled during execution of this
* function. They are enabled by the caller in trap.c.
*/
/*
* The only way we could have gotten here if there is no FP unit
* is via a user executing an INT $19 instruction, so there is
* no fault in that case.
*/
if (!fpu_exists)
return (0);
/*
* Do an unconditional save of the FP state. If it's dirty (TS=0),
* it'll be saved into the fpu context area passed in (that of the
* current thread). If it's not dirty, then it's safe to just use
* the stored values in the context save area to determine the
* cause of the fault.
*/
/*
* compute the mask that determines which conditions can cause
* a #xm exception, and use this to clean the status bits so that
* we can identify the true cause of this one.
*/
}
/*
* In the unlikely event that someone is relying on this subcode being
* FPE_FLTILL for denormalize exceptions, it can always be patched back
* again to restore old behaviour.
*/
/*
* Map from the FPU status word to the FP exception si_code.
*/
static int
{
return (FPE_FLTINV);
return (FPE_FLTDIV);
return (fpe_fltden);
return (FPE_FLTOVF);
return (FPE_FLTUND);
return (FPE_FLTRES);
return (FPE_FLTINV); /* default si_code for other exceptions */
}
/*
* Map from the SSE status word to the FP exception si_code.
*/
static int
{
return (FPE_FLTINV);
return (FPE_FLTDIV);
return (FPE_FLTDEN);
return (FPE_FLTOVF);
return (FPE_FLTUND);
return (FPE_FLTRES);
return (FPE_FLTINV); /* default si_code for other exceptions */
}
/*
* This routine is invoked as part of libc's __fpstart implementation
* via sysi86(2).
*
* It may be called -before- any context has been assigned in which case
* we try and avoid touching the hardware. Or it may be invoked well
* after the context has been assigned and fiddled with, in which case
* just tweak it directly.
*/
void
{
return;
/*
* Common case. Floating point unit not yet
* enabled, and kernel already intends to initialize
* the hardware the way the caller wants.
*/
return;
}
/*
* Hmm. Userland wants a different default.
* Do a fake "first trap" to establish the context, then
* handle as if we already had a context before we came in.
*/
fp_seed();
}
/*
* Ensure that the current hardware state is flushed back to the
* pcb, then modify that copy. Next use of the fp will
* restore the context.
*/
switch (fp_save_mech) {
#if defined(__i386)
case FP_FNSAVE:
break;
#endif
case FP_FXSAVE:
break;
case FP_XSAVE:
/*
* Always set LEGACY_FP as it may have been cleared by XSAVE
* instruction
*/
break;
default:
panic("Invalid fp_save_mech");
/*NOTREACHED*/
}
}