xref: /illumos-gate/usr/src/uts/intel/os/fpu_subr.c (revision 75840da35ecec00345f7f5f5d85a1f19fae4bd26)
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  * Copyright (c) 2018, Joyent, Inc.
25  */
26 
27 /*
28  * Floating point configuration.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/regset.h>
33 #include <sys/privregs.h>
34 #include <sys/x86_archext.h>
35 #include <sys/archsystm.h>
36 #include <sys/fp.h>
37 #include <sys/cmn_err.h>
38 #include <sys/exec.h>
39 
40 #define	XMM_ALIGN	16
41 
42 /*
43  * If fpu_exists is non-zero, fpu_probe will attempt to use any
44  * hardware FPU (subject to other constraints, see below).  If
45  * fpu_exists is zero, fpu_probe will report that there is no
46  * FPU even if there is one.
47  */
48 int fpu_exists = 1;
49 
50 int fp_kind = FP_387;
51 
52 /*
53  * The variable fpu_ignored is provided to allow other code to
54  * determine whether emulation is being done because there is
55  * no FPU or because of an override requested via /etc/system.
56  */
57 int fpu_ignored = 0;
58 
59 /*
60  * Used by ppcopy and ppzero to determine whether or not to use the
61  * SSE-based pagecopy and pagezero routines
62  */
63 int use_sse_pagecopy = 0;
64 int use_sse_pagezero = 0;
65 int use_sse_copy = 0;
66 
67 #if defined(__xpv)
68 
69 /*
70  * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
71  */
72 
73 #define	ENABLE_SSE()
74 #define	DISABLE_SSE()
75 
76 #else	/* __xpv */
77 
78 #define	ENABLE_SSE()	setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
79 #define	DISABLE_SSE()	setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
80 
81 #endif	/* __xpv */
82 
83 /*
84  * Try and figure out what kind of FP capabilities we have, and
85  * set up the control registers accordingly.
86  */
87 void
fpu_probe(void)88 fpu_probe(void)
89 {
90 	if (fpu_initial_probe() != 0)
91 		goto nofpu;
92 
93 	if (fpu_exists == 0) {
94 		fpu_ignored = 1;
95 		goto nofpu;
96 	}
97 
98 #ifndef __xpv
99 	/*
100 	 * Check and see if the fpu is present by looking
101 	 * at the "extension type" bit.  (While this used to
102 	 * indicate a 387DX coprocessor in days gone by,
103 	 * it's forced on by modern implementations for
104 	 * compatibility.)
105 	 */
106 	if ((getcr0() & CR0_ET) == 0)
107 		goto nofpu;
108 #endif
109 
110 	/* Use the more complex exception clearing code if necessary */
111 	if (cpuid_need_fp_excp_handling())
112 		fpsave_ctxt = fpxsave_excp_clr_ctxt;
113 
114 	/*
115 	 * SSE and SSE2 are required for the 64-bit ABI.
116 	 *
117 	 * If they're not present, we can in principal run
118 	 * 32-bit userland, though 64-bit processes will be hosed.
119 	 *
120 	 * (Perhaps we should complain more about this case!)
121 	 */
122 	if (is_x86_feature(x86_featureset, X86FSET_SSE) &&
123 	    is_x86_feature(x86_featureset, X86FSET_SSE2)) {
124 		fp_kind |= __FP_SSE;
125 		ENABLE_SSE();
126 
127 		if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
128 			ASSERT(is_x86_feature(x86_featureset, X86FSET_XSAVE));
129 			fp_kind |= __FP_AVX;
130 		}
131 
132 		if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
133 			fp_save_mech = FP_XSAVE;
134 			fp_elf = AT_386_FPINFO_XSAVE;
135 			if (is_x86_feature(x86_featureset, X86FSET_XSAVEOPT)) {
136 				/*
137 				 * Use the more complex exception
138 				 * clearing code if necessary.
139 				 */
140 				if (cpuid_need_fp_excp_handling()) {
141 					fpsave_ctxt = xsaveopt_excp_clr_ctxt;
142 					fp_elf = AT_386_FPINFO_XSAVE_AMD;
143 				} else {
144 					fpsave_ctxt = xsaveopt_ctxt;
145 				}
146 				xsavep = xsaveopt;
147 			} else {
148 				/*
149 				 * Use the more complex exception
150 				 * clearing code if necessary.
151 				 */
152 				if (cpuid_need_fp_excp_handling()) {
153 					fpsave_ctxt = xsave_excp_clr_ctxt;
154 					fp_elf = AT_386_FPINFO_XSAVE_AMD;
155 				} else {
156 					fpsave_ctxt = xsave_ctxt;
157 				}
158 			}
159 			fprestore_ctxt = xrestore_ctxt;
160 		} else {
161 			/* fp_save_mech defaults to FP_FXSAVE */
162 			fp_elf = AT_386_FPINFO_FXSAVE;
163 		}
164 	}
165 
166 	if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
167 		use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
168 	}
169 
170 	if (fp_kind & __FP_SSE) {
171 		struct fxsave_state *fx;
172 		uint8_t fxsave_state[sizeof (struct fxsave_state) + XMM_ALIGN];
173 
174 		/*
175 		 * Extract the mxcsr mask from our first fxsave
176 		 */
177 		fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
178 		    XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
179 
180 		fx->fx_mxcsr_mask = 0;
181 		fxsave_insn(fx);
182 		if (fx->fx_mxcsr_mask != 0) {
183 			/*
184 			 * Override default mask initialized in fpu.c
185 			 */
186 			sse_mxcsr_mask = fx->fx_mxcsr_mask;
187 		}
188 	}
189 
190 	setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
191 	return;
192 
193 	/*
194 	 * No FPU hardware present
195 	 */
196 nofpu:
197 	setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
198 	DISABLE_SSE();
199 	fp_kind = FP_NO;
200 	fpu_exists = 0;
201 }
202