xref: /titanic_51/usr/src/uts/i86pc/os/fpu_subr.c (revision dfb96a4f56fb431b915bc67e5d9d5c8d4f4f6679)
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 
79 #define	ENABLE_SSE()	setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
80 #define	DISABLE_SSE()	setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
81 
82 /*
83  * Try and figure out what kind of FP capabilities we have, and
84  * set up the control registers accordingly.
85  */
86 void
87 fpu_probe(void)
88 {
89 	do {
90 		if (fpu_initial_probe() != 0)
91 			continue;
92 
93 		if (fpu_exists == 0) {
94 			fpu_ignored = 1;
95 			continue;
96 		}
97 
98 #if defined(__i386)
99 		fpu_pentium_fdivbug = fpu_probe_pentium_fdivbug();
100 		/*
101 		 * The test does some real floating point operations.
102 		 * Reset it back to previous state.
103 		 */
104 		(void) fpu_initial_probe();
105 
106 		if (fpu_pentium_fdivbug != 0) {
107 			fpu_ignored = 1;
108 			continue;
109 		}
110 #endif
111 
112 		/*
113 		 * Check and see if the fpu is present by looking
114 		 * at the "extension type" bit.  (While this used to
115 		 * indicate a 387DX coprocessor in days gone by,
116 		 * it's forced on by modern implementations for
117 		 * compatibility.)
118 		 */
119 		if ((getcr0() & CR0_ET) == 0)
120 			continue;
121 
122 #if defined(__amd64)
123 		/*
124 		 * SSE and SSE2 are required for the 64-bit ABI.
125 		 *
126 		 * If they're not present, we can in principal run
127 		 * 32-bit userland, though 64-bit processes will be hosed.
128 		 *
129 		 * (Perhaps we should complain more about this case!)
130 		 */
131 		if ((x86_feature & X86_SSE|X86_SSE2) == (X86_SSE|X86_SSE2)) {
132 			fp_kind = __FP_SSE;
133 			ENABLE_SSE();
134 		}
135 #elif defined(__i386)
136 		/*
137 		 * SSE and SSE2 are both optional, and we patch kernel
138 		 * code to exploit it when present.
139 		 */
140 		if (x86_feature & X86_SSE) {
141 			fp_kind = __FP_SSE;
142 			fpsave_ctxt = fpxsave_ctxt;
143 			patch_sse();
144 			if (x86_feature & X86_SSE2)
145 				patch_sse2();
146 			ENABLE_SSE();
147 		} else {
148 			x86_feature &= ~X86_SSE2;
149 			/*
150 			 * (Just in case the BIOS decided we wanted SSE
151 			 * enabled when we didn't. See 4965674.)
152 			 */
153 			DISABLE_SSE();
154 		}
155 #endif
156 		if (x86_feature & X86_SSE2) {
157 			use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
158 		}
159 
160 		if (fp_kind == __FP_SSE) {
161 			struct fxsave_state *fx;
162 			uint8_t fxsave_state[sizeof (struct fxsave_state) +
163 			    XMM_ALIGN];
164 
165 			/*
166 			 * Extract the mxcsr mask from our first fxsave
167 			 */
168 			fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
169 			    XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
170 
171 			fx->fx_mxcsr_mask = 0;
172 			fxsave_insn(fx);
173 			if (fx->fx_mxcsr_mask != 0) {
174 				/*
175 				 * Override default mask initialized in fpu.c
176 				 */
177 				sse_mxcsr_mask = fx->fx_mxcsr_mask;
178 			}
179 		}
180 
181 		setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
182 		return;
183 		/*CONSTANTCONDITION*/
184 	} while (0);
185 
186 	/*
187 	 * No FPU hardware present
188 	 */
189 	setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
190 	DISABLE_SSE();
191 	fp_kind = FP_NO;
192 	fpu_exists = 0;
193 }
194