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