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