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 */
60int fpu_exists = 1;
61
62int 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 */
68int fp_elf = AT_386_FPINFO_FXSAVE;
69
70/*
71 * Mechanism to save FPU state.
72 */
73int 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 */
80int 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 */
86int use_sse_pagecopy = 0;
87int use_sse_pagezero = 0;
88int 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 */
110void
111fpu_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	 */
225nofpu:
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 */
235void
236fpu_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}
251