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