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