9596 Initial xsave xstate_bv should not include all features
[unleashed.git] / usr / src / uts / intel / ia32 / os / fpu.c
blobc307c97957026ffa42764845df6f3a9d18784b9e
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2018, Joyent, Inc.
26 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
28 /* All Rights Reserved */
30 /* Copyright (c) 1987, 1988 Microsoft Corporation */
31 /* All Rights Reserved */
34 * Copyright (c) 2009, Intel Corporation.
35 * All rights reserved.
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/signal.h>
41 #include <sys/regset.h>
42 #include <sys/privregs.h>
43 #include <sys/psw.h>
44 #include <sys/trap.h>
45 #include <sys/fault.h>
46 #include <sys/systm.h>
47 #include <sys/user.h>
48 #include <sys/file.h>
49 #include <sys/proc.h>
50 #include <sys/pcb.h>
51 #include <sys/lwp.h>
52 #include <sys/cpuvar.h>
53 #include <sys/thread.h>
54 #include <sys/disp.h>
55 #include <sys/fp.h>
56 #include <sys/siginfo.h>
57 #include <sys/archsystm.h>
58 #include <sys/kmem.h>
59 #include <sys/debug.h>
60 #include <sys/x86_archext.h>
61 #include <sys/sysmacros.h>
62 #include <sys/cmn_err.h>
64 kmem_cache_t *fpsave_cachep;
66 /* Legacy fxsave layout + xsave header + ymm */
67 #define AVX_XSAVE_SIZE (512 + 64 + 256)
69 /*CSTYLED*/
70 #pragma align 16 (sse_initial)
73 * Initial kfpu state for SSE/SSE2 used by fpinit()
75 const struct fxsave_state sse_initial = {
76 FPU_CW_INIT, /* fx_fcw */
77 0, /* fx_fsw */
78 0, /* fx_fctw */
79 0, /* fx_fop */
80 #if defined(__amd64)
81 0, /* fx_rip */
82 0, /* fx_rdp */
83 #else
84 0, /* fx_eip */
85 0, /* fx_cs */
86 0, /* __fx_ign0 */
87 0, /* fx_dp */
88 0, /* fx_ds */
89 0, /* __fx_ign1 */
90 #endif /* __amd64 */
91 SSE_MXCSR_INIT /* fx_mxcsr */
92 /* rest of structure is zero */
95 /*CSTYLED*/
96 #pragma align 64 (avx_initial)
99 * Initial kfpu state for AVX used by fpinit()
101 const struct xsave_state avx_initial = {
103 * The definition below needs to be identical with sse_initial
104 * defined above.
107 FPU_CW_INIT, /* fx_fcw */
108 0, /* fx_fsw */
109 0, /* fx_fctw */
110 0, /* fx_fop */
111 #if defined(__amd64)
112 0, /* fx_rip */
113 0, /* fx_rdp */
114 #else
115 0, /* fx_eip */
116 0, /* fx_cs */
117 0, /* __fx_ign0 */
118 0, /* fx_dp */
119 0, /* fx_ds */
120 0, /* __fx_ign1 */
121 #endif /* __amd64 */
122 SSE_MXCSR_INIT /* fx_mxcsr */
123 /* rest of structure is zero */
126 * bit0 = 1 for XSTATE_BV to indicate that legacy fields are valid,
127 * and CPU should initialize XMM/YMM.
130 0 /* xs_xcomp_bv */
131 /* rest of structure is zero */
135 * mxcsr_mask value (possibly reset in fpu_probe); used to avoid
136 * the #gp exception caused by setting unsupported bits in the
137 * MXCSR register
139 uint32_t sse_mxcsr_mask = SSE_MXCSR_MASK_DEFAULT;
142 * Initial kfpu state for x87 used by fpinit()
144 const struct fnsave_state x87_initial = {
145 FPU_CW_INIT, /* f_fcw */
146 0, /* __f_ign0 */
147 0, /* f_fsw */
148 0, /* __f_ign1 */
149 0xffff, /* f_ftw */
150 /* rest of structure is zero */
153 #if defined(__amd64)
155 * This vector is patched to xsave_ctxt() if we discover we have an
156 * XSAVE-capable chip in fpu_probe.
158 void (*fpsave_ctxt)(void *) = fpxsave_ctxt;
159 #elif defined(__i386)
161 * This vector is patched to fpxsave_ctxt() if we discover we have an
162 * SSE-capable chip in fpu_probe(). It is patched to xsave_ctxt
163 * if we discover we have an XSAVE-capable chip in fpu_probe.
165 void (*fpsave_ctxt)(void *) = fpnsave_ctxt;
166 #endif
169 * This function pointer is changed to xsaveopt if the CPU is xsaveopt capable.
171 void (*xsavep)(struct xsave_state *, uint64_t) = xsave;
173 static int fpe_sicode(uint_t);
174 static int fpe_simd_sicode(uint_t);
177 * Copy the state of parent lwp's floating point context into the new lwp.
178 * Invoked for both fork() and lwp_create().
180 * Note that we inherit -only- the control state (e.g. exception masks,
181 * rounding, precision control, etc.); the FPU registers are otherwise
182 * reset to their initial state.
184 static void
185 fp_new_lwp(kthread_id_t t, kthread_id_t ct)
187 struct fpu_ctx *fp; /* parent fpu context */
188 struct fpu_ctx *cfp; /* new fpu context */
189 struct fxsave_state *fx, *cfx;
190 #if defined(__i386)
191 struct fnsave_state *fn, *cfn;
192 #endif
193 struct xsave_state *cxs;
195 ASSERT(fp_kind != FP_NO);
197 fp = &t->t_lwp->lwp_pcb.pcb_fpu;
198 cfp = &ct->t_lwp->lwp_pcb.pcb_fpu;
201 * If the parent FPU state is still in the FPU hw then save it;
202 * conveniently, fp_save() already does this for us nicely.
204 fp_save(fp);
206 cfp->fpu_flags = FPU_EN | FPU_VALID;
207 cfp->fpu_regs.kfpu_status = 0;
208 cfp->fpu_regs.kfpu_xstatus = 0;
210 switch (fp_save_mech) {
211 #if defined(__i386)
212 case FP_FNSAVE:
213 fn = fp->fpu_regs.kfpu_u.kfpu_fn;
214 cfn = cfp->fpu_regs.kfpu_u.kfpu_fn;
215 bcopy(&x87_initial, cfn, sizeof (*cfn));
216 cfn->f_fcw = fn->f_fcw;
217 break;
218 #endif
219 case FP_FXSAVE:
220 fx = fp->fpu_regs.kfpu_u.kfpu_fx;
221 cfx = cfp->fpu_regs.kfpu_u.kfpu_fx;
222 bcopy(&sse_initial, cfx, sizeof (*cfx));
223 cfx->fx_mxcsr = fx->fx_mxcsr & ~SSE_MXCSR_EFLAGS;
224 cfx->fx_fcw = fx->fx_fcw;
225 break;
227 case FP_XSAVE:
228 cfp->fpu_xsave_mask = fp->fpu_xsave_mask;
230 VERIFY(fp->fpu_regs.kfpu_u.kfpu_xs != NULL);
232 fx = &fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave;
233 cxs = cfp->fpu_regs.kfpu_u.kfpu_xs;
234 cfx = &cxs->xs_fxsave;
236 bcopy(&avx_initial, cxs, sizeof (*cxs));
237 cfx->fx_mxcsr = fx->fx_mxcsr & ~SSE_MXCSR_EFLAGS;
238 cfx->fx_fcw = fx->fx_fcw;
239 cxs->xs_xstate_bv |= (get_xcr(XFEATURE_ENABLED_MASK) &
240 XFEATURE_FP_INITIAL);
241 break;
242 default:
243 panic("Invalid fp_save_mech");
244 /*NOTREACHED*/
247 installctx(ct, cfp,
248 fpsave_ctxt, NULL, fp_new_lwp, fp_new_lwp, NULL, fp_free);
250 * Now, when the new lwp starts running, it will take a trap
251 * that will be handled inline in the trap table to cause
252 * the appropriate f*rstor instruction to load the save area we
253 * constructed above directly into the hardware.
258 * Free any state associated with floating point context.
259 * Fp_free can be called in three cases:
260 * 1) from reaper -> thread_free -> freectx-> fp_free
261 * fp context belongs to a thread on deathrow
262 * nothing to do, thread will never be resumed
263 * thread calling ctxfree is reaper
265 * 2) from exec -> freectx -> fp_free
266 * fp context belongs to the current thread
267 * must disable fpu, thread calling ctxfree is curthread
269 * 3) from restorecontext -> setfpregs -> fp_free
270 * we have a modified context in the memory (lwp->pcb_fpu)
271 * disable fpu and release the fp context for the CPU
274 /*ARGSUSED*/
275 void
276 fp_free(struct fpu_ctx *fp, int isexec)
278 ASSERT(fp_kind != FP_NO);
280 if (fp->fpu_flags & FPU_VALID)
281 return;
283 kpreempt_disable();
285 * We want to do fpsave rather than fpdisable so that we can
286 * keep the fpu_flags as FPU_VALID tracking the CR0_TS bit
288 fp->fpu_flags |= FPU_VALID;
289 /* If for current thread disable FP to track FPU_VALID */
290 if (curthread->t_lwp && fp == &curthread->t_lwp->lwp_pcb.pcb_fpu) {
291 /* Clear errors if any to prevent frstor from complaining */
292 (void) fperr_reset();
293 if (fp_kind & __FP_SSE)
294 (void) fpxerr_reset();
295 fpdisable();
297 kpreempt_enable();
301 * Store the floating point state and disable the floating point unit.
303 void
304 fp_save(struct fpu_ctx *fp)
306 ASSERT(fp_kind != FP_NO);
308 kpreempt_disable();
309 if (!fp || fp->fpu_flags & FPU_VALID) {
310 kpreempt_enable();
311 return;
313 ASSERT(curthread->t_lwp && fp == &curthread->t_lwp->lwp_pcb.pcb_fpu);
315 switch (fp_save_mech) {
316 #if defined(__i386)
317 case FP_FNSAVE:
318 fpsave(fp->fpu_regs.kfpu_u.kfpu_fn);
319 break;
320 #endif
321 case FP_FXSAVE:
322 fpxsave(fp->fpu_regs.kfpu_u.kfpu_fx);
323 break;
325 case FP_XSAVE:
326 xsavep(fp->fpu_regs.kfpu_u.kfpu_xs, fp->fpu_xsave_mask);
327 break;
328 default:
329 panic("Invalid fp_save_mech");
330 /*NOTREACHED*/
333 fp->fpu_flags |= FPU_VALID;
334 kpreempt_enable();
338 * Restore the FPU context for the thread:
339 * The possibilities are:
340 * 1. No active FPU context: Load the new context into the FPU hw
341 * and enable the FPU.
343 void
344 fp_restore(struct fpu_ctx *fp)
346 switch (fp_save_mech) {
347 #if defined(__i386)
348 case FP_FNSAVE:
349 fprestore(fp->fpu_regs.kfpu_u.kfpu_fn);
350 break;
351 #endif
352 case FP_FXSAVE:
353 fpxrestore(fp->fpu_regs.kfpu_u.kfpu_fx);
354 break;
356 case FP_XSAVE:
357 xrestore(fp->fpu_regs.kfpu_u.kfpu_xs, fp->fpu_xsave_mask);
358 break;
359 default:
360 panic("Invalid fp_save_mech");
361 /*NOTREACHED*/
364 fp->fpu_flags &= ~FPU_VALID;
369 * Seeds the initial state for the current thread. The possibilities are:
370 * 1. Another process has modified the FPU state before we have done any
371 * initialization: Load the FPU state from the LWP state.
372 * 2. The FPU state has not been externally modified: Load a clean state.
374 static void
375 fp_seed(void)
377 struct fpu_ctx *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu;
379 ASSERT(curthread->t_preempt >= 1);
380 ASSERT((fp->fpu_flags & FPU_EN) == 0);
383 * Always initialize a new context and initialize the hardware.
385 if (fp_save_mech == FP_XSAVE) {
386 fp->fpu_xsave_mask = XFEATURE_FP_ALL;
389 installctx(curthread, fp,
390 fpsave_ctxt, NULL, fp_new_lwp, fp_new_lwp, NULL, fp_free);
391 fpinit();
394 * If FPU_VALID is set, it means someone has modified registers via
395 * /proc. In this case, restore the current lwp's state.
397 if (fp->fpu_flags & FPU_VALID)
398 fp_restore(fp);
400 ASSERT((fp->fpu_flags & FPU_VALID) == 0);
401 fp->fpu_flags = FPU_EN;
405 * When using xsave/xrstor, these three functions are used by the lwp code to
406 * manage the memory for the xsave area.
408 void
409 fp_lwp_init(struct _klwp *lwp)
411 struct fpu_ctx *fp = &lwp->lwp_pcb.pcb_fpu;
414 * We keep a copy of the pointer in lwp_fpu so that we can restore the
415 * value in forklwp() after we duplicate the parent's LWP state.
417 lwp->lwp_fpu = fp->fpu_regs.kfpu_u.kfpu_generic =
418 kmem_cache_alloc(fpsave_cachep, KM_SLEEP);
420 if (fp_save_mech == FP_XSAVE) {
423 * We bzero since the fpinit() code path will only
424 * partially initialize the xsave area using avx_inital.
426 ASSERT(cpuid_get_xsave_size() >= sizeof (struct xsave_state));
427 bzero(fp->fpu_regs.kfpu_u.kfpu_xs, cpuid_get_xsave_size());
431 void
432 fp_lwp_cleanup(struct _klwp *lwp)
434 struct fpu_ctx *fp = &lwp->lwp_pcb.pcb_fpu;
436 if (fp->fpu_regs.kfpu_u.kfpu_generic != NULL) {
437 kmem_cache_free(fpsave_cachep,
438 fp->fpu_regs.kfpu_u.kfpu_generic);
439 lwp->lwp_fpu = fp->fpu_regs.kfpu_u.kfpu_generic = NULL;
444 * Called during the process of forklwp(). The kfpu_u pointer will have been
445 * overwritten while copying the parent's LWP structure. We have a valid copy
446 * stashed in the child's lwp_fpu which we use to restore the correct value.
448 void
449 fp_lwp_dup(struct _klwp *lwp)
451 void *xp = lwp->lwp_fpu;
452 size_t sz;
454 switch (fp_save_mech) {
455 #if defined(__i386)
456 case FP_FNSAVE:
457 sz = sizeof (struct fnsave_state);
458 break;
459 #endif
460 case FP_FXSAVE:
461 sz = sizeof (struct fxsave_state);
462 break;
463 case FP_XSAVE:
464 sz = cpuid_get_xsave_size();
465 break;
466 default:
467 panic("Invalid fp_save_mech");
468 /*NOTREACHED*/
471 /* copy the parent's values into the new lwp's struct */
472 bcopy(lwp->lwp_pcb.pcb_fpu.fpu_regs.kfpu_u.kfpu_generic, xp, sz);
473 /* now restore the pointer */
474 lwp->lwp_pcb.pcb_fpu.fpu_regs.kfpu_u.kfpu_generic = xp;
479 * This routine is called from trap() when User thread takes No Extension
480 * Fault. The possiblities are:
481 * 1. User thread has executed a FP instruction for the first time.
482 * Save current FPU context if any. Initialize FPU, setup FPU
483 * context for the thread and enable FP hw.
484 * 2. Thread's pcb has a valid FPU state: Restore the FPU state and
485 * enable FP hw.
487 * Note that case #2 is inlined in the trap table.
490 fpnoextflt(struct regs *rp)
492 struct fpu_ctx *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu;
494 #if !defined(__lint)
495 ASSERT(sizeof (struct fxsave_state) == 512 &&
496 sizeof (struct fnsave_state) == 108);
497 ASSERT((offsetof(struct fxsave_state, fx_xmm[0]) & 0xf) == 0);
499 ASSERT(sizeof (struct xsave_state) >= AVX_XSAVE_SIZE);
501 #if defined(__i386)
502 ASSERT(sizeof (struct _fpu) == sizeof (struct __old_fpu));
503 #endif /* __i386 */
504 #endif /* !__lint */
506 kpreempt_disable();
508 * Now we can enable the interrupts.
509 * (NOTE: fp-no-coprocessor comes thru interrupt gate)
511 sti();
513 if (!fpu_exists) { /* check for FPU hw exists */
514 if (fp_kind == FP_NO) {
515 uint32_t inst;
518 * When the system has no floating point support,
519 * i.e. no FP hardware and no emulator, skip the
520 * two kinds of FP instruction that occur in
521 * fpstart. Allows processes that do no real FP
522 * to run normally.
524 if (fuword32((void *)rp->r_pc, &inst) != -1 &&
525 ((inst & 0xFFFF) == 0x7dd9 ||
526 (inst & 0xFFFF) == 0x6dd9)) {
527 rp->r_pc += 3;
528 kpreempt_enable();
529 return (0);
534 * If we have neither a processor extension nor
535 * an emulator, kill the process OR panic the kernel.
537 kpreempt_enable();
538 return (1); /* error */
541 #if !defined(__xpv) /* XXPV Is this ifdef needed now? */
543 * A paranoid cross-check: for the SSE case, ensure that %cr4 is
544 * configured to enable fully fledged (%xmm) fxsave/fxrestor on
545 * this CPU. For the non-SSE case, ensure that it isn't.
547 ASSERT(((fp_kind & __FP_SSE) &&
548 (getcr4() & CR4_OSFXSR) == CR4_OSFXSR) ||
549 (!(fp_kind & __FP_SSE) &&
550 (getcr4() & (CR4_OSXMMEXCPT|CR4_OSFXSR)) == 0));
551 #endif
553 if (fp->fpu_flags & FPU_EN) {
554 /* case 2 */
555 fp_restore(fp);
556 } else {
557 /* case 1 */
558 fp_seed();
560 kpreempt_enable();
561 return (0);
566 * Handle a processor extension overrun fault
567 * Returns non zero for error.
569 * XXX Shouldn't this just be abolished given that we're not supporting
570 * anything prior to Pentium?
573 /* ARGSUSED */
575 fpextovrflt(struct regs *rp)
577 #if !defined(__xpv) /* XXPV Do we need this ifdef either */
578 ulong_t cur_cr0;
580 ASSERT(fp_kind != FP_NO);
582 cur_cr0 = getcr0();
583 fpinit(); /* initialize the FPU hardware */
584 setcr0(cur_cr0);
585 #endif
586 sti();
587 return (1); /* error, send SIGSEGV signal to the thread */
591 * Handle a processor extension error fault
592 * Returns non zero for error.
595 /*ARGSUSED*/
597 fpexterrflt(struct regs *rp)
599 uint32_t fpcw, fpsw;
600 fpu_ctx_t *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu;
602 ASSERT(fp_kind != FP_NO);
605 * Now we can enable the interrupts.
606 * (NOTE: x87 fp exceptions come thru interrupt gate)
608 sti();
610 if (!fpu_exists)
611 return (FPE_FLTINV);
614 * Do an unconditional save of the FP state. If it's dirty (TS=0),
615 * it'll be saved into the fpu context area passed in (that of the
616 * current thread). If it's not dirty (it may not be, due to
617 * an intervening save due to a context switch between the sti(),
618 * above and here, then it's safe to just use the stored values in
619 * the context save area to determine the cause of the fault.
621 fp_save(fp);
623 /* clear exception flags in saved state, as if by fnclex */
624 switch (fp_save_mech) {
625 #if defined(__i386)
626 case FP_FNSAVE:
627 fpsw = fp->fpu_regs.kfpu_u.kfpu_fn->f_fsw;
628 fpcw = fp->fpu_regs.kfpu_u.kfpu_fn->f_fcw;
629 fp->fpu_regs.kfpu_u.kfpu_fn->f_fsw &= ~FPS_SW_EFLAGS;
630 break;
631 #endif
633 case FP_FXSAVE:
634 fpsw = fp->fpu_regs.kfpu_u.kfpu_fx->fx_fsw;
635 fpcw = fp->fpu_regs.kfpu_u.kfpu_fx->fx_fcw;
636 fp->fpu_regs.kfpu_u.kfpu_fx->fx_fsw &= ~FPS_SW_EFLAGS;
637 break;
639 case FP_XSAVE:
640 fpsw = fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave.fx_fsw;
641 fpcw = fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave.fx_fcw;
642 fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave.fx_fsw &= ~FPS_SW_EFLAGS;
644 * Always set LEGACY_FP as it may have been cleared by XSAVE
645 * instruction
647 fp->fpu_regs.kfpu_u.kfpu_xs->xs_xstate_bv |= XFEATURE_LEGACY_FP;
648 break;
649 default:
650 panic("Invalid fp_save_mech");
651 /*NOTREACHED*/
654 fp->fpu_regs.kfpu_status = fpsw;
656 if ((fpsw & FPS_ES) == 0)
657 return (0); /* No exception */
660 * "and" the exception flags with the complement of the mask
661 * bits to determine which exception occurred
663 return (fpe_sicode(fpsw & ~fpcw & 0x3f));
667 * Handle an SSE/SSE2 precise exception.
668 * Returns a non-zero sicode for error.
670 /*ARGSUSED*/
672 fpsimderrflt(struct regs *rp)
674 uint32_t mxcsr, xmask;
675 fpu_ctx_t *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu;
677 ASSERT(fp_kind & __FP_SSE);
680 * NOTE: Interrupts are disabled during execution of this
681 * function. They are enabled by the caller in trap.c.
685 * The only way we could have gotten here if there is no FP unit
686 * is via a user executing an INT $19 instruction, so there is
687 * no fault in that case.
689 if (!fpu_exists)
690 return (0);
693 * Do an unconditional save of the FP state. If it's dirty (TS=0),
694 * it'll be saved into the fpu context area passed in (that of the
695 * current thread). If it's not dirty, then it's safe to just use
696 * the stored values in the context save area to determine the
697 * cause of the fault.
699 fp_save(fp); /* save the FPU state */
701 if (fp_save_mech == FP_XSAVE) {
702 mxcsr = fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave.fx_mxcsr;
703 fp->fpu_regs.kfpu_status =
704 fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave.fx_fsw;
705 } else {
706 mxcsr = fp->fpu_regs.kfpu_u.kfpu_fx->fx_mxcsr;
707 fp->fpu_regs.kfpu_status = fp->fpu_regs.kfpu_u.kfpu_fx->fx_fsw;
709 fp->fpu_regs.kfpu_xstatus = mxcsr;
712 * compute the mask that determines which conditions can cause
713 * a #xm exception, and use this to clean the status bits so that
714 * we can identify the true cause of this one.
716 xmask = (mxcsr >> 7) & SSE_MXCSR_EFLAGS;
717 return (fpe_simd_sicode((mxcsr & SSE_MXCSR_EFLAGS) & ~xmask));
721 * In the unlikely event that someone is relying on this subcode being
722 * FPE_FLTILL for denormalize exceptions, it can always be patched back
723 * again to restore old behaviour.
725 int fpe_fltden = FPE_FLTDEN;
728 * Map from the FPU status word to the FP exception si_code.
730 static int
731 fpe_sicode(uint_t sw)
733 if (sw & FPS_IE)
734 return (FPE_FLTINV);
735 if (sw & FPS_ZE)
736 return (FPE_FLTDIV);
737 if (sw & FPS_DE)
738 return (fpe_fltden);
739 if (sw & FPS_OE)
740 return (FPE_FLTOVF);
741 if (sw & FPS_UE)
742 return (FPE_FLTUND);
743 if (sw & FPS_PE)
744 return (FPE_FLTRES);
745 return (FPE_FLTINV); /* default si_code for other exceptions */
749 * Map from the SSE status word to the FP exception si_code.
751 static int
752 fpe_simd_sicode(uint_t sw)
754 if (sw & SSE_IE)
755 return (FPE_FLTINV);
756 if (sw & SSE_ZE)
757 return (FPE_FLTDIV);
758 if (sw & SSE_DE)
759 return (FPE_FLTDEN);
760 if (sw & SSE_OE)
761 return (FPE_FLTOVF);
762 if (sw & SSE_UE)
763 return (FPE_FLTUND);
764 if (sw & SSE_PE)
765 return (FPE_FLTRES);
766 return (FPE_FLTINV); /* default si_code for other exceptions */
770 * This routine is invoked as part of libc's __fpstart implementation
771 * via sysi86(2).
773 * It may be called -before- any context has been assigned in which case
774 * we try and avoid touching the hardware. Or it may be invoked well
775 * after the context has been assigned and fiddled with, in which case
776 * just tweak it directly.
778 void
779 fpsetcw(uint16_t fcw, uint32_t mxcsr)
781 struct fpu_ctx *fp = &curthread->t_lwp->lwp_pcb.pcb_fpu;
782 struct fxsave_state *fx;
784 if (!fpu_exists || fp_kind == FP_NO)
785 return;
787 if ((fp->fpu_flags & FPU_EN) == 0) {
788 if (fcw == FPU_CW_INIT && mxcsr == SSE_MXCSR_INIT) {
790 * Common case. Floating point unit not yet
791 * enabled, and kernel already intends to initialize
792 * the hardware the way the caller wants.
794 return;
797 * Hmm. Userland wants a different default.
798 * Do a fake "first trap" to establish the context, then
799 * handle as if we already had a context before we came in.
801 kpreempt_disable();
802 fp_seed();
803 kpreempt_enable();
807 * Ensure that the current hardware state is flushed back to the
808 * pcb, then modify that copy. Next use of the fp will
809 * restore the context.
811 fp_save(fp);
813 switch (fp_save_mech) {
814 #if defined(__i386)
815 case FP_FNSAVE:
816 fp->fpu_regs.kfpu_u.kfpu_fn->f_fcw = fcw;
817 break;
818 #endif
819 case FP_FXSAVE:
820 fx = fp->fpu_regs.kfpu_u.kfpu_fx;
821 fx->fx_fcw = fcw;
822 fx->fx_mxcsr = sse_mxcsr_mask & mxcsr;
823 break;
825 case FP_XSAVE:
826 fx = &fp->fpu_regs.kfpu_u.kfpu_xs->xs_fxsave;
827 fx->fx_fcw = fcw;
828 fx->fx_mxcsr = sse_mxcsr_mask & mxcsr;
830 * Always set LEGACY_FP as it may have been cleared by XSAVE
831 * instruction
833 fp->fpu_regs.kfpu_u.kfpu_xs->xs_xstate_bv |= XFEATURE_LEGACY_FP;
834 break;
835 default:
836 panic("Invalid fp_save_mech");
837 /*NOTREACHED*/