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