xref: /onnv-gate/usr/src/uts/i86pc/os/fpu_subr.c (revision 13134:8315ff49e22e)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj 
223446Smrj /*
2312826Skuriakose.kuruvilla@oracle.com  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
243446Smrj  */
253446Smrj 
263446Smrj /*
273446Smrj  * Floating point configuration.
283446Smrj  */
293446Smrj 
303446Smrj #include <sys/types.h>
313446Smrj #include <sys/regset.h>
323446Smrj #include <sys/privregs.h>
333446Smrj #include <sys/x86_archext.h>
343446Smrj #include <sys/archsystm.h>
353446Smrj #include <sys/fp.h>
363446Smrj #include <sys/cmn_err.h>
373446Smrj 
383446Smrj #define	XMM_ALIGN	16
393446Smrj 
403446Smrj /*
413446Smrj  * If fpu_exists is non-zero, fpu_probe will attempt to use any
423446Smrj  * hardware FPU (subject to other constraints, see below).  If
433446Smrj  * fpu_exists is zero, fpu_probe will report that there is no
443446Smrj  * FPU even if there is one.
453446Smrj  */
463446Smrj int fpu_exists = 1;
473446Smrj 
483446Smrj int fp_kind = FP_387;
493446Smrj 
503446Smrj /*
51*13134Skuriakose.kuruvilla@oracle.com  * Mechanism to save FPU state.
52*13134Skuriakose.kuruvilla@oracle.com  */
53*13134Skuriakose.kuruvilla@oracle.com #if defined(__amd64)
54*13134Skuriakose.kuruvilla@oracle.com int fp_save_mech = FP_FXSAVE;
55*13134Skuriakose.kuruvilla@oracle.com #elif defined(__i386)
56*13134Skuriakose.kuruvilla@oracle.com int fp_save_mech = FP_FNSAVE;
57*13134Skuriakose.kuruvilla@oracle.com #endif
58*13134Skuriakose.kuruvilla@oracle.com 
59*13134Skuriakose.kuruvilla@oracle.com /*
603446Smrj  * The variable fpu_ignored is provided to allow other code to
613446Smrj  * determine whether emulation is being done because there is
623446Smrj  * no FPU or because of an override requested via /etc/system.
633446Smrj  */
643446Smrj int fpu_ignored = 0;
653446Smrj 
663446Smrj /*
673446Smrj  * Used by ppcopy and ppzero to determine whether or not to use the
683446Smrj  * SSE-based pagecopy and pagezero routines
693446Smrj  */
703446Smrj int use_sse_pagecopy = 0;
713446Smrj int use_sse_pagezero = 0;
723446Smrj int use_sse_copy = 0;
733446Smrj 
743446Smrj #if defined(__i386)
753446Smrj 
763446Smrj /*
773446Smrj  * The variable fpu_pentium_fdivbug is provided to allow other code to
783446Smrj  * determine whether the system contains a Pentium with the FDIV problem.
793446Smrj  */
803446Smrj int fpu_pentium_fdivbug = 0;
813446Smrj 
823446Smrj #endif
833446Smrj 
845084Sjohnlev #if defined(__xpv)
855084Sjohnlev 
865084Sjohnlev /*
875084Sjohnlev  * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
885084Sjohnlev  */
895084Sjohnlev 
905084Sjohnlev #define	ENABLE_SSE()
915084Sjohnlev #define	DISABLE_SSE()
925084Sjohnlev 
935084Sjohnlev #else	/* __xpv */
943446Smrj 
953446Smrj #define	ENABLE_SSE()	setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
963446Smrj #define	DISABLE_SSE()	setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
973446Smrj 
985084Sjohnlev #endif	/* __xpv */
995084Sjohnlev 
1003446Smrj /*
1013446Smrj  * Try and figure out what kind of FP capabilities we have, and
1023446Smrj  * set up the control registers accordingly.
1033446Smrj  */
1043446Smrj void
fpu_probe(void)1053446Smrj fpu_probe(void)
1063446Smrj {
1073446Smrj 	do {
1083446Smrj 		if (fpu_initial_probe() != 0)
1093446Smrj 			continue;
1103446Smrj 
1113446Smrj 		if (fpu_exists == 0) {
1123446Smrj 			fpu_ignored = 1;
1133446Smrj 			continue;
1143446Smrj 		}
1153446Smrj 
1163446Smrj #if defined(__i386)
1173446Smrj 		fpu_pentium_fdivbug = fpu_probe_pentium_fdivbug();
1183446Smrj 		/*
1193446Smrj 		 * The test does some real floating point operations.
1203446Smrj 		 * Reset it back to previous state.
1213446Smrj 		 */
1223446Smrj 		(void) fpu_initial_probe();
1233446Smrj 
1243446Smrj 		if (fpu_pentium_fdivbug != 0) {
1253446Smrj 			fpu_ignored = 1;
1263446Smrj 			continue;
1273446Smrj 		}
1283446Smrj #endif
1293446Smrj 
1305084Sjohnlev #ifndef __xpv
1313446Smrj 		/*
1323446Smrj 		 * Check and see if the fpu is present by looking
1333446Smrj 		 * at the "extension type" bit.  (While this used to
1343446Smrj 		 * indicate a 387DX coprocessor in days gone by,
1353446Smrj 		 * it's forced on by modern implementations for
1363446Smrj 		 * compatibility.)
1373446Smrj 		 */
1383446Smrj 		if ((getcr0() & CR0_ET) == 0)
1393446Smrj 			continue;
1405084Sjohnlev #endif
1413446Smrj 
1423446Smrj #if defined(__amd64)
1433446Smrj 		/*
1443446Smrj 		 * SSE and SSE2 are required for the 64-bit ABI.
1453446Smrj 		 *
1463446Smrj 		 * If they're not present, we can in principal run
1473446Smrj 		 * 32-bit userland, though 64-bit processes will be hosed.
1483446Smrj 		 *
1493446Smrj 		 * (Perhaps we should complain more about this case!)
1503446Smrj 		 */
15112826Skuriakose.kuruvilla@oracle.com 		if (is_x86_feature(x86_featureset, X86FSET_SSE) &&
15212826Skuriakose.kuruvilla@oracle.com 		    is_x86_feature(x86_featureset, X86FSET_SSE2)) {
153*13134Skuriakose.kuruvilla@oracle.com 			fp_kind |= __FP_SSE;
1543446Smrj 			ENABLE_SSE();
155*13134Skuriakose.kuruvilla@oracle.com 
156*13134Skuriakose.kuruvilla@oracle.com 			if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
157*13134Skuriakose.kuruvilla@oracle.com 				ASSERT(is_x86_feature(x86_featureset,
158*13134Skuriakose.kuruvilla@oracle.com 				    X86FSET_XSAVE));
159*13134Skuriakose.kuruvilla@oracle.com 				fp_kind |= __FP_AVX;
160*13134Skuriakose.kuruvilla@oracle.com 			}
161*13134Skuriakose.kuruvilla@oracle.com 
162*13134Skuriakose.kuruvilla@oracle.com 			if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
163*13134Skuriakose.kuruvilla@oracle.com 				fp_save_mech = FP_XSAVE;
164*13134Skuriakose.kuruvilla@oracle.com 				fpsave_ctxt = xsave_ctxt;
165*13134Skuriakose.kuruvilla@oracle.com 				patch_xsave();
166*13134Skuriakose.kuruvilla@oracle.com 			}
1673446Smrj 		}
1683446Smrj #elif defined(__i386)
1693446Smrj 		/*
1703446Smrj 		 * SSE and SSE2 are both optional, and we patch kernel
1713446Smrj 		 * code to exploit it when present.
1723446Smrj 		 */
17312826Skuriakose.kuruvilla@oracle.com 		if (is_x86_feature(x86_featureset, X86FSET_SSE)) {
174*13134Skuriakose.kuruvilla@oracle.com 			fp_kind |= __FP_SSE;
175*13134Skuriakose.kuruvilla@oracle.com 			ENABLE_SSE();
176*13134Skuriakose.kuruvilla@oracle.com 			fp_save_mech = FP_FXSAVE;
1773446Smrj 			fpsave_ctxt = fpxsave_ctxt;
178*13134Skuriakose.kuruvilla@oracle.com 
179*13134Skuriakose.kuruvilla@oracle.com 			if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
1803446Smrj 				patch_sse2();
181*13134Skuriakose.kuruvilla@oracle.com 			}
182*13134Skuriakose.kuruvilla@oracle.com 
183*13134Skuriakose.kuruvilla@oracle.com 			if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
184*13134Skuriakose.kuruvilla@oracle.com 				ASSERT(is_x86_feature(x86_featureset,
185*13134Skuriakose.kuruvilla@oracle.com 				    X86FSET_XSAVE));
186*13134Skuriakose.kuruvilla@oracle.com 				fp_kind |= __FP_AVX;
187*13134Skuriakose.kuruvilla@oracle.com 			}
188*13134Skuriakose.kuruvilla@oracle.com 
189*13134Skuriakose.kuruvilla@oracle.com 			if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
190*13134Skuriakose.kuruvilla@oracle.com 				fp_save_mech = FP_XSAVE;
191*13134Skuriakose.kuruvilla@oracle.com 				fpsave_ctxt = xsave_ctxt;
192*13134Skuriakose.kuruvilla@oracle.com 				patch_xsave();
193*13134Skuriakose.kuruvilla@oracle.com 			} else {
194*13134Skuriakose.kuruvilla@oracle.com 				patch_sse();	/* use fxrstor */
195*13134Skuriakose.kuruvilla@oracle.com 			}
1963446Smrj 		} else {
19712826Skuriakose.kuruvilla@oracle.com 			remove_x86_feature(x86_featureset, X86FSET_SSE2);
1983446Smrj 			/*
199*13134Skuriakose.kuruvilla@oracle.com 			 * We will not likely to have a chip with AVX but not
200*13134Skuriakose.kuruvilla@oracle.com 			 * SSE. But to be safe we disable AVX if SSE is not
201*13134Skuriakose.kuruvilla@oracle.com 			 * enabled.
202*13134Skuriakose.kuruvilla@oracle.com 			 */
203*13134Skuriakose.kuruvilla@oracle.com 			remove_x86_feature(x86_featureset, X86FSET_AVX);
204*13134Skuriakose.kuruvilla@oracle.com 			/*
2053446Smrj 			 * (Just in case the BIOS decided we wanted SSE
2063446Smrj 			 * enabled when we didn't. See 4965674.)
2073446Smrj 			 */
2083446Smrj 			DISABLE_SSE();
2093446Smrj 		}
2103446Smrj #endif
21112826Skuriakose.kuruvilla@oracle.com 		if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
2123446Smrj 			use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
2133446Smrj 		}
2143446Smrj 
215*13134Skuriakose.kuruvilla@oracle.com 		if (fp_kind & __FP_SSE) {
2163446Smrj 			struct fxsave_state *fx;
2173446Smrj 			uint8_t fxsave_state[sizeof (struct fxsave_state) +
2183446Smrj 			    XMM_ALIGN];
2193446Smrj 
2203446Smrj 			/*
2213446Smrj 			 * Extract the mxcsr mask from our first fxsave
2223446Smrj 			 */
2233446Smrj 			fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
2243446Smrj 			    XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
2253446Smrj 
2263446Smrj 			fx->fx_mxcsr_mask = 0;
2273446Smrj 			fxsave_insn(fx);
2283446Smrj 			if (fx->fx_mxcsr_mask != 0) {
2293446Smrj 				/*
2303446Smrj 				 * Override default mask initialized in fpu.c
2313446Smrj 				 */
2323446Smrj 				sse_mxcsr_mask = fx->fx_mxcsr_mask;
2333446Smrj 			}
2343446Smrj 		}
2353446Smrj 
2363446Smrj 		setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
2373446Smrj 		return;
2383446Smrj 		/*CONSTANTCONDITION*/
2393446Smrj 	} while (0);
2403446Smrj 
2413446Smrj 	/*
2423446Smrj 	 * No FPU hardware present
2433446Smrj 	 */
2443446Smrj 	setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
2453446Smrj 	DISABLE_SSE();
2463446Smrj 	fp_kind = FP_NO;
2473446Smrj 	fpu_exists = 0;
2483446Smrj }
249