xref: /titanic_50/usr/src/uts/i86pc/os/fpu_subr.c (revision 694c35faa87b858ecdadfe4fc592615f4eefbb07)
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
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