1ae115bc7Smrj /*
2ae115bc7Smrj * CDDL HEADER START
3ae115bc7Smrj *
4ae115bc7Smrj * The contents of this file are subject to the terms of the
5ae115bc7Smrj * Common Development and Distribution License (the "License").
6ae115bc7Smrj * You may not use this file except in compliance with the License.
7ae115bc7Smrj *
8ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj * See the License for the specific language governing permissions
11ae115bc7Smrj * and limitations under the License.
12ae115bc7Smrj *
13ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj *
19ae115bc7Smrj * CDDL HEADER END
20ae115bc7Smrj */
21ae115bc7Smrj
22ae115bc7Smrj /*
237417cfdeSKuriakose Kuruvilla * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24ae115bc7Smrj */
25ae115bc7Smrj
26ae115bc7Smrj /*
27ae115bc7Smrj * Floating point configuration.
28ae115bc7Smrj */
29ae115bc7Smrj
30ae115bc7Smrj #include <sys/types.h>
31ae115bc7Smrj #include <sys/regset.h>
32ae115bc7Smrj #include <sys/privregs.h>
33ae115bc7Smrj #include <sys/x86_archext.h>
34ae115bc7Smrj #include <sys/archsystm.h>
35ae115bc7Smrj #include <sys/fp.h>
36ae115bc7Smrj #include <sys/cmn_err.h>
37ae115bc7Smrj
38ae115bc7Smrj #define XMM_ALIGN 16
39ae115bc7Smrj
40ae115bc7Smrj /*
41ae115bc7Smrj * If fpu_exists is non-zero, fpu_probe will attempt to use any
42ae115bc7Smrj * hardware FPU (subject to other constraints, see below). If
43ae115bc7Smrj * fpu_exists is zero, fpu_probe will report that there is no
44ae115bc7Smrj * FPU even if there is one.
45ae115bc7Smrj */
46ae115bc7Smrj int fpu_exists = 1;
47ae115bc7Smrj
48ae115bc7Smrj int fp_kind = FP_387;
49ae115bc7Smrj
50ae115bc7Smrj /*
51*7af88ac7SKuriakose Kuruvilla * Mechanism to save FPU state.
52*7af88ac7SKuriakose Kuruvilla */
53*7af88ac7SKuriakose Kuruvilla #if defined(__amd64)
54*7af88ac7SKuriakose Kuruvilla int fp_save_mech = FP_FXSAVE;
55*7af88ac7SKuriakose Kuruvilla #elif defined(__i386)
56*7af88ac7SKuriakose Kuruvilla int fp_save_mech = FP_FNSAVE;
57*7af88ac7SKuriakose Kuruvilla #endif
58*7af88ac7SKuriakose Kuruvilla
59*7af88ac7SKuriakose Kuruvilla /*
60ae115bc7Smrj * The variable fpu_ignored is provided to allow other code to
61ae115bc7Smrj * determine whether emulation is being done because there is
62ae115bc7Smrj * no FPU or because of an override requested via /etc/system.
63ae115bc7Smrj */
64ae115bc7Smrj int fpu_ignored = 0;
65ae115bc7Smrj
66ae115bc7Smrj /*
67ae115bc7Smrj * Used by ppcopy and ppzero to determine whether or not to use the
68ae115bc7Smrj * SSE-based pagecopy and pagezero routines
69ae115bc7Smrj */
70ae115bc7Smrj int use_sse_pagecopy = 0;
71ae115bc7Smrj int use_sse_pagezero = 0;
72ae115bc7Smrj int use_sse_copy = 0;
73ae115bc7Smrj
74ae115bc7Smrj #if defined(__i386)
75ae115bc7Smrj
76ae115bc7Smrj /*
77ae115bc7Smrj * The variable fpu_pentium_fdivbug is provided to allow other code to
78ae115bc7Smrj * determine whether the system contains a Pentium with the FDIV problem.
79ae115bc7Smrj */
80ae115bc7Smrj int fpu_pentium_fdivbug = 0;
81ae115bc7Smrj
82ae115bc7Smrj #endif
83ae115bc7Smrj
84843e1988Sjohnlev #if defined(__xpv)
85843e1988Sjohnlev
86843e1988Sjohnlev /*
87843e1988Sjohnlev * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
88843e1988Sjohnlev */
89843e1988Sjohnlev
90843e1988Sjohnlev #define ENABLE_SSE()
91843e1988Sjohnlev #define DISABLE_SSE()
92843e1988Sjohnlev
93843e1988Sjohnlev #else /* __xpv */
94ae115bc7Smrj
95ae115bc7Smrj #define ENABLE_SSE() setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
96ae115bc7Smrj #define DISABLE_SSE() setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
97ae115bc7Smrj
98843e1988Sjohnlev #endif /* __xpv */
99843e1988Sjohnlev
100ae115bc7Smrj /*
101ae115bc7Smrj * Try and figure out what kind of FP capabilities we have, and
102ae115bc7Smrj * set up the control registers accordingly.
103ae115bc7Smrj */
104ae115bc7Smrj void
fpu_probe(void)105ae115bc7Smrj fpu_probe(void)
106ae115bc7Smrj {
107ae115bc7Smrj do {
108ae115bc7Smrj if (fpu_initial_probe() != 0)
109ae115bc7Smrj continue;
110ae115bc7Smrj
111ae115bc7Smrj if (fpu_exists == 0) {
112ae115bc7Smrj fpu_ignored = 1;
113ae115bc7Smrj continue;
114ae115bc7Smrj }
115ae115bc7Smrj
116ae115bc7Smrj #if defined(__i386)
117ae115bc7Smrj fpu_pentium_fdivbug = fpu_probe_pentium_fdivbug();
118ae115bc7Smrj /*
119ae115bc7Smrj * The test does some real floating point operations.
120ae115bc7Smrj * Reset it back to previous state.
121ae115bc7Smrj */
122ae115bc7Smrj (void) fpu_initial_probe();
123ae115bc7Smrj
124ae115bc7Smrj if (fpu_pentium_fdivbug != 0) {
125ae115bc7Smrj fpu_ignored = 1;
126ae115bc7Smrj continue;
127ae115bc7Smrj }
128ae115bc7Smrj #endif
129ae115bc7Smrj
130843e1988Sjohnlev #ifndef __xpv
131ae115bc7Smrj /*
132ae115bc7Smrj * Check and see if the fpu is present by looking
133ae115bc7Smrj * at the "extension type" bit. (While this used to
134ae115bc7Smrj * indicate a 387DX coprocessor in days gone by,
135ae115bc7Smrj * it's forced on by modern implementations for
136ae115bc7Smrj * compatibility.)
137ae115bc7Smrj */
138ae115bc7Smrj if ((getcr0() & CR0_ET) == 0)
139ae115bc7Smrj continue;
140843e1988Sjohnlev #endif
141ae115bc7Smrj
142ae115bc7Smrj #if defined(__amd64)
143ae115bc7Smrj /*
144ae115bc7Smrj * SSE and SSE2 are required for the 64-bit ABI.
145ae115bc7Smrj *
146ae115bc7Smrj * If they're not present, we can in principal run
147ae115bc7Smrj * 32-bit userland, though 64-bit processes will be hosed.
148ae115bc7Smrj *
149ae115bc7Smrj * (Perhaps we should complain more about this case!)
150ae115bc7Smrj */
1517417cfdeSKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_SSE) &&
1527417cfdeSKuriakose Kuruvilla is_x86_feature(x86_featureset, X86FSET_SSE2)) {
153*7af88ac7SKuriakose Kuruvilla fp_kind |= __FP_SSE;
154ae115bc7Smrj ENABLE_SSE();
155*7af88ac7SKuriakose Kuruvilla
156*7af88ac7SKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
157*7af88ac7SKuriakose Kuruvilla ASSERT(is_x86_feature(x86_featureset,
158*7af88ac7SKuriakose Kuruvilla X86FSET_XSAVE));
159*7af88ac7SKuriakose Kuruvilla fp_kind |= __FP_AVX;
160*7af88ac7SKuriakose Kuruvilla }
161*7af88ac7SKuriakose Kuruvilla
162*7af88ac7SKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
163*7af88ac7SKuriakose Kuruvilla fp_save_mech = FP_XSAVE;
164*7af88ac7SKuriakose Kuruvilla fpsave_ctxt = xsave_ctxt;
165*7af88ac7SKuriakose Kuruvilla patch_xsave();
166*7af88ac7SKuriakose Kuruvilla }
167ae115bc7Smrj }
168ae115bc7Smrj #elif defined(__i386)
169ae115bc7Smrj /*
170ae115bc7Smrj * SSE and SSE2 are both optional, and we patch kernel
171ae115bc7Smrj * code to exploit it when present.
172ae115bc7Smrj */
1737417cfdeSKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_SSE)) {
174*7af88ac7SKuriakose Kuruvilla fp_kind |= __FP_SSE;
175ae115bc7Smrj ENABLE_SSE();
176*7af88ac7SKuriakose Kuruvilla fp_save_mech = FP_FXSAVE;
177*7af88ac7SKuriakose Kuruvilla fpsave_ctxt = fpxsave_ctxt;
178*7af88ac7SKuriakose Kuruvilla
179*7af88ac7SKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
180*7af88ac7SKuriakose Kuruvilla patch_sse2();
181*7af88ac7SKuriakose Kuruvilla }
182*7af88ac7SKuriakose Kuruvilla
183*7af88ac7SKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
184*7af88ac7SKuriakose Kuruvilla ASSERT(is_x86_feature(x86_featureset,
185*7af88ac7SKuriakose Kuruvilla X86FSET_XSAVE));
186*7af88ac7SKuriakose Kuruvilla fp_kind |= __FP_AVX;
187*7af88ac7SKuriakose Kuruvilla }
188*7af88ac7SKuriakose Kuruvilla
189*7af88ac7SKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
190*7af88ac7SKuriakose Kuruvilla fp_save_mech = FP_XSAVE;
191*7af88ac7SKuriakose Kuruvilla fpsave_ctxt = xsave_ctxt;
192*7af88ac7SKuriakose Kuruvilla patch_xsave();
193*7af88ac7SKuriakose Kuruvilla } else {
194*7af88ac7SKuriakose Kuruvilla patch_sse(); /* use fxrstor */
195*7af88ac7SKuriakose Kuruvilla }
196ae115bc7Smrj } else {
1977417cfdeSKuriakose Kuruvilla remove_x86_feature(x86_featureset, X86FSET_SSE2);
198ae115bc7Smrj /*
199*7af88ac7SKuriakose Kuruvilla * We will not likely to have a chip with AVX but not
200*7af88ac7SKuriakose Kuruvilla * SSE. But to be safe we disable AVX if SSE is not
201*7af88ac7SKuriakose Kuruvilla * enabled.
202*7af88ac7SKuriakose Kuruvilla */
203*7af88ac7SKuriakose Kuruvilla remove_x86_feature(x86_featureset, X86FSET_AVX);
204*7af88ac7SKuriakose Kuruvilla /*
205ae115bc7Smrj * (Just in case the BIOS decided we wanted SSE
206ae115bc7Smrj * enabled when we didn't. See 4965674.)
207ae115bc7Smrj */
208ae115bc7Smrj DISABLE_SSE();
209ae115bc7Smrj }
210ae115bc7Smrj #endif
2117417cfdeSKuriakose Kuruvilla if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
212ae115bc7Smrj use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
213ae115bc7Smrj }
214ae115bc7Smrj
215*7af88ac7SKuriakose Kuruvilla if (fp_kind & __FP_SSE) {
216ae115bc7Smrj struct fxsave_state *fx;
217ae115bc7Smrj uint8_t fxsave_state[sizeof (struct fxsave_state) +
218ae115bc7Smrj XMM_ALIGN];
219ae115bc7Smrj
220ae115bc7Smrj /*
221ae115bc7Smrj * Extract the mxcsr mask from our first fxsave
222ae115bc7Smrj */
223ae115bc7Smrj fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
224ae115bc7Smrj XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
225ae115bc7Smrj
226ae115bc7Smrj fx->fx_mxcsr_mask = 0;
227ae115bc7Smrj fxsave_insn(fx);
228ae115bc7Smrj if (fx->fx_mxcsr_mask != 0) {
229ae115bc7Smrj /*
230ae115bc7Smrj * Override default mask initialized in fpu.c
231ae115bc7Smrj */
232ae115bc7Smrj sse_mxcsr_mask = fx->fx_mxcsr_mask;
233ae115bc7Smrj }
234ae115bc7Smrj }
235ae115bc7Smrj
236ae115bc7Smrj setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
237ae115bc7Smrj return;
238ae115bc7Smrj /*CONSTANTCONDITION*/
239ae115bc7Smrj } while (0);
240ae115bc7Smrj
241ae115bc7Smrj /*
242ae115bc7Smrj * No FPU hardware present
243ae115bc7Smrj */
244ae115bc7Smrj setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
245ae115bc7Smrj DISABLE_SSE();
246ae115bc7Smrj fp_kind = FP_NO;
247ae115bc7Smrj fpu_exists = 0;
248ae115bc7Smrj }
249