]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/fpu.c
xnu-4570.20.62.tar.gz
[apple/xnu.git] / osfmk / i386 / fpu.c
CommitLineData
1c79356b 1/*
39236c6e 2 * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1992-1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58
1c79356b
A
59
60#include <mach/exception_types.h>
61#include <mach/i386/thread_status.h>
62#include <mach/i386/fp_reg.h>
6d2010ae 63#include <mach/branch_predicates.h>
1c79356b
A
64
65#include <kern/mach_param.h>
91447636 66#include <kern/processor.h>
1c79356b
A
67#include <kern/thread.h>
68#include <kern/zalloc.h>
69#include <kern/misc_protos.h>
70#include <kern/spl.h>
71#include <kern/assert.h>
72
060df5ea
A
73#include <libkern/OSAtomic.h>
74
0c530ab8 75#include <architecture/i386/pio.h>
55e303ae 76#include <i386/cpuid.h>
b0d623f7 77#include <i386/fpu.h>
0c530ab8 78#include <i386/proc_reg.h>
b0d623f7
A
79#include <i386/misc_protos.h>
80#include <i386/thread.h>
81#include <i386/trap.h>
1c79356b 82
5ba3f43e
A
83xstate_t fpu_capability = UNDEFINED; /* extended state capability */
84xstate_t fpu_default = UNDEFINED; /* default extended state */
1c79356b 85
b0d623f7 86#define ALIGNED(addr,size) (((uintptr_t)(addr)&((size)-1))==0)
1c79356b
A
87
88/* Forward */
89
90extern void fpinit(void);
91extern void fp_save(
91447636 92 thread_t thr_act);
1c79356b 93extern void fp_load(
91447636 94 thread_t thr_act);
1c79356b 95
5ba3f43e
A
96static void configure_mxcsr_capability_mask(x86_ext_thread_state_t *fps);
97static xstate_t thread_xstate(thread_t);
0c530ab8 98
5ba3f43e 99x86_ext_thread_state_t initial_fp_state __attribute((aligned(64)));
0c530ab8
A
100
101
102/* Global MXCSR capability bitmask */
103static unsigned int mxcsr_capability_mask;
104
060df5ea
A
105#define fninit() \
106 __asm__ volatile("fninit")
107
108#define fnstcw(control) \
109 __asm__("fnstcw %0" : "=m" (*(unsigned short *)(control)))
110
111#define fldcw(control) \
112 __asm__ volatile("fldcw %0" : : "m" (*(unsigned short *) &(control)) )
113
114#define fnclex() \
115 __asm__ volatile("fnclex")
116
117#define fnsave(state) \
118 __asm__ volatile("fnsave %0" : "=m" (*state))
119
120#define frstor(state) \
121 __asm__ volatile("frstor %0" : : "m" (state))
122
123#define fwait() \
124 __asm__("fwait");
125
5ba3f43e
A
126static inline void fxrstor(struct x86_fx_thread_state *a) {
127 __asm__ __volatile__("fxrstor %0" :: "m" (*a));
128}
129
130static inline void fxsave(struct x86_fx_thread_state *a) {
131 __asm__ __volatile__("fxsave %0" : "=m" (*a));
132}
133
134static inline void fxrstor64(struct x86_fx_thread_state *a) {
135 __asm__ __volatile__("fxrstor64 %0" :: "m" (*a));
136}
137
138static inline void fxsave64(struct x86_fx_thread_state *a) {
139 __asm__ __volatile__("fxsave64 %0" : "=m" (*a));
140}
141
142#if !defined(RC_HIDE_XNU_J137)
143#define IS_VALID_XSTATE(x) ((x) == FP || (x) == AVX || (x) == AVX512)
144#else
145#define IS_VALID_XSTATE(x) ((x) == FP || (x) == AVX)
146#endif
147
148zone_t ifps_zone[] = {
149 [FP] = NULL,
150 [AVX] = NULL,
151#if !defined(RC_HIDE_XNU_J137)
152 [AVX512] = NULL
153#endif
154};
155static uint32_t fp_state_size[] = {
156 [FP] = sizeof(struct x86_fx_thread_state),
157 [AVX] = sizeof(struct x86_avx_thread_state),
158#if !defined(RC_HIDE_XNU_J137)
159 [AVX512] = sizeof(struct x86_avx512_thread_state)
160#endif
161};
162
163static const char *xstate_name[] = {
164 [UNDEFINED] = "UNDEFINED",
165 [FP] = "FP",
166 [AVX] = "AVX",
167#if !defined(RC_HIDE_XNU_J137)
168 [AVX512] = "AVX512"
169#endif
170};
060df5ea 171
5ba3f43e
A
172#if !defined(RC_HIDE_XNU_J137)
173#define fpu_ZMM_capable (fpu_capability == AVX512)
174#define fpu_YMM_capable (fpu_capability == AVX || fpu_capability == AVX512)
175/*
176 * On-demand AVX512 support
177 * ------------------------
178 * On machines with AVX512 support, by default, threads are created with
179 * AVX512 masked off in XCR0 and an AVX-sized savearea is used. However, AVX512
180 * capabilities are advertised in the commpage and via sysctl. If a thread
181 * opts to use AVX512 instructions, the first will result in a #UD exception.
182 * Faulting AVX512 intructions are recognizable by their unique prefix.
183 * This exception results in the thread being promoted to use an AVX512-sized
184 * savearea and for the AVX512 bit masks being set in its XCR0. The faulting
185 * instruction is re-driven and the thread can proceed to perform AVX512
186 * operations.
187 *
188 * In addition to AVX512 instructions causing promotion, the thread_set_state()
189 * primitive with an AVX512 state flavor result in promotion.
190 *
191 * AVX512 promotion of the first thread in a task causes the default xstate
192 * of the task to be promoted so that any subsequently created or subsequently
193 * DNA-faulted thread will have AVX512 xstate and it will not need to fault-in
194 * a promoted xstate.
195 *
196 * Two savearea zones are used: the default pool of AVX-sized (832 byte) areas
197 * and a second pool of larger AVX512-sized (2688 byte) areas.
198 *
199 * Note the initial state value is an AVX512 object but that the AVX initial
200 * value is a subset of it.
201 */
202#else
203#define fpu_YMM_capable (fpu_capability == AVX)
204#endif
060df5ea
A
205static uint32_t cpuid_reevaluated = 0;
206
207static void fpu_store_registers(void *, boolean_t);
208static void fpu_load_registers(void *);
209
5ba3f43e
A
210#define FP_XMASK ((uint32_t) (XFEM_X87 | XFEM_SSE))
211#define AVX_XMASK ((uint32_t) (XFEM_X87 | XFEM_SSE | XFEM_YMM))
212#if !defined(RC_HIDE_XNU_J137)
213#define AVX512_XMASK ((uint32_t) (XFEM_X87 | XFEM_SSE | XFEM_YMM | XFEM_ZMM))
214static const uint32_t xstate_xmask[] = {
215 [FP] = FP_XMASK,
216 [AVX] = AVX_XMASK,
217 [AVX512] = AVX512_XMASK
218};
219#else
220static const uint32_t xstate_xmask[] = {
221 [FP] = FP_XMASK,
222 [AVX] = AVX_XMASK,
223};
224#endif
060df5ea 225
060df5ea 226static inline void xsetbv(uint32_t mask_hi, uint32_t mask_lo) {
fe8ab488 227 __asm__ __volatile__("xsetbv" :: "a"(mask_lo), "d"(mask_hi), "c" (XCR0));
060df5ea
A
228}
229
5ba3f43e
A
230static inline void xsave(struct x86_fx_thread_state *a, uint32_t rfbm) {
231 __asm__ __volatile__("xsave %0" :"=m" (*a) : "a"(rfbm), "d"(0));
232}
233
234static inline void xsave64(struct x86_fx_thread_state *a, uint32_t rfbm) {
235 __asm__ __volatile__("xsave64 %0" :"=m" (*a) : "a"(rfbm), "d"(0));
236}
237
238static inline void xrstor(struct x86_fx_thread_state *a, uint32_t rfbm) {
239 __asm__ __volatile__("xrstor %0" :: "m" (*a), "a"(rfbm), "d"(0));
240}
241
242static inline void xrstor64(struct x86_fx_thread_state *a, uint32_t rfbm) {
243 __asm__ __volatile__("xrstor64 %0" :: "m" (*a), "a"(rfbm), "d"(0));
060df5ea
A
244}
245
5ba3f43e
A
246#if !defined(RC_HIDE_XNU_J137)
247static inline void vzeroupper(void) {
248 __asm__ __volatile__("vzeroupper" ::);
249}
250#if DEVELOPMENT || DEBUG
251static inline uint64_t xgetbv(uint32_t c) {
252 uint32_t mask_hi, mask_lo;
253 __asm__ __volatile__("xgetbv" : "=a"(mask_lo), "=d"(mask_hi) : "c" (c));
254 return ((uint64_t) mask_hi<<32) + (uint64_t) mask_lo;
060df5ea 255}
5ba3f43e
A
256#endif
257
258static boolean_t fpu_thread_promote_avx512(thread_t); /* Forward */
259
260/*
261 * Define a wrapper for bcopy to defeat destination size checka.
262 * This is needed to treat repeated objects such as
263 * _STRUCT_XMM_REG fpu_ymmh0;
264 * ...
265 * _STRUCT_XMM_REG fpu_ymmh7;
266 * as an array and to copy like so:
267 * bcopy_nockch(src,&dst->fpu_ymmh0,8*sizeof(_STRUCT_XMM_REG));
268 * without the compiler throwing a __builtin__memmove_chk error.
269 */
270static inline void bcopy_nochk(void *_src, void *_dst, size_t _len) {
271 bcopy(_src, _dst, _len);
272}
273
274/*
275 * Furthermore, make compile-time asserts that no padding creeps into structures
276 * for which we're doing this.
277 */
278#define ASSERT_PACKED(t, m1, m2, n, mt) \
279extern char assert_packed_ ## t ## _ ## m1 ## _ ## m2 \
280 [(offsetof(t,m2) - offsetof(t,m1) == (n - 1)*sizeof(mt)) ? 1 : -1]
281
282ASSERT_PACKED(x86_avx_state32_t, fpu_ymmh0, fpu_ymmh7, 8, _STRUCT_XMM_REG);
283
284ASSERT_PACKED(x86_avx_state64_t, fpu_ymmh0, fpu_ymmh15, 16, _STRUCT_XMM_REG);
285
286ASSERT_PACKED(x86_avx512_state32_t, fpu_k0, fpu_k7, 8, _STRUCT_OPMASK_REG);
287ASSERT_PACKED(x86_avx512_state32_t, fpu_ymmh0, fpu_ymmh7, 8, _STRUCT_XMM_REG);
288ASSERT_PACKED(x86_avx512_state32_t, fpu_zmmh0, fpu_zmmh7, 8, _STRUCT_YMM_REG);
289
290ASSERT_PACKED(x86_avx512_state64_t, fpu_k0, fpu_k7, 8, _STRUCT_OPMASK_REG);
291ASSERT_PACKED(x86_avx512_state64_t, fpu_ymmh0, fpu_ymmh15, 16, _STRUCT_XMM_REG);
292ASSERT_PACKED(x86_avx512_state64_t, fpu_zmmh0, fpu_zmmh15, 16, _STRUCT_YMM_REG);
293ASSERT_PACKED(x86_avx512_state64_t, fpu_zmm16, fpu_zmm31, 16, _STRUCT_ZMM_REG);
294
295#if defined(DEBUG_AVX512)
296
297#define DBG(x...) kprintf("DBG: " x)
298
299typedef struct { uint8_t byte[8]; } opmask_t;
300typedef struct { uint8_t byte[16]; } xmm_t;
301typedef struct { uint8_t byte[32]; } ymm_t;
302typedef struct { uint8_t byte[64]; } zmm_t;
303
304static void
305DBG_AVX512_STATE(struct x86_avx512_thread_state *sp)
306{
307 int i, j;
308 xmm_t *xmm = (xmm_t *) &sp->fp.fx_XMM_reg;
309 xmm_t *ymmh = (xmm_t *) &sp->x_YMM_Hi128;
310 ymm_t *zmmh = (ymm_t *) &sp->x_ZMM_Hi256;
311 zmm_t *zmm = (zmm_t *) &sp->x_Hi16_ZMM;
312 opmask_t *k = (opmask_t *) &sp->x_Opmask;
313
314 kprintf("x_YMM_Hi128: %lu\n", offsetof(struct x86_avx512_thread_state, x_YMM_Hi128));
315 kprintf("x_Opmask: %lu\n", offsetof(struct x86_avx512_thread_state, x_Opmask));
316 kprintf("x_ZMM_Hi256: %lu\n", offsetof(struct x86_avx512_thread_state, x_ZMM_Hi256));
317 kprintf("x_Hi16_ZMM: %lu\n", offsetof(struct x86_avx512_thread_state, x_Hi16_ZMM));
318
319 kprintf("XCR0: 0x%016llx\n", xgetbv(XCR0));
320 kprintf("XINUSE: 0x%016llx\n", xgetbv(1));
321
322 /* Print all ZMM registers */
323 for (i = 0; i < 16; i++) {
324 kprintf("zmm%d:\t0x", i);
325 for (j = 0; j < 16; j++)
326 kprintf("%02x", xmm[i].byte[j]);
327 for (j = 0; j < 16; j++)
328 kprintf("%02x", ymmh[i].byte[j]);
329 for (j = 0; j < 32; j++)
330 kprintf("%02x", zmmh[i].byte[j]);
331 kprintf("\n");
332 }
333 for (i = 0; i < 16; i++) {
334 kprintf("zmm%d:\t0x", 16+i);
335 for (j = 0; j < 64; j++)
336 kprintf("%02x", zmm[i].byte[j]);
337 kprintf("\n");
338 }
339 for (i = 0; i < 8; i++) {
340 kprintf("k%d:\t0x", i);
341 for (j = 0; j < 8; j++)
342 kprintf("%02x", k[i].byte[j]);
343 kprintf("\n");
344 }
345
346 kprintf("xstate_bv: 0x%016llx\n", sp->_xh.xstate_bv);
347 kprintf("xcomp_bv: 0x%016llx\n", sp->_xh.xcomp_bv);
348}
349#else
350#define DBG(x...)
351static void
352DBG_AVX512_STATE(__unused struct x86_avx512_thread_state *sp)
353{
354 return;
355}
356#endif /* DEBUG_AVX512 */
357
358#endif
060df5ea 359
fe8ab488 360#if DEBUG
060df5ea
A
361static inline unsigned short
362fnstsw(void)
363{
364 unsigned short status;
365 __asm__ volatile("fnstsw %0" : "=ma" (status));
366 return(status);
367}
fe8ab488 368#endif
060df5ea 369
0c530ab8 370/*
060df5ea 371 * Configure the initial FPU state presented to new threads.
0c530ab8
A
372 * Determine the MXCSR capability mask, which allows us to mask off any
373 * potentially unsafe "reserved" bits before restoring the FPU context.
374 * *Not* per-cpu, assumes symmetry.
375 */
060df5ea 376
0c530ab8 377static void
5ba3f43e 378configure_mxcsr_capability_mask(x86_ext_thread_state_t *fps)
0c530ab8 379{
060df5ea
A
380 /* XSAVE requires a 64 byte aligned store */
381 assert(ALIGNED(fps, 64));
0c530ab8 382 /* Clear, to prepare for the diagnostic FXSAVE */
060df5ea
A
383 bzero(fps, sizeof(*fps));
384
385 fpinit();
386 fpu_store_registers(fps, FALSE);
387
5ba3f43e 388 mxcsr_capability_mask = fps->fx.fx_MXCSR_MASK;
0c530ab8
A
389
390 /* Set default mask value if necessary */
391 if (mxcsr_capability_mask == 0)
392 mxcsr_capability_mask = 0xffbf;
393
060df5ea 394 /* Clear vector register store */
5ba3f43e
A
395 bzero(&fps->fx.fx_XMM_reg[0][0], sizeof(fps->fx.fx_XMM_reg));
396 bzero(fps->avx.x_YMM_Hi128, sizeof(fps->avx.x_YMM_Hi128));
397#if !defined(RC_HIDE_XNU_J137)
398 if (fpu_ZMM_capable) {
399 bzero(fps->avx512.x_ZMM_Hi256, sizeof(fps->avx512.x_ZMM_Hi256));
400 bzero(fps->avx512.x_Hi16_ZMM, sizeof(fps->avx512.x_Hi16_ZMM));
401 bzero(fps->avx512.x_Opmask, sizeof(fps->avx512.x_Opmask));
402 }
403#endif
0c530ab8 404
5ba3f43e
A
405 fps->fx.fp_valid = TRUE;
406 fps->fx.fp_save_layout = fpu_YMM_capable ? XSAVE32: FXSAVE32;
060df5ea 407 fpu_load_registers(fps);
0c530ab8 408
060df5ea 409 /* Poison values to trap unsafe usage */
5ba3f43e
A
410 fps->fx.fp_valid = 0xFFFFFFFF;
411 fps->fx.fp_save_layout = FP_UNUSED;
0c530ab8 412
060df5ea
A
413 /* Re-enable FPU/SSE DNA exceptions */
414 set_ts();
0c530ab8
A
415}
416
1c79356b
A
417/*
418 * Look for FPU and initialize it.
419 * Called on each CPU.
420 */
421void
422init_fpu(void)
423{
060df5ea
A
424#if DEBUG
425 unsigned short status;
426 unsigned short control;
427#endif
1c79356b
A
428 /*
429 * Check for FPU by initializing it,
430 * then trying to read the correct bit patterns from
431 * the control and status registers.
432 */
91447636 433 set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */
1c79356b 434 fninit();
060df5ea 435#if DEBUG
1c79356b
A
436 status = fnstsw();
437 fnstcw(&control);
060df5ea
A
438
439 assert(((status & 0xff) == 0) && ((control & 0x103f) == 0x3f));
440#endif
441 /* Advertise SSE support */
442 if (cpuid_features() & CPUID_FEATURE_FXSR) {
060df5ea
A
443 set_cr4(get_cr4() | CR4_OSFXS);
444 /* And allow SIMD exceptions if present */
445 if (cpuid_features() & CPUID_FEATURE_SSE) {
446 set_cr4(get_cr4() | CR4_OSXMM);
447 }
060df5ea
A
448 } else
449 panic("fpu is not FP_FXSR");
55e303ae 450
5ba3f43e
A
451 fpu_capability = fpu_default = FP;
452
453#if !defined(RC_HIDE_XNU_J137)
454 static boolean_t is_avx512_enabled = TRUE;
455 if (cpu_number() == master_cpu) {
456 if (cpuid_leaf7_features() & CPUID_LEAF7_FEATURE_AVX512F) {
457 PE_parse_boot_argn("avx512", &is_avx512_enabled, sizeof(boolean_t));
458 kprintf("AVX512 supported %s\n",
459 is_avx512_enabled ? "and enabled" : "but disabled");
460 }
461 }
462#endif
463
060df5ea
A
464 /* Configure the XSAVE context mechanism if the processor supports
465 * AVX/YMM registers
466 */
467 if (cpuid_features() & CPUID_FEATURE_XSAVE) {
5ba3f43e
A
468 cpuid_xsave_leaf_t *xs0p = &cpuid_info()->cpuid_xsave_leaf[0];
469#if !defined(RC_HIDE_XNU_J137)
470 if (is_avx512_enabled &&
471 (xs0p->extended_state[eax] & XFEM_ZMM) == XFEM_ZMM) {
472 assert(xs0p->extended_state[eax] & XFEM_SSE);
473 assert(xs0p->extended_state[eax] & XFEM_YMM);
474 fpu_capability = AVX512;
475 /* XSAVE container size for all features */
476 set_cr4(get_cr4() | CR4_OSXSAVE);
477 xsetbv(0, AVX512_XMASK);
478 /* Re-evaluate CPUID, once, to reflect OSXSAVE */
479 if (OSCompareAndSwap(0, 1, &cpuid_reevaluated))
480 cpuid_set_info();
481 /* Verify that now selected state can be accommodated */
482 assert(xs0p->extended_state[ebx] == fp_state_size[AVX512]);
483 /*
484 * AVX set until AVX512 is used.
485 * See comment above about on-demand AVX512 support.
486 */
487 xsetbv(0, AVX_XMASK);
488 fpu_default = AVX;
489 } else
490#endif
491 if (xs0p->extended_state[eax] & XFEM_YMM) {
492 assert(xs0p->extended_state[eax] & XFEM_SSE);
493 fpu_capability = AVX;
494 fpu_default = AVX;
060df5ea 495 /* XSAVE container size for all features */
060df5ea 496 set_cr4(get_cr4() | CR4_OSXSAVE);
5ba3f43e 497 xsetbv(0, AVX_XMASK);
060df5ea
A
498 /* Re-evaluate CPUID, once, to reflect OSXSAVE */
499 if (OSCompareAndSwap(0, 1, &cpuid_reevaluated))
500 cpuid_set_info();
3e170ce0 501 /* Verify that now selected state can be accommodated */
5ba3f43e 502 assert(xs0p->extended_state[ebx] == fp_state_size[AVX]);
060df5ea
A
503 }
504 }
5ba3f43e
A
505
506 if (cpu_number() == master_cpu)
507 kprintf("fpu_state: %s, state_size: %d\n",
508 xstate_name[fpu_capability],
509 fp_state_size[fpu_capability]);
060df5ea
A
510
511 fpinit();
512
513 /*
514 * Trap wait instructions. Turn off FPU for now.
515 */
516 set_cr0(get_cr0() | CR0_TS | CR0_MP);
517}
518
519/*
5ba3f43e 520 * Allocate and initialize FP state for specified xstate.
060df5ea
A
521 * Don't load state.
522 */
523static void *
5ba3f43e 524fp_state_alloc(xstate_t xs)
060df5ea 525{
5ba3f43e
A
526 struct x86_fx_thread_state *ifps;
527
528 assert(ifps_zone[xs] != NULL);
529 ifps = zalloc(ifps_zone[xs]);
0c530ab8 530
060df5ea
A
531#if DEBUG
532 if (!(ALIGNED(ifps,64))) {
5ba3f43e
A
533 panic("fp_state_alloc: %p, %u, %p, %u",
534 ifps, (unsigned) ifps_zone[xs]->elem_size,
535 (void *) ifps_zone[xs]->free_elements,
536 (unsigned) ifps_zone[xs]->alloc_size);
1c79356b 537 }
060df5ea 538#endif
5ba3f43e
A
539 bzero(ifps, fp_state_size[xs]);
540
060df5ea
A
541 return ifps;
542}
543
544static inline void
5ba3f43e 545fp_state_free(void *ifps, xstate_t xs)
060df5ea 546{
5ba3f43e
A
547 assert(ifps_zone[xs] != NULL);
548 zfree(ifps_zone[xs], ifps);
060df5ea
A
549}
550
551void clear_fpu(void)
552{
553 set_ts();
554}
555
556
557static void fpu_load_registers(void *fstate) {
558 struct x86_fx_thread_state *ifps = fstate;
559 fp_save_layout_t layout = ifps->fp_save_layout;
560
5ba3f43e
A
561 assert(current_task() == NULL || \
562 (thread_is_64bit(current_thread()) ? \
563 (layout == FXSAVE64 || layout == XSAVE64) : \
564 (layout == FXSAVE32 || layout == XSAVE32)));
060df5ea
A
565 assert(ALIGNED(ifps, 64));
566 assert(ml_get_interrupts_enabled() == FALSE);
567
568#if DEBUG
569 if (layout == XSAVE32 || layout == XSAVE64) {
570 struct x86_avx_thread_state *iavx = fstate;
571 unsigned i;
572 /* Verify reserved bits in the XSAVE header*/
5ba3f43e
A
573 if (iavx->_xh.xstate_bv & ~xstate_xmask[current_xstate()])
574 panic("iavx->_xh.xstate_bv: 0x%llx", iavx->_xh.xstate_bv);
060df5ea
A
575 for (i = 0; i < sizeof(iavx->_xh.xhrsvd); i++)
576 if (iavx->_xh.xhrsvd[i])
577 panic("Reserved bit set");
578 }
5ba3f43e 579 if (fpu_YMM_capable) {
060df5ea
A
580 if (layout != XSAVE32 && layout != XSAVE64)
581 panic("Inappropriate layout: %u\n", layout);
582 }
583#endif /* DEBUG */
584
5ba3f43e
A
585 switch (layout) {
586 case FXSAVE64:
587 fxrstor64(ifps);
588 break;
589 case FXSAVE32:
060df5ea 590 fxrstor(ifps);
5ba3f43e
A
591 break;
592 case XSAVE64:
593 xrstor64(ifps, xstate_xmask[current_xstate()]);
594 break;
595 case XSAVE32:
596 xrstor(ifps, xstate_xmask[current_xstate()]);
597 break;
598 default:
599 panic("fpu_load_registers() bad layout: %d\n", layout);
600 }
060df5ea
A
601}
602
603static void fpu_store_registers(void *fstate, boolean_t is64) {
604 struct x86_fx_thread_state *ifps = fstate;
605 assert(ALIGNED(ifps, 64));
5ba3f43e
A
606 xstate_t xs = current_xstate();
607 switch (xs) {
608 case FP:
609 if (is64) {
610 fxsave64(fstate);
611 ifps->fp_save_layout = FXSAVE64;
612 } else {
613 fxsave(fstate);
614 ifps->fp_save_layout = FXSAVE32;
615 }
616 break;
617 case AVX:
618#if !defined(RC_HIDE_XNU_J137)
619 case AVX512:
620#endif
621 if (is64) {
622 xsave64(ifps, xstate_xmask[xs]);
623 ifps->fp_save_layout = XSAVE64;
624 } else {
625 xsave(ifps, xstate_xmask[xs]);
626 ifps->fp_save_layout = XSAVE32;
627 }
628 break;
629 default:
630 panic("fpu_store_registers() bad xstate: %d\n", xs);
060df5ea 631 }
1c79356b
A
632}
633
634/*
635 * Initialize FP handling.
636 */
060df5ea 637
1c79356b
A
638void
639fpu_module_init(void)
640{
5ba3f43e
A
641 if (!IS_VALID_XSTATE(fpu_default))
642 panic("fpu_module_init: invalid extended state %u\n",
643 fpu_default);
060df5ea 644
5ba3f43e 645 /* We explicitly choose an allocation size of 13 pages = 64 * 832
060df5ea
A
646 * to eliminate waste for the 832 byte sized
647 * AVX XSAVE register save area.
648 */
5ba3f43e
A
649 ifps_zone[fpu_default] = zinit(fp_state_size[fpu_default],
650 thread_max * fp_state_size[fpu_default],
651 64 * fp_state_size[fpu_default],
652 "x86 fpsave state");
060df5ea 653
060df5ea
A
654 /* To maintain the required alignment, disable
655 * zone debugging for this zone as that appends
656 * 16 bytes to each element.
657 */
5ba3f43e
A
658 zone_change(ifps_zone[fpu_default], Z_ALIGNMENT_REQUIRED, TRUE);
659
660#if !defined(RC_HIDE_XNU_J137)
661 /*
662 * If AVX512 is supported, create a separate savearea zone.
663 * with allocation size: 19 pages = 32 * 2668
664 */
665 if (fpu_capability == AVX512) {
666 ifps_zone[AVX512] = zinit(fp_state_size[AVX512],
667 thread_max * fp_state_size[AVX512],
668 32 * fp_state_size[AVX512],
669 "x86 avx512 save state");
670 zone_change(ifps_zone[AVX512], Z_ALIGNMENT_REQUIRED, TRUE);
671 }
672#endif
673
060df5ea
A
674 /* Determine MXCSR reserved bits and configure initial FPU state*/
675 configure_mxcsr_capability_mask(&initial_fp_state);
676}
677
678/*
5ba3f43e
A
679 * Context switch fpu state.
680 * Always save old thread`s FPU context but don't load new .. allow that to fault-in.
681 * Switch to the new task's xstate.
060df5ea
A
682 */
683void
5ba3f43e 684fpu_switch_context(thread_t old, thread_t new)
060df5ea 685{
5ba3f43e
A
686 struct x86_fx_thread_state *ifps;
687 boolean_t is_ts_cleared = FALSE;
060df5ea
A
688
689 assert(ml_get_interrupts_enabled() == FALSE);
5ba3f43e 690 ifps = (old)->machine.ifps;
060df5ea
A
691#if DEBUG
692 if (ifps && ((ifps->fp_valid != FALSE) && (ifps->fp_valid != TRUE))) {
693 panic("ifps->fp_valid: %u\n", ifps->fp_valid);
694 }
695#endif
696 if (ifps != 0 && (ifps->fp_valid == FALSE)) {
697 /* Clear CR0.TS in preparation for the FP context save. In
698 * theory, this shouldn't be necessary since a live FPU should
699 * indicate that TS is clear. However, various routines
700 * (such as sendsig & sigreturn) manipulate TS directly.
701 */
702 clear_ts();
5ba3f43e 703 is_ts_cleared = TRUE;
060df5ea 704 /* registers are in FPU - save to memory */
5ba3f43e 705 fpu_store_registers(ifps, (thread_is_64bit(old) && is_saved_state64(old->machine.iss)));
060df5ea
A
706 ifps->fp_valid = TRUE;
707 }
5ba3f43e
A
708#if !defined(RC_HIDE_XNU_J137)
709 xstate_t old_xstate = thread_xstate(old);
710 xstate_t new_xstate = new ? thread_xstate(new) : fpu_default;
711 if (old_xstate == AVX512 && ifps != 0) {
712 DBG_AVX512_STATE((struct x86_avx512_thread_state *) ifps);
713 /*
714 * Clear upper bits for potential power-saving
715 * but first ensure the TS bit is clear.
716 */
717 if (!is_ts_cleared)
718 clear_ts();
719 vzeroupper();
720 }
721 if (new_xstate != old_xstate) {
722 DBG("fpu_switch_context(%p,%p) new xstate: %s\n",
723 old, new, xstate_name[new_xstate]);
724 xsetbv(0, xstate_xmask[new_xstate]);
725 }
726#else
727#pragma unused(new)
728#endif
060df5ea 729 set_ts();
1c79356b
A
730}
731
060df5ea 732
1c79356b
A
733/*
734 * Free a FPU save area.
735 * Called only when thread terminating - no locking necessary.
736 */
737void
5ba3f43e 738fpu_free(thread_t thread, void *fps)
1c79356b 739{
5ba3f43e
A
740 pcb_t pcb = THREAD_TO_PCB(thread);
741
742 fp_state_free(fps, pcb->xstate);
743 pcb->xstate = UNDEFINED;
1c79356b
A
744}
745
55e303ae
A
746/*
747 * Set the floating-point state for a thread based
748 * on the FXSave formatted data. This is basically
749 * the same as fpu_set_state except it uses the
750 * expanded data structure.
751 * If the thread is not the current thread, it is
752 * not running (held). Locking needed against
753 * concurrent fpu_set_state or fpu_get_state.
754 */
755kern_return_t
756fpu_set_fxstate(
060df5ea
A
757 thread_t thr_act,
758 thread_state_t tstate,
759 thread_flavor_t f)
55e303ae 760{
5ba3f43e
A
761 struct x86_fx_thread_state *ifps;
762 struct x86_fx_thread_state *new_ifps;
763 x86_float_state64_t *state;
764 pcb_t pcb;
765 boolean_t old_valid, fresh_state = FALSE;
fe8ab488 766
5ba3f43e 767 if (fpu_capability == UNDEFINED)
fe8ab488 768 return KERN_FAILURE;
0c530ab8 769
bd504ef0 770 if ((f == x86_AVX_STATE32 || f == x86_AVX_STATE64) &&
5ba3f43e 771 fpu_capability < AVX)
fe8ab488 772 return KERN_FAILURE;
bd504ef0 773
5ba3f43e
A
774#if !defined(RC_HIDE_XNU_J137)
775 if ((f == x86_AVX512_STATE32 || f == x86_AVX512_STATE64) &&
776 thread_xstate(thr_act) == AVX)
777 if (!fpu_thread_promote_avx512(thr_act))
778 return KERN_FAILURE;
779#endif
780
0c530ab8 781 state = (x86_float_state64_t *)tstate;
55e303ae 782
91447636 783 assert(thr_act != THREAD_NULL);
6d2010ae 784 pcb = THREAD_TO_PCB(thr_act);
55e303ae 785
0c530ab8 786 if (state == NULL) {
fe8ab488
A
787 /*
788 * new FPU state is 'invalid'.
789 * Deallocate the fp state if it exists.
790 */
791 simple_lock(&pcb->lock);
0c530ab8
A
792
793 ifps = pcb->ifps;
794 pcb->ifps = 0;
4452a7af 795
fe8ab488 796 simple_unlock(&pcb->lock);
0c530ab8 797
fe8ab488 798 if (ifps != 0) {
5ba3f43e 799 fp_state_free(ifps, thread_xstate(thr_act));
fe8ab488 800 }
0c530ab8 801 } else {
fe8ab488
A
802 /*
803 * Valid incoming state. Allocate the fp state if there is none.
804 */
805 new_ifps = 0;
806 Retry:
807 simple_lock(&pcb->lock);
0c530ab8
A
808
809 ifps = pcb->ifps;
fe8ab488
A
810 if (ifps == 0) {
811 if (new_ifps == 0) {
812 simple_unlock(&pcb->lock);
5ba3f43e 813 new_ifps = fp_state_alloc(thread_xstate(thr_act));
fe8ab488
A
814 goto Retry;
815 }
816 ifps = new_ifps;
817 new_ifps = 0;
818 pcb->ifps = ifps;
5ba3f43e 819 pcb->xstate = thread_xstate(thr_act);
fe8ab488
A
820 fresh_state = TRUE;
821 }
822
823 /*
824 * now copy over the new data.
825 */
826
827 old_valid = ifps->fp_valid;
828
829#if DEBUG || DEVELOPMENT
830 if ((fresh_state == FALSE) && (old_valid == FALSE) && (thr_act != current_thread())) {
831 panic("fpu_set_fxstate inconsistency, thread: %p not stopped", thr_act);
55e303ae 832 }
060df5ea 833#endif
fe8ab488
A
834 /*
835 * Clear any reserved bits in the MXCSR to prevent a GPF
836 * when issuing an FXRSTOR.
837 */
7ddcb079 838
fe8ab488 839 state->fpu_mxcsr &= mxcsr_capability_mask;
060df5ea 840
5ba3f43e 841 bcopy((char *)&state->fpu_fcw, (char *)ifps, fp_state_size[FP]);
060df5ea 842
5ba3f43e
A
843 switch (thread_xstate(thr_act)) {
844 case UNDEFINED:
845 panic("fpu_set_fxstate() UNDEFINED xstate");
846 break;
847 case FP:
848 ifps->fp_save_layout = thread_is_64bit(thr_act) ? FXSAVE64 : FXSAVE32;
849 break;
850 case AVX: {
fe8ab488 851 struct x86_avx_thread_state *iavx = (void *) ifps;
5ba3f43e 852 x86_avx_state64_t *xs = (x86_avx_state64_t *) state;
7ddcb079 853
5ba3f43e 854 iavx->fp.fp_save_layout = thread_is_64bit(thr_act) ? XSAVE64 : XSAVE32;
7ddcb079 855
5ba3f43e
A
856 /* Sanitize XSAVE header */
857 bzero(&iavx->_xh.xhrsvd[0], sizeof(iavx->_xh.xhrsvd));
858 iavx->_xh.xstate_bv = AVX_XMASK;
859 iavx->_xh.xcomp_bv = 0;
860
861 if (f == x86_AVX_STATE32) {
862 bcopy_nochk(&xs->fpu_ymmh0, iavx->x_YMM_Hi128, 8 * sizeof(_STRUCT_XMM_REG));
863 } else if (f == x86_AVX_STATE64) {
864 bcopy_nochk(&xs->fpu_ymmh0, iavx->x_YMM_Hi128, 16 * sizeof(_STRUCT_XMM_REG));
865 } else {
866 iavx->_xh.xstate_bv = (XFEM_SSE | XFEM_X87);
fe8ab488 867 }
5ba3f43e
A
868 break;
869 }
870#if !defined(RC_HIDE_XNU_J137)
871 case AVX512: {
872 struct x86_avx512_thread_state *iavx = (void *) ifps;
873 union {
874 thread_state_t ts;
875 x86_avx512_state32_t *s32;
876 x86_avx512_state64_t *s64;
877 } xs = { .ts = tstate };
878
879 iavx->fp.fp_save_layout = thread_is_64bit(thr_act) ? XSAVE64 : XSAVE32;
fe8ab488 880
fe8ab488
A
881 /* Sanitize XSAVE header */
882 bzero(&iavx->_xh.xhrsvd[0], sizeof(iavx->_xh.xhrsvd));
5ba3f43e
A
883 iavx->_xh.xstate_bv = AVX512_XMASK;
884 iavx->_xh.xcomp_bv = 0;
885
886 switch (f) {
887 case x86_AVX512_STATE32:
888 bcopy_nochk(&xs.s32->fpu_k0, iavx->x_Opmask, 8 * sizeof(_STRUCT_OPMASK_REG));
889 bcopy_nochk(&xs.s32->fpu_zmmh0, iavx->x_ZMM_Hi256, 8 * sizeof(_STRUCT_YMM_REG));
890 bcopy_nochk(&xs.s32->fpu_ymmh0, iavx->x_YMM_Hi128, 8 * sizeof(_STRUCT_XMM_REG));
891 DBG_AVX512_STATE(iavx);
892 break;
893 case x86_AVX_STATE32:
894 bcopy_nochk(&xs.s32->fpu_ymmh0, iavx->x_YMM_Hi128, 8 * sizeof(_STRUCT_XMM_REG));
895 break;
896 case x86_AVX512_STATE64:
897 bcopy_nochk(&xs.s64->fpu_k0, iavx->x_Opmask, 8 * sizeof(_STRUCT_OPMASK_REG));
898 bcopy_nochk(&xs.s64->fpu_zmm16, iavx->x_Hi16_ZMM, 16 * sizeof(_STRUCT_ZMM_REG));
899 bcopy_nochk(&xs.s64->fpu_zmmh0, iavx->x_ZMM_Hi256, 16 * sizeof(_STRUCT_YMM_REG));
900 bcopy_nochk(&xs.s64->fpu_ymmh0, iavx->x_YMM_Hi128, 16 * sizeof(_STRUCT_XMM_REG));
901 DBG_AVX512_STATE(iavx);
902 break;
903 case x86_AVX_STATE64:
904 bcopy_nochk(&xs.s64->fpu_ymmh0, iavx->x_YMM_Hi128, 16 * sizeof(_STRUCT_XMM_REG));
905 break;
906 }
907 break;
908 }
909#endif
7ddcb079 910 }
5ba3f43e 911
fe8ab488 912 ifps->fp_valid = old_valid;
7ddcb079 913
fe8ab488
A
914 if (old_valid == FALSE) {
915 boolean_t istate = ml_set_interrupts_enabled(FALSE);
916 ifps->fp_valid = TRUE;
917 /* If altering the current thread's state, disable FPU */
918 if (thr_act == current_thread())
919 set_ts();
920
921 ml_set_interrupts_enabled(istate);
922 }
923
924 simple_unlock(&pcb->lock);
925
926 if (new_ifps != 0)
5ba3f43e 927 fp_state_free(new_ifps, thread_xstate(thr_act));
0c530ab8 928 }
55e303ae
A
929 return KERN_SUCCESS;
930}
931
932/*
933 * Get the floating-point state for a thread.
934 * If the thread is not the current thread, it is
935 * not running (held). Locking needed against
936 * concurrent fpu_set_state or fpu_get_state.
937 */
938kern_return_t
939fpu_get_fxstate(
060df5ea
A
940 thread_t thr_act,
941 thread_state_t tstate,
942 thread_flavor_t f)
55e303ae 943{
060df5ea 944 struct x86_fx_thread_state *ifps;
5ba3f43e
A
945 x86_float_state64_t *state;
946 kern_return_t ret = KERN_FAILURE;
947 pcb_t pcb;
55e303ae 948
5ba3f43e 949 if (fpu_capability == UNDEFINED)
2d21ac55 950 return KERN_FAILURE;
0c530ab8 951
bd504ef0 952 if ((f == x86_AVX_STATE32 || f == x86_AVX_STATE64) &&
5ba3f43e 953 fpu_capability < AVX)
bd504ef0
A
954 return KERN_FAILURE;
955
5ba3f43e
A
956#if !defined(RC_HIDE_XNU_J137)
957 if ((f == x86_AVX512_STATE32 || f == x86_AVX512_STATE64) &&
958 thread_xstate(thr_act) != AVX512)
959 return KERN_FAILURE;
960#endif
961
0c530ab8 962 state = (x86_float_state64_t *)tstate;
55e303ae 963
91447636 964 assert(thr_act != THREAD_NULL);
6d2010ae 965 pcb = THREAD_TO_PCB(thr_act);
55e303ae
A
966
967 simple_lock(&pcb->lock);
0c530ab8
A
968
969 ifps = pcb->ifps;
55e303ae 970 if (ifps == 0) {
2d21ac55 971 /*
0c530ab8
A
972 * No valid floating-point state.
973 */
060df5ea
A
974
975 bcopy((char *)&initial_fp_state, (char *)&state->fpu_fcw,
5ba3f43e 976 fp_state_size[FP]);
0c530ab8
A
977
978 simple_unlock(&pcb->lock);
6601e61a 979
0c530ab8
A
980 return KERN_SUCCESS;
981 }
982 /*
983 * Make sure we`ve got the latest fp state info
984 * If the live fpu state belongs to our target
985 */
2d21ac55
A
986 if (thr_act == current_thread()) {
987 boolean_t intr;
8f6c56a5 988
0c530ab8 989 intr = ml_set_interrupts_enabled(FALSE);
89b3af67 990
0c530ab8
A
991 clear_ts();
992 fp_save(thr_act);
993 clear_fpu();
6601e61a 994
0c530ab8 995 (void)ml_set_interrupts_enabled(intr);
6601e61a 996 }
0c530ab8 997 if (ifps->fp_valid) {
5ba3f43e
A
998 bcopy((char *)ifps, (char *)&state->fpu_fcw, fp_state_size[FP]);
999 switch (thread_xstate(thr_act)) {
1000 case UNDEFINED:
1001 panic("fpu_get_fxstate() UNDEFINED xstate");
1002 break;
1003 case FP:
1004 break; /* already done */
1005 case AVX: {
7ddcb079 1006 struct x86_avx_thread_state *iavx = (void *) ifps;
5ba3f43e
A
1007 x86_avx_state64_t *xs = (x86_avx_state64_t *) state;
1008 if (f == x86_AVX_STATE32) {
1009 bcopy_nochk(iavx->x_YMM_Hi128, &xs->fpu_ymmh0, 8 * sizeof(_STRUCT_XMM_REG));
1010 } else if (f == x86_AVX_STATE64) {
1011 bcopy_nochk(iavx->x_YMM_Hi128, &xs->fpu_ymmh0, 16 * sizeof(_STRUCT_XMM_REG));
1012 }
1013 break;
1014 }
1015#if !defined(RC_HIDE_XNU_J137)
1016 case AVX512: {
1017 struct x86_avx512_thread_state *iavx = (void *) ifps;
1018 union {
1019 thread_state_t ts;
1020 x86_avx512_state32_t *s32;
1021 x86_avx512_state64_t *s64;
1022 } xs = { .ts = tstate };
1023 switch (f) {
1024 case x86_AVX512_STATE32:
1025 bcopy_nochk(iavx->x_Opmask, &xs.s32->fpu_k0, 8 * sizeof(_STRUCT_OPMASK_REG));
1026 bcopy_nochk(iavx->x_ZMM_Hi256, &xs.s32->fpu_zmmh0, 8 * sizeof(_STRUCT_YMM_REG));
1027 bcopy_nochk(iavx->x_YMM_Hi128, &xs.s32->fpu_ymmh0, 8 * sizeof(_STRUCT_XMM_REG));
1028 DBG_AVX512_STATE(iavx);
1029 break;
1030 case x86_AVX_STATE32:
1031 bcopy_nochk(iavx->x_YMM_Hi128, &xs.s32->fpu_ymmh0, 8 * sizeof(_STRUCT_XMM_REG));
1032 break;
1033 case x86_AVX512_STATE64:
1034 bcopy_nochk(iavx->x_Opmask, &xs.s64->fpu_k0, 8 * sizeof(_STRUCT_OPMASK_REG));
1035 bcopy_nochk(iavx->x_Hi16_ZMM, &xs.s64->fpu_zmm16, 16 * sizeof(_STRUCT_ZMM_REG));
1036 bcopy_nochk(iavx->x_ZMM_Hi256, &xs.s64->fpu_zmmh0, 16 * sizeof(_STRUCT_YMM_REG));
1037 bcopy_nochk(iavx->x_YMM_Hi128, &xs.s64->fpu_ymmh0, 16 * sizeof(_STRUCT_XMM_REG));
1038 DBG_AVX512_STATE(iavx);
1039 break;
1040 case x86_AVX_STATE64:
1041 bcopy_nochk(iavx->x_YMM_Hi128, &xs.s64->fpu_ymmh0, 16 * sizeof(_STRUCT_XMM_REG));
1042 break;
7ddcb079 1043 }
5ba3f43e
A
1044 break;
1045 }
1046#endif
7ddcb079
A
1047 }
1048
0c530ab8 1049 ret = KERN_SUCCESS;
6601e61a 1050 }
0c530ab8 1051 simple_unlock(&pcb->lock);
21362eb3 1052
0c530ab8 1053 return ret;
6601e61a 1054}
21362eb3 1055
0c530ab8 1056
2d21ac55 1057
6601e61a 1058/*
0c530ab8
A
1059 * the child thread is 'stopped' with the thread
1060 * mutex held and is currently not known by anyone
1061 * so no way for fpu state to get manipulated by an
1062 * outside agency -> no need for pcb lock
6601e61a 1063 */
0c530ab8
A
1064
1065void
1066fpu_dup_fxstate(
1067 thread_t parent,
1068 thread_t child)
6601e61a 1069{
060df5ea
A
1070 struct x86_fx_thread_state *new_ifps = NULL;
1071 boolean_t intr;
0c530ab8 1072 pcb_t ppcb;
5ba3f43e 1073 xstate_t xstate = thread_xstate(parent);
21362eb3 1074
6d2010ae 1075 ppcb = THREAD_TO_PCB(parent);
21362eb3 1076
0c530ab8
A
1077 if (ppcb->ifps == NULL)
1078 return;
4452a7af 1079
6d2010ae 1080 if (child->machine.ifps)
0c530ab8 1081 panic("fpu_dup_fxstate: child's ifps non-null");
4452a7af 1082
5ba3f43e 1083 new_ifps = fp_state_alloc(xstate);
5d5c5d0d 1084
0c530ab8 1085 simple_lock(&ppcb->lock);
6601e61a 1086
0c530ab8 1087 if (ppcb->ifps != NULL) {
060df5ea 1088 struct x86_fx_thread_state *ifps = ppcb->ifps;
0c530ab8
A
1089 /*
1090 * Make sure we`ve got the latest fp state info
1091 */
39037602
A
1092 if (current_thread() == parent) {
1093 intr = ml_set_interrupts_enabled(FALSE);
1094 assert(current_thread() == parent);
1095 clear_ts();
1096 fp_save(parent);
1097 clear_fpu();
1098
1099 (void)ml_set_interrupts_enabled(intr);
1100 }
6601e61a 1101
060df5ea 1102 if (ifps->fp_valid) {
6d2010ae 1103 child->machine.ifps = new_ifps;
5ba3f43e 1104 child->machine.xstate = xstate;
060df5ea 1105 bcopy((char *)(ppcb->ifps),
5ba3f43e
A
1106 (char *)(child->machine.ifps),
1107 fp_state_size[xstate]);
0c530ab8 1108
2d21ac55
A
1109 /* Mark the new fp saved state as non-live. */
1110 /* Temporarily disabled: radar 4647827
1111 * new_ifps->fp_valid = TRUE;
1112 */
060df5ea 1113
0c530ab8
A
1114 /*
1115 * Clear any reserved bits in the MXCSR to prevent a GPF
1116 * when issuing an FXRSTOR.
1117 */
060df5ea 1118 new_ifps->fx_MXCSR &= mxcsr_capability_mask;
0c530ab8
A
1119 new_ifps = NULL;
1120 }
6601e61a 1121 }
0c530ab8 1122 simple_unlock(&ppcb->lock);
89b3af67 1123
0c530ab8 1124 if (new_ifps != NULL)
5ba3f43e 1125 fp_state_free(new_ifps, xstate);
6601e61a 1126}
4452a7af 1127
0c530ab8 1128
1c79356b
A
1129/*
1130 * Initialize FPU.
1131 *
1c79356b 1132 */
060df5ea 1133
1c79356b
A
1134void
1135fpinit(void)
1136{
1137 unsigned short control;
1138
1c79356b
A
1139 clear_ts();
1140 fninit();
1141 fnstcw(&control);
1142 control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
0c530ab8 1143 control |= (FPC_PC_64 | /* Set precision */
1c79356b
A
1144 FPC_RC_RN | /* round-to-nearest */
1145 FPC_ZE | /* Suppress zero-divide */
1146 FPC_OE | /* and overflow */
1147 FPC_UE | /* underflow */
1148 FPC_IE | /* Allow NaNQs and +-INF */
1149 FPC_DE | /* Allow denorms as operands */
1150 FPC_PE); /* No trap for precision loss */
1151 fldcw(control);
0c530ab8
A
1152
1153 /* Initialize SSE/SSE2 */
060df5ea 1154 __builtin_ia32_ldmxcsr(0x1f80);
b0d623f7 1155}
1c79356b
A
1156
1157/*
1158 * Coprocessor not present.
1159 */
1160
3e170ce0
A
1161uint64_t x86_isr_fp_simd_use;
1162
1c79356b
A
1163void
1164fpnoextflt(void)
1165{
0c530ab8 1166 boolean_t intr;
2d21ac55
A
1167 thread_t thr_act;
1168 pcb_t pcb;
060df5ea 1169 struct x86_fx_thread_state *ifps = 0;
5ba3f43e 1170 xstate_t xstate = current_xstate();
2d21ac55
A
1171
1172 thr_act = current_thread();
6d2010ae 1173 pcb = THREAD_TO_PCB(thr_act);
2d21ac55 1174
060df5ea 1175 if (pcb->ifps == 0 && !get_interrupt_level()) {
5ba3f43e 1176 ifps = fp_state_alloc(xstate);
060df5ea 1177 bcopy((char *)&initial_fp_state, (char *)ifps,
5ba3f43e 1178 fp_state_size[xstate]);
060df5ea 1179 if (!thread_is_64bit(thr_act)) {
5ba3f43e 1180 ifps->fp_save_layout = fpu_YMM_capable ? XSAVE32 : FXSAVE32;
060df5ea
A
1181 }
1182 else
5ba3f43e 1183 ifps->fp_save_layout = fpu_YMM_capable ? XSAVE64 : FXSAVE64;
060df5ea
A
1184 ifps->fp_valid = TRUE;
1185 }
0c530ab8
A
1186 intr = ml_set_interrupts_enabled(FALSE);
1187
1188 clear_ts(); /* Enable FPU use */
1189
6d2010ae 1190 if (__improbable(get_interrupt_level())) {
3e170ce0
A
1191 /* Track number of #DNA traps at interrupt context,
1192 * which is likely suboptimal. Racy, but good enough.
1193 */
1194 x86_isr_fp_simd_use++;
0c530ab8 1195 /*
3e170ce0
A
1196 * Save current FP/SIMD context if valid
1197 * Initialize live FP/SIMD registers
0c530ab8 1198 */
3e170ce0
A
1199 if (pcb->ifps) {
1200 fp_save(thr_act);
1201 }
0c530ab8
A
1202 fpinit();
1203 } else {
2d21ac55
A
1204 if (pcb->ifps == 0) {
1205 pcb->ifps = ifps;
5ba3f43e 1206 pcb->xstate = xstate;
2d21ac55
A
1207 ifps = 0;
1208 }
0c530ab8
A
1209 /*
1210 * Load this thread`s state into coprocessor live context.
1211 */
2d21ac55 1212 fp_load(thr_act);
0c530ab8 1213 }
0c530ab8 1214 (void)ml_set_interrupts_enabled(intr);
2d21ac55
A
1215
1216 if (ifps)
5ba3f43e 1217 fp_state_free(ifps, xstate);
1c79356b
A
1218}
1219
1220/*
1221 * FPU overran end of segment.
1222 * Re-initialize FPU. Floating point state is not valid.
1223 */
1224
1225void
1226fpextovrflt(void)
1227{
0c530ab8
A
1228 thread_t thr_act = current_thread();
1229 pcb_t pcb;
060df5ea 1230 struct x86_fx_thread_state *ifps;
0c530ab8 1231 boolean_t intr;
5ba3f43e 1232 xstate_t xstate = current_xstate();
0c530ab8
A
1233
1234 intr = ml_set_interrupts_enabled(FALSE);
1235
1236 if (get_interrupt_level())
2d21ac55 1237 panic("FPU segment overrun exception at interrupt context\n");
0c530ab8
A
1238 if (current_task() == kernel_task)
1239 panic("FPU segment overrun exception in kernel thread context\n");
1c79356b 1240
1c79356b
A
1241 /*
1242 * This is a non-recoverable error.
1243 * Invalidate the thread`s FPU state.
1244 */
6d2010ae 1245 pcb = THREAD_TO_PCB(thr_act);
1c79356b 1246 simple_lock(&pcb->lock);
0c530ab8
A
1247 ifps = pcb->ifps;
1248 pcb->ifps = 0;
1c79356b
A
1249 simple_unlock(&pcb->lock);
1250
1251 /*
1252 * Re-initialize the FPU.
1253 */
1254 clear_ts();
1255 fninit();
1256
1257 /*
1258 * And disable access.
1259 */
1260 clear_fpu();
1261
0c530ab8
A
1262 (void)ml_set_interrupts_enabled(intr);
1263
1c79356b 1264 if (ifps)
5ba3f43e 1265 fp_state_free(ifps, xstate);
1c79356b
A
1266
1267 /*
1268 * Raise exception.
1269 */
1270 i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
1271 /*NOTREACHED*/
1272}
1273
cc8bc92a
A
1274extern void fpxlog(int, uint32_t, uint32_t, uint32_t);
1275
1c79356b
A
1276/*
1277 * FPU error. Called by AST.
1278 */
1279
1280void
1281fpexterrflt(void)
1282{
0c530ab8 1283 thread_t thr_act = current_thread();
6d2010ae 1284 struct x86_fx_thread_state *ifps = thr_act->machine.ifps;
0c530ab8
A
1285 boolean_t intr;
1286
1287 intr = ml_set_interrupts_enabled(FALSE);
1288
1289 if (get_interrupt_level())
1290 panic("FPU error exception at interrupt context\n");
1291 if (current_task() == kernel_task)
1292 panic("FPU error exception in kernel thread context\n");
1c79356b 1293
1c79356b
A
1294 /*
1295 * Save the FPU state and turn off the FPU.
1296 */
1297 fp_save(thr_act);
1c79356b 1298
0c530ab8
A
1299 (void)ml_set_interrupts_enabled(intr);
1300
cc8bc92a
A
1301 const uint32_t mask = ifps->fx_control &
1302 (FPC_IM | FPC_DM | FPC_ZM | FPC_OM | FPC_UE | FPC_PE);
1303 const uint32_t xcpt = ~mask & (ifps->fx_status &
1304 (FPS_IE | FPS_DE | FPS_ZE | FPS_OE | FPS_UE | FPS_PE));
1305 fpxlog(EXC_I386_EXTERR, ifps->fx_status, ifps->fx_control, xcpt);
1c79356b
A
1306 /*
1307 * Raise FPU exception.
0c530ab8 1308 * Locking not needed on pcb->ifps,
1c79356b
A
1309 * since thread is running.
1310 */
1311 i386_exception(EXC_ARITHMETIC,
1312 EXC_I386_EXTERR,
060df5ea 1313 ifps->fx_status);
0c530ab8 1314
1c79356b
A
1315 /*NOTREACHED*/
1316}
1317
1318/*
1319 * Save FPU state.
1320 *
1321 * Locking not needed:
1322 * . if called from fpu_get_state, pcb already locked.
1323 * . if called from fpnoextflt or fp_intr, we are single-cpu
1324 * . otherwise, thread is running.
0c530ab8 1325 * N.B.: Must be called with interrupts disabled
1c79356b 1326 */
0c530ab8 1327
1c79356b
A
1328void
1329fp_save(
91447636 1330 thread_t thr_act)
1c79356b 1331{
6d2010ae 1332 pcb_t pcb = THREAD_TO_PCB(thr_act);
060df5ea 1333 struct x86_fx_thread_state *ifps = pcb->ifps;
0c530ab8 1334
060df5ea 1335 assert(ifps != 0);
1c79356b 1336 if (ifps != 0 && !ifps->fp_valid) {
0c530ab8
A
1337 assert((get_cr0() & CR0_TS) == 0);
1338 /* registers are in FPU */
1339 ifps->fp_valid = TRUE;
060df5ea 1340 fpu_store_registers(ifps, thread_is_64bit(thr_act));
1c79356b
A
1341 }
1342}
1343
1344/*
1345 * Restore FPU state from PCB.
1346 *
1347 * Locking not needed; always called on the current thread.
1348 */
1349
1350void
1351fp_load(
91447636 1352 thread_t thr_act)
1c79356b 1353{
6d2010ae 1354 pcb_t pcb = THREAD_TO_PCB(thr_act);
060df5ea 1355 struct x86_fx_thread_state *ifps = pcb->ifps;
0c530ab8 1356
060df5ea 1357 assert(ifps);
39236c6e
A
1358#if DEBUG
1359 if (ifps->fp_valid != FALSE && ifps->fp_valid != TRUE) {
1360 panic("fp_load() invalid fp_valid: %u, fp_save_layout: %u\n",
1361 ifps->fp_valid, ifps->fp_save_layout);
1362 }
1363#endif
060df5ea
A
1364
1365 if (ifps->fp_valid == FALSE) {
0c530ab8 1366 fpinit();
1c79356b 1367 } else {
060df5ea 1368 fpu_load_registers(ifps);
1c79356b
A
1369 }
1370 ifps->fp_valid = FALSE; /* in FPU */
1371}
1372
1c79356b 1373/*
0c530ab8
A
1374 * SSE arithmetic exception handling code.
1375 * Basically the same as the x87 exception handler with a different subtype
1c79356b
A
1376 */
1377
1378void
0c530ab8 1379fpSSEexterrflt(void)
1c79356b 1380{
0c530ab8 1381 thread_t thr_act = current_thread();
6d2010ae 1382 struct x86_fx_thread_state *ifps = thr_act->machine.ifps;
0c530ab8 1383 boolean_t intr;
4452a7af 1384
0c530ab8
A
1385 intr = ml_set_interrupts_enabled(FALSE);
1386
1387 if (get_interrupt_level())
1388 panic("SSE exception at interrupt context\n");
1389 if (current_task() == kernel_task)
1390 panic("SSE exception in kernel thread context\n");
1c79356b
A
1391
1392 /*
0c530ab8 1393 * Save the FPU state and turn off the FPU.
1c79356b 1394 */
1c79356b 1395 fp_save(thr_act);
1c79356b 1396
0c530ab8 1397 (void)ml_set_interrupts_enabled(intr);
1c79356b 1398 /*
0c530ab8
A
1399 * Raise FPU exception.
1400 * Locking not needed on pcb->ifps,
1401 * since thread is running.
1c79356b 1402 */
cc8bc92a
A
1403 const uint32_t mask = (ifps->fx_MXCSR >> 7) &
1404 (FPC_IM | FPC_DM | FPC_ZM | FPC_OM | FPC_UE | FPC_PE);
1405 const uint32_t xcpt = ~mask & (ifps->fx_MXCSR &
1406 (FPS_IE | FPS_DE | FPS_ZE | FPS_OE | FPS_UE | FPS_PE));
1407 fpxlog(EXC_I386_SSEEXTERR, ifps->fx_MXCSR, ifps->fx_MXCSR, xcpt);
fe8ab488 1408
0c530ab8
A
1409 i386_exception(EXC_ARITHMETIC,
1410 EXC_I386_SSEEXTERR,
060df5ea 1411 ifps->fx_MXCSR);
0c530ab8
A
1412 /*NOTREACHED*/
1413}
1414
5ba3f43e
A
1415
1416#if !defined(RC_HIDE_XNU_J137)
1417/*
1418 * If a thread is using an AVX-sized savearea:
1419 * - allocate a new AVX512-sized area,
1420 * - copy the 256-bit state into the 512-bit area,
1421 * - deallocate the smaller area
1422 */
1423static void
1424fpu_savearea_promote_avx512(thread_t thread)
1425{
cc8bc92a
A
1426 struct x86_avx_thread_state *ifps = NULL;
1427 struct x86_avx512_thread_state *ifps512 = NULL;
5ba3f43e 1428 pcb_t pcb = THREAD_TO_PCB(thread);
cc8bc92a 1429 boolean_t do_avx512_alloc = FALSE;
5ba3f43e
A
1430
1431 DBG("fpu_upgrade_savearea(%p)\n", thread);
cc8bc92a 1432
5ba3f43e 1433 simple_lock(&pcb->lock);
cc8bc92a 1434
5ba3f43e
A
1435 ifps = pcb->ifps;
1436 if (ifps == NULL) {
cc8bc92a 1437 pcb->xstate = AVX512;
5ba3f43e 1438 simple_unlock(&pcb->lock);
cc8bc92a
A
1439 if (thread != current_thread()) {
1440 /* nothing to be done */
1441
1442 return;
1443 }
1444 fpnoextflt();
5ba3f43e
A
1445 return;
1446 }
cc8bc92a
A
1447
1448 if (pcb->xstate != AVX512) {
1449 do_avx512_alloc = TRUE;
1450 }
1451 simple_unlock(&pcb->lock);
1452
1453 if (do_avx512_alloc == TRUE) {
1454 ifps512 = fp_state_alloc(AVX512);
1455 }
1456
1457 simple_lock(&pcb->lock);
5ba3f43e
A
1458 if (thread == current_thread()) {
1459 boolean_t intr;
1460
1461 intr = ml_set_interrupts_enabled(FALSE);
1462
1463 clear_ts();
1464 fp_save(thread);
1465 clear_fpu();
1466
1467 xsetbv(0, AVX512_XMASK);
1468
1469 (void)ml_set_interrupts_enabled(intr);
1470 }
1471 assert(ifps->fp.fp_valid);
1472
1473 /* Allocate an AVX512 savearea and copy AVX state into it */
cc8bc92a
A
1474 if (pcb->xstate != AVX512) {
1475 bcopy(ifps, ifps512, fp_state_size[AVX]);
1476 pcb->ifps = ifps512;
1477 pcb->xstate = AVX512;
1478 ifps512 = NULL;
1479 } else {
1480 ifps = NULL;
1481 }
1482 /* The PCB lock is redundant in some scenarios given the higher level
1483 * thread mutex, but its pre-emption disablement is relied upon here
1484 */
5ba3f43e 1485 simple_unlock(&pcb->lock);
cc8bc92a
A
1486
1487 if (ifps) {
1488 fp_state_free(ifps, AVX);
1489 }
1490 if (ifps512) {
1491 fp_state_free(ifps, AVX512);
1492 }
5ba3f43e
A
1493}
1494
1495/*
1496 * Upgrade the calling thread to AVX512.
1497 */
1498boolean_t
1499fpu_thread_promote_avx512(thread_t thread)
1500{
1501 task_t task = current_task();
1502
1503 if (thread != current_thread())
1504 return FALSE;
1505 if (!ml_fpu_avx512_enabled())
1506 return FALSE;
1507
1508 fpu_savearea_promote_avx512(thread);
1509
1510 /* Racy but the task's xstate is only a hint */
1511 task->xstate = AVX512;
1512
1513 return TRUE;
1514}
1515
1516
1517/*
1518 * Called from user_trap() when an invalid opcode fault is taken.
1519 * If the user is attempting an AVX512 instruction on a machine
1520 * that supports this, we switch the calling thread to use
1521 * a larger savearea, set its XCR0 bit mask to enable AVX512 and
1522 * return directly via thread_exception_return().
1523 * Otherwise simply return.
1524 */
cc8bc92a 1525#define MAX_X86_INSN_LENGTH (16)
5ba3f43e
A
1526void
1527fpUDflt(user_addr_t rip)
1528{
1529 uint8_t instruction_prefix;
1530 boolean_t is_AVX512_instruction = FALSE;
cc8bc92a 1531 user_addr_t original_rip = rip;
5ba3f43e 1532 do {
cc8bc92a
A
1533 /* TODO: as an optimisation, copy up to the lesser of the
1534 * next page boundary or maximal prefix length in one pass
1535 * rather than issue multiple copyins
1536 */
1537 if (copyin(rip, (char *) &instruction_prefix, 1)) {
5ba3f43e 1538 return;
cc8bc92a 1539 }
5ba3f43e
A
1540 DBG("fpUDflt(0x%016llx) prefix: 0x%x\n",
1541 rip, instruction_prefix);
cc8bc92a
A
1542 /* TODO: determine more specifically which prefixes
1543 * are sane possibilities for AVX512 insns
1544 */
5ba3f43e
A
1545 switch (instruction_prefix) {
1546 case 0x2E: /* CS segment override */
1547 case 0x36: /* SS segment override */
1548 case 0x3E: /* DS segment override */
1549 case 0x26: /* ES segment override */
1550 case 0x64: /* FS segment override */
1551 case 0x65: /* GS segment override */
cc8bc92a 1552 case 0x66: /* Operand-size override */
5ba3f43e
A
1553 case 0x67: /* address-size override */
1554 /* Skip optional prefixes */
1555 rip++;
cc8bc92a
A
1556 if ((rip - original_rip) > MAX_X86_INSN_LENGTH) {
1557 return;
1558 }
5ba3f43e
A
1559 break;
1560 case 0x62: /* EVEX */
1561 case 0xC5: /* VEX 2-byte */
1562 case 0xC4: /* VEX 3-byte */
1563 is_AVX512_instruction = TRUE;
1564 break;
1565 default:
1566 return;
1567 }
1568 } while (!is_AVX512_instruction);
1569
1570 /* Here if we detect attempted execution of an AVX512 instruction */
1571
1572 /*
cc8bc92a 1573 * Fail if this machine doesn't support AVX512
5ba3f43e 1574 */
cc8bc92a 1575 if (fpu_capability != AVX512)
5ba3f43e
A
1576 return;
1577
1578 assert(xgetbv(XCR0) == AVX_XMASK);
1579
1580 DBG("fpUDflt() switching xstate to AVX512\n");
1581 (void) fpu_thread_promote_avx512(current_thread());
1582
1583 thread_exception_return();
1584 /* NOT REACHED */
1585}
1586#endif /* !defined(RC_HIDE_XNU_J137) */
1587
0c530ab8
A
1588void
1589fp_setvalid(boolean_t value) {
1590 thread_t thr_act = current_thread();
6d2010ae 1591 struct x86_fx_thread_state *ifps = thr_act->machine.ifps;
0c530ab8
A
1592
1593 if (ifps) {
1594 ifps->fp_valid = value;
1595
060df5ea
A
1596 if (value == TRUE) {
1597 boolean_t istate = ml_set_interrupts_enabled(FALSE);
0c530ab8 1598 clear_fpu();
060df5ea
A
1599 ml_set_interrupts_enabled(istate);
1600 }
0c530ab8 1601 }
1c79356b 1602}
060df5ea 1603
316670eb 1604boolean_t
060df5ea 1605ml_fpu_avx_enabled(void) {
5ba3f43e
A
1606 return (fpu_capability >= AVX);
1607}
1608
1609#if !defined(RC_HIDE_XNU_J137)
1610boolean_t
1611ml_fpu_avx512_enabled(void) {
1612 return (fpu_capability == AVX512);
1613}
1614#endif
1615
1616static xstate_t
1617task_xstate(task_t task)
1618{
1619 if (task == TASK_NULL)
1620 return fpu_default;
1621 else
1622 return task->xstate;
1623}
1624
1625static xstate_t
1626thread_xstate(thread_t thread)
1627{
1628 xstate_t xs = THREAD_TO_PCB(thread)->xstate;
1629 if (xs == UNDEFINED)
1630 return task_xstate(thread->task);
1631 else
1632 return xs;
1633}
1634
1635xstate_t
1636current_xstate(void)
1637{
1638 return thread_xstate(current_thread());
1639}
1640
1641/*
1642 * Called when exec'ing between bitnesses.
1643 * If valid FPU state exists, adjust the layout.
1644 */
1645void
1646fpu_switch_addrmode(thread_t thread, boolean_t is_64bit)
1647{
1648 struct x86_fx_thread_state *ifps = thread->machine.ifps;
1649
1650 if (ifps && ifps->fp_valid) {
1651 if (thread_xstate(thread) == FP) {
1652 ifps->fp_save_layout = is_64bit ? FXSAVE64 : FXSAVE32;
1653 } else {
1654 ifps->fp_save_layout = is_64bit ? XSAVE64 : XSAVE32;
1655 }
1656 }
060df5ea 1657}