xref: /titanic_44/usr/src/uts/i86pc/os/fpu_subr.c (revision fc2613b0a10c787c0f90e9b36f170183746c63f8)
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  * The variable fpu_ignored is provided to allow other code to
52  * determine whether emulation is being done because there is
53  * no FPU or because of an override requested via /etc/system.
54  */
55 int fpu_ignored = 0;
56 
57 /*
58  * Used by ppcopy and ppzero to determine whether or not to use the
59  * SSE-based pagecopy and pagezero routines
60  */
61 int use_sse_pagecopy = 0;
62 int use_sse_pagezero = 0;
63 int use_sse_copy = 0;
64 
65 #if defined(__i386)
66 
67 /*
68  * The variable fpu_pentium_fdivbug is provided to allow other code to
69  * determine whether the system contains a Pentium with the FDIV problem.
70  */
71 int fpu_pentium_fdivbug = 0;
72 
73 #endif
74 
75 #if defined(__xpv)
76 
77 /*
78  * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
79  */
80 
81 #define	ENABLE_SSE()
82 #define	DISABLE_SSE()
83 
84 #else	/* __xpv */
85 
86 #define	ENABLE_SSE()	setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
87 #define	DISABLE_SSE()	setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
88 
89 #endif	/* __xpv */
90 
91 /*
92  * Try and figure out what kind of FP capabilities we have, and
93  * set up the control registers accordingly.
94  */
95 void
96 fpu_probe(void)
97 {
98 	do {
99 		if (fpu_initial_probe() != 0)
100 			continue;
101 
102 		if (fpu_exists == 0) {
103 			fpu_ignored = 1;
104 			continue;
105 		}
106 
107 #if defined(__i386)
108 		fpu_pentium_fdivbug = fpu_probe_pentium_fdivbug();
109 		/*
110 		 * The test does some real floating point operations.
111 		 * Reset it back to previous state.
112 		 */
113 		(void) fpu_initial_probe();
114 
115 		if (fpu_pentium_fdivbug != 0) {
116 			fpu_ignored = 1;
117 			continue;
118 		}
119 #endif
120 
121 #ifndef __xpv
122 		/*
123 		 * Check and see if the fpu is present by looking
124 		 * at the "extension type" bit.  (While this used to
125 		 * indicate a 387DX coprocessor in days gone by,
126 		 * it's forced on by modern implementations for
127 		 * compatibility.)
128 		 */
129 		if ((getcr0() & CR0_ET) == 0)
130 			continue;
131 #endif
132 
133 #if defined(__amd64)
134 		/*
135 		 * SSE and SSE2 are required for the 64-bit ABI.
136 		 *
137 		 * If they're not present, we can in principal run
138 		 * 32-bit userland, though 64-bit processes will be hosed.
139 		 *
140 		 * (Perhaps we should complain more about this case!)
141 		 */
142 		if (is_x86_feature(x86_featureset, X86FSET_SSE) &&
143 		    is_x86_feature(x86_featureset, X86FSET_SSE2)) {
144 			fp_kind = __FP_SSE;
145 			ENABLE_SSE();
146 		}
147 #elif defined(__i386)
148 		/*
149 		 * SSE and SSE2 are both optional, and we patch kernel
150 		 * code to exploit it when present.
151 		 */
152 		if (is_x86_feature(x86_featureset, X86FSET_SSE)) {
153 			fp_kind = __FP_SSE;
154 			fpsave_ctxt = fpxsave_ctxt;
155 			patch_sse();
156 			if (is_x86_feature(x86_featureset, X86FSET_SSE2))
157 				patch_sse2();
158 			ENABLE_SSE();
159 		} else {
160 			remove_x86_feature(x86_featureset, X86FSET_SSE2);
161 			/*
162 			 * (Just in case the BIOS decided we wanted SSE
163 			 * enabled when we didn't. See 4965674.)
164 			 */
165 			DISABLE_SSE();
166 		}
167 #endif
168 		if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
169 			use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
170 		}
171 
172 		if (fp_kind == __FP_SSE) {
173 			struct fxsave_state *fx;
174 			uint8_t fxsave_state[sizeof (struct fxsave_state) +
175 			    XMM_ALIGN];
176 
177 			/*
178 			 * Extract the mxcsr mask from our first fxsave
179 			 */
180 			fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
181 			    XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
182 
183 			fx->fx_mxcsr_mask = 0;
184 			fxsave_insn(fx);
185 			if (fx->fx_mxcsr_mask != 0) {
186 				/*
187 				 * Override default mask initialized in fpu.c
188 				 */
189 				sse_mxcsr_mask = fx->fx_mxcsr_mask;
190 			}
191 		}
192 
193 		setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
194 		return;
195 		/*CONSTANTCONDITION*/
196 	} while (0);
197 
198 	/*
199 	 * No FPU hardware present
200 	 */
201 	setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
202 	DISABLE_SSE();
203 	fp_kind = FP_NO;
204 	fpu_exists = 0;
205 }
206