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 kind of FPU we advertise to rtld so it knows what to do on context
66 * switch.
67 */
68 int fp_elf = AT_386_FPINFO_FXSAVE;
69
70 /*
71 * Mechanism to save FPU state.
72 */
73 int fp_save_mech = FP_FXSAVE;
74
75 /*
76 * The variable fpu_ignored is provided to allow other code to
77 * determine whether emulation is being done because there is
78 * no FPU or because of an override requested via /etc/system.
79 */
80 int fpu_ignored = 0;
81
82 /*
83 * Used by ppcopy and ppzero to determine whether or not to use the
84 * SSE-based pagecopy and pagezero routines
85 */
86 int use_sse_pagecopy = 0;
87 int use_sse_pagezero = 0;
88 int use_sse_copy = 0;
89
90 #if defined(__xpv)
91
92 /*
93 * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
94 */
95
96 #define ENABLE_SSE()
97 #define DISABLE_SSE()
98
99 #else /* __xpv */
100
101 #define ENABLE_SSE() setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
102 #define DISABLE_SSE() setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
103
104 #endif /* __xpv */
105
106 /*
107 * Try and figure out what kind of FP capabilities we have, and
108 * set up the control registers accordingly.
109 */
110 void
111 fpu_probe(void)
112 {
113 if (fpu_initial_probe() != 0)
114 goto nofpu;
115
116 if (fpu_exists == 0) {
117 fpu_ignored = 1;
118 goto nofpu;
119 }
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 goto nofpu;
131 #endif
132
133 /* Use the more complex exception clearing code if necessary */
134 if (cpuid_need_fp_excp_handling())
135 fpsave_ctxt = fpxsave_excp_clr_ctxt;
136
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 (is_x86_feature(x86_featureset, X86FSET_SSE) &&
146 is_x86_feature(x86_featureset, X86FSET_SSE2)) {
147 fp_kind |= __FP_SSE;
148 ENABLE_SSE();
149
150 if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
151 ASSERT(is_x86_feature(x86_featureset, X86FSET_XSAVE));
152 fp_kind |= __FP_AVX;
153 }
154
155 if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
156 fp_save_mech = FP_XSAVE;
157 fp_elf = AT_386_FPINFO_XSAVE;
158 if (is_x86_feature(x86_featureset, X86FSET_XSAVEOPT)) {
159 /*
160 * Use the more complex exception
161 * clearing code if necessary.
162 */
163 if (cpuid_need_fp_excp_handling()) {
164 fpsave_ctxt = xsaveopt_excp_clr_ctxt;
165 fp_elf = AT_386_FPINFO_XSAVE_AMD;
166 } else {
167 fpsave_ctxt = xsaveopt_ctxt;
168 }
169 xsavep = xsaveopt;
170 } else {
171 /*
172 * Use the more complex exception
173 * clearing code if necessary.
174 */
175 if (cpuid_need_fp_excp_handling()) {
176 fpsave_ctxt = xsave_excp_clr_ctxt;
177 fp_elf = AT_386_FPINFO_XSAVE_AMD;
178 } else {
179 fpsave_ctxt = xsave_ctxt;
180 }
181 }
182 fprestore_ctxt = xrestore_ctxt;
183 fpsave_cachep = kmem_cache_create("xsave_cache",
184 cpuid_get_xsave_size(), XSAVE_ALIGN,
185 NULL, NULL, NULL, NULL, NULL, 0);
186 } else {
187 /* fp_save_mech defaults to FP_FXSAVE */
188 fpsave_cachep = kmem_cache_create("fxsave_cache",
189 sizeof (struct fxsave_state), FXSAVE_ALIGN,
190 NULL, NULL, NULL, NULL, NULL, 0);
191 fp_elf = AT_386_FPINFO_FXSAVE;
192 }
193 }
194
195 if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
196 use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
197 }
198
199 if (fp_kind & __FP_SSE) {
200 struct fxsave_state *fx;
201 uint8_t fxsave_state[sizeof (struct fxsave_state) + XMM_ALIGN];
202
203 /*
204 * Extract the mxcsr mask from our first fxsave
205 */
206 fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
207 XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
208
209 fx->fx_mxcsr_mask = 0;
210 fxsave_insn(fx);
211 if (fx->fx_mxcsr_mask != 0) {
212 /*
213 * Override default mask initialized in fpu.c
214 */
215 sse_mxcsr_mask = fx->fx_mxcsr_mask;
216 }
217 }
218
219 setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
220 return;
221
222 /*
223 * No FPU hardware present
224 */
225 nofpu:
226 setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
227 DISABLE_SSE();
228 fp_kind = FP_NO;
229 fpu_exists = 0;
230 }
231
232 /*
233 * Fill in FPU information that is required by exec.
234 */
235 void
236 fpu_auxv_info(int *typep, size_t *lenp)
237 {
238 *typep = fp_elf;
239 switch (fp_save_mech) {
240 case FP_FXSAVE:
241 *lenp = sizeof (struct fxsave_state);
242 break;
243 case FP_XSAVE:
244 *lenp = cpuid_get_xsave_size();
245 break;
246 default:
247 *lenp = 0;
248 break;
249 }
250 }