fpu.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* 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 */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/privregs.h>
#include <sys/archsystm.h>
#include <sys/x86_archext.h>
#include <sys/sysmacros.h>
/*CSTYLED*/
/*
*/
const struct fxsave_state sse_initial = {
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 */
};
/*
* 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()
*/
const struct fnsave_state x87_initial = {
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)
#define fpsave_begin fpxsave_begin
/*
* This vector is patched to fpxsave_begin() if we discover
* we have an SSE-capable chip in fpu_probe().
*/
void (*fpsave_begin)(void *) = fpnsave_begin;
#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 the parent FPU state is still in the FPU hw then save it;
* conveniently, fp_save() already does this for us nicely.
*/
#if defined(__amd64)
#else
} else {
}
#endif
/*
* 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;
}
#if defined(__amd64)
#else
switch (fp_kind) {
case __FP_SSE:
break;
default:
break;
}
#endif
}
/*
* 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
{
#if defined(__amd64)
#else
/* case 2 */
else
#endif
}
/*
* 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.
*/
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 */
}
/*
* A paranoid cross-check: for the SSE case, ensure that %cr4 is
* this CPU. For the non-SSE case, ensure that it isn't.
*/
/* case 2 */
fp_restore(fp);
} else {
/* case 1 */
fp_seed();
}
return (0);
}
/*
* Handle a processor extension overrun fault
* Returns non zero for error.
*/
/* ARGSUSED */
int
{
fpinit(); /* initialize the FPU hardware */
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();
return (0); /* No exception */
if (fpu_exists) {
/* clear exception flags in saved state, as if by fnclex */
#if defined(__amd64)
#else
switch (fp_kind) {
case __FP_SSE:
break;
default:
break;
}
#endif
}
/*
* "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
{
mxcsr = fpgetmxcsr();
if (fpu_exists) {
} else {
}
/*
* 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.
*/
int fpe_fltden = FPE_FLTDEN;
/*
* 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
{
struct fxsave_state *fx;
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.
*/
#if defined(__amd64)
#else
switch (fp_kind) {
case __FP_SSE:
break;
default:
break;
}
#endif
}