10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53446Smrj * Common Development and Distribution License (the "License"). 63446Smrj * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 270Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 280Sstevel@tonic-gate /* All Rights Reserved */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate /* Copyright (c) 1987, 1988 Microsoft Corporation */ 310Sstevel@tonic-gate /* All Rights Reserved */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 340Sstevel@tonic-gate 350Sstevel@tonic-gate #include <sys/types.h> 360Sstevel@tonic-gate #include <sys/param.h> 370Sstevel@tonic-gate #include <sys/signal.h> 380Sstevel@tonic-gate #include <sys/regset.h> 390Sstevel@tonic-gate #include <sys/privregs.h> 400Sstevel@tonic-gate #include <sys/psw.h> 410Sstevel@tonic-gate #include <sys/trap.h> 420Sstevel@tonic-gate #include <sys/fault.h> 430Sstevel@tonic-gate #include <sys/systm.h> 440Sstevel@tonic-gate #include <sys/user.h> 450Sstevel@tonic-gate #include <sys/file.h> 460Sstevel@tonic-gate #include <sys/proc.h> 470Sstevel@tonic-gate #include <sys/pcb.h> 480Sstevel@tonic-gate #include <sys/lwp.h> 490Sstevel@tonic-gate #include <sys/cpuvar.h> 500Sstevel@tonic-gate #include <sys/thread.h> 510Sstevel@tonic-gate #include <sys/disp.h> 520Sstevel@tonic-gate #include <sys/fp.h> 530Sstevel@tonic-gate #include <sys/siginfo.h> 540Sstevel@tonic-gate #include <sys/archsystm.h> 550Sstevel@tonic-gate #include <sys/kmem.h> 560Sstevel@tonic-gate #include <sys/debug.h> 570Sstevel@tonic-gate #include <sys/x86_archext.h> 580Sstevel@tonic-gate #include <sys/sysmacros.h> 590Sstevel@tonic-gate 600Sstevel@tonic-gate /*CSTYLED*/ 610Sstevel@tonic-gate #pragma align 16 (sse_initial) 620Sstevel@tonic-gate 630Sstevel@tonic-gate /* 640Sstevel@tonic-gate * Initial kfpu state for SSE/SSE2 used by fpinit() 650Sstevel@tonic-gate */ 660Sstevel@tonic-gate const struct fxsave_state sse_initial = { 670Sstevel@tonic-gate FPU_CW_INIT, /* fx_fcw */ 680Sstevel@tonic-gate 0, /* fx_fsw */ 690Sstevel@tonic-gate 0, /* fx_fctw */ 700Sstevel@tonic-gate 0, /* fx_fop */ 710Sstevel@tonic-gate #if defined(__amd64) 720Sstevel@tonic-gate 0, /* fx_rip */ 730Sstevel@tonic-gate 0, /* fx_rdp */ 740Sstevel@tonic-gate #else 750Sstevel@tonic-gate 0, /* fx_eip */ 760Sstevel@tonic-gate 0, /* fx_cs */ 770Sstevel@tonic-gate 0, /* __fx_ign0 */ 780Sstevel@tonic-gate 0, /* fx_dp */ 790Sstevel@tonic-gate 0, /* fx_ds */ 800Sstevel@tonic-gate 0, /* __fx_ign1 */ 810Sstevel@tonic-gate #endif /* __amd64 */ 820Sstevel@tonic-gate SSE_MXCSR_INIT /* fx_mxcsr */ 830Sstevel@tonic-gate /* rest of structure is zero */ 840Sstevel@tonic-gate }; 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * mxcsr_mask value (possibly reset in fpu_probe); used to avoid 880Sstevel@tonic-gate * the #gp exception caused by setting unsupported bits in the 890Sstevel@tonic-gate * MXCSR register 900Sstevel@tonic-gate */ 910Sstevel@tonic-gate uint32_t sse_mxcsr_mask = SSE_MXCSR_MASK_DEFAULT; 920Sstevel@tonic-gate 930Sstevel@tonic-gate /* 940Sstevel@tonic-gate * Initial kfpu state for x87 used by fpinit() 950Sstevel@tonic-gate */ 960Sstevel@tonic-gate const struct fnsave_state x87_initial = { 970Sstevel@tonic-gate FPU_CW_INIT, /* f_fcw */ 980Sstevel@tonic-gate 0, /* __f_ign0 */ 990Sstevel@tonic-gate 0, /* f_fsw */ 1000Sstevel@tonic-gate 0, /* __f_ign1 */ 1010Sstevel@tonic-gate 0xffff, /* f_ftw */ 1020Sstevel@tonic-gate /* rest of structure is zero */ 1030Sstevel@tonic-gate }; 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate #if defined(__amd64) 1063446Smrj #define fpsave_ctxt fpxsave_ctxt 1070Sstevel@tonic-gate #elif defined(__i386) 1080Sstevel@tonic-gate /* 1093446Smrj * This vector is patched to fpxsave_ctxt() if we discover 1100Sstevel@tonic-gate * we have an SSE-capable chip in fpu_probe(). 1110Sstevel@tonic-gate */ 1123446Smrj void (*fpsave_ctxt)(void *) = fpnsave_ctxt; 1130Sstevel@tonic-gate #endif 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate static int fpe_sicode(uint_t); 1160Sstevel@tonic-gate static int fpe_simd_sicode(uint_t); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 1190Sstevel@tonic-gate * Copy the state of parent lwp's floating point context into the new lwp. 1200Sstevel@tonic-gate * Invoked for both fork() and lwp_create(). 1210Sstevel@tonic-gate * 1220Sstevel@tonic-gate * Note that we inherit -only- the control state (e.g. exception masks, 1230Sstevel@tonic-gate * rounding, precision control, etc.); the FPU registers are otherwise 1240Sstevel@tonic-gate * reset to their initial state. 1250Sstevel@tonic-gate */ 1260Sstevel@tonic-gate static void 1270Sstevel@tonic-gate fp_new_lwp(kthread_id_t t, kthread_id_t ct) 1280Sstevel@tonic-gate { 1290Sstevel@tonic-gate struct fpu_ctx *fp; /* parent fpu context */ 1300Sstevel@tonic-gate struct fpu_ctx *cfp; /* new fpu context */ 1310Sstevel@tonic-gate struct fxsave_state *fx, *cfx; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate ASSERT(fp_kind != FP_NO); 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate fp = &t->t_lwp->lwp_pcb.pcb_fpu; 1360Sstevel@tonic-gate cfp = &ct->t_lwp->lwp_pcb.pcb_fpu; 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 1390Sstevel@tonic-gate * If the parent FPU state is still in the FPU hw then save it; 1400Sstevel@tonic-gate * conveniently, fp_save() already does this for us nicely. 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate fp_save(fp); 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate cfp->fpu_flags = FPU_EN | FPU_VALID; 1450Sstevel@tonic-gate cfp->fpu_regs.kfpu_status = 0; 1460Sstevel@tonic-gate cfp->fpu_regs.kfpu_xstatus = 0; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate #if defined(__amd64) 1490Sstevel@tonic-gate fx = &fp->fpu_regs.kfpu_u.kfpu_fx; 1500Sstevel@tonic-gate cfx = &cfp->fpu_regs.kfpu_u.kfpu_fx; 1510Sstevel@tonic-gate bcopy(&sse_initial, cfx, sizeof (*cfx)); 1520Sstevel@tonic-gate cfx->fx_mxcsr = fx->fx_mxcsr & ~SSE_MXCSR_EFLAGS; 1530Sstevel@tonic-gate cfx->fx_fcw = fx->fx_fcw; 1540Sstevel@tonic-gate #else 1550Sstevel@tonic-gate if (fp_kind == __FP_SSE) { 1560Sstevel@tonic-gate fx = &fp->fpu_regs.kfpu_u.kfpu_fx; 1570Sstevel@tonic-gate cfx = &cfp->fpu_regs.kfpu_u.kfpu_fx; 1580Sstevel@tonic-gate bcopy(&sse_initial, cfx, sizeof (*cfx)); 1590Sstevel@tonic-gate cfx->fx_mxcsr = fx->fx_mxcsr & ~SSE_MXCSR_EFLAGS; 1600Sstevel@tonic-gate cfx->fx_fcw = fx->fx_fcw; 1610Sstevel@tonic-gate } else { 1620Sstevel@tonic-gate struct fnsave_state *fn = &fp->fpu_regs.kfpu_u.kfpu_fn; 1630Sstevel@tonic-gate struct fnsave_state *cfn = &cfp->fpu_regs.kfpu_u.kfpu_fn; 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate bcopy(&x87_initial, cfn, sizeof (*cfn)); 1660Sstevel@tonic-gate cfn->f_fcw = fn->f_fcw; 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate #endif 1690Sstevel@tonic-gate installctx(ct, cfp, 1703446Smrj fpsave_ctxt, NULL, fp_new_lwp, fp_new_lwp, NULL, fp_free); 1710Sstevel@tonic-gate /* 1720Sstevel@tonic-gate * Now, when the new lwp starts running, it will take a trap 1730Sstevel@tonic-gate * that will be handled inline in the trap table to cause 1740Sstevel@tonic-gate * the appropriate f*rstor instruction to load the save area we 1750Sstevel@tonic-gate * constructed above directly into the hardware. 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate } 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate /* 1800Sstevel@tonic-gate * Free any state associated with floating point context. 1810Sstevel@tonic-gate * Fp_free can be called in three cases: 1820Sstevel@tonic-gate * 1) from reaper -> thread_free -> ctxfree -> fp_free 1830Sstevel@tonic-gate * fp context belongs to a thread on deathrow 1840Sstevel@tonic-gate * nothing to do, thread will never be resumed 1850Sstevel@tonic-gate * thread calling ctxfree is reaper 1860Sstevel@tonic-gate * 1870Sstevel@tonic-gate * 2) from exec -> ctxfree -> fp_free 1880Sstevel@tonic-gate * fp context belongs to the current thread 1890Sstevel@tonic-gate * must disable fpu, thread calling ctxfree is curthread 1900Sstevel@tonic-gate * 1910Sstevel@tonic-gate * 3) from restorecontext -> setfpregs -> fp_free 1920Sstevel@tonic-gate * we have a modified context in the memory (lwp->pcb_fpu) 1930Sstevel@tonic-gate * disable fpu and release the fp context for the CPU 1940Sstevel@tonic-gate * 1950Sstevel@tonic-gate */ 1960Sstevel@tonic-gate /*ARGSUSED*/ 1970Sstevel@tonic-gate void 1980Sstevel@tonic-gate fp_free(struct fpu_ctx *fp, int isexec) 1990Sstevel@tonic-gate { 2000Sstevel@tonic-gate ASSERT(fp_kind != FP_NO); 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate if (fp->fpu_flags & FPU_VALID) 2030Sstevel@tonic-gate return; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate kpreempt_disable(); 2060Sstevel@tonic-gate /* 2070Sstevel@tonic-gate * We want to do fpsave rather than fpdisable so that we can 2080Sstevel@tonic-gate * keep the fpu_flags as FPU_VALID tracking the CR0_TS bit 2090Sstevel@tonic-gate */ 2100Sstevel@tonic-gate fp->fpu_flags |= FPU_VALID; 2110Sstevel@tonic-gate /* If for current thread disable FP to track FPU_VALID */ 2120Sstevel@tonic-gate if (curthread->t_lwp && fp == &curthread->t_lwp->lwp_pcb.pcb_fpu) { 2130Sstevel@tonic-gate /* Clear errors if any to prevent frstor from complaining */ 2140Sstevel@tonic-gate (void) fperr_reset(); 2150Sstevel@tonic-gate if (fp_kind == __FP_SSE) 2160Sstevel@tonic-gate (void) fpxerr_reset(); 2170Sstevel@tonic-gate fpdisable(); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate kpreempt_enable(); 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate /* 2230Sstevel@tonic-gate * Store the floating point state and disable the floating point unit. 2240Sstevel@tonic-gate */ 2250Sstevel@tonic-gate void 2260Sstevel@tonic-gate fp_save(struct fpu_ctx *fp) 2270Sstevel@tonic-gate { 2280Sstevel@tonic-gate ASSERT(fp_kind != FP_NO); 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate kpreempt_disable(); 2310Sstevel@tonic-gate if (!fp || fp->fpu_flags & FPU_VALID) { 2320Sstevel@tonic-gate kpreempt_enable(); 2330Sstevel@tonic-gate return; 2340Sstevel@tonic-gate } 2350Sstevel@tonic-gate ASSERT(curthread->t_lwp && fp == &curthread->t_lwp->lwp_pcb.pcb_fpu); 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate #if defined(__amd64) 2380Sstevel@tonic-gate fpxsave(&fp->fpu_regs.kfpu_u.kfpu_fx); 2390Sstevel@tonic-gate #else 2400Sstevel@tonic-gate switch (fp_kind) { 2410Sstevel@tonic-gate case __FP_SSE: 2420Sstevel@tonic-gate fpxsave(&fp->fpu_regs.kfpu_u.kfpu_fx); 2430Sstevel@tonic-gate break; 2440Sstevel@tonic-gate default: 2450Sstevel@tonic-gate fpsave(&fp->fpu_regs.kfpu_u.kfpu_fn); 2460Sstevel@tonic-gate break; 2470Sstevel@tonic-gate } 2480Sstevel@tonic-gate #endif 2490Sstevel@tonic-gate fp->fpu_flags |= FPU_VALID; 2500Sstevel@tonic-gate kpreempt_enable(); 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate /* 2540Sstevel@tonic-gate * Restore the FPU context for the thread: 2550Sstevel@tonic-gate * The possibilities are: 2560Sstevel@tonic-gate * 1. No active FPU context: Load the new context into the FPU hw 2570Sstevel@tonic-gate * and enable the FPU. 2580Sstevel@tonic-gate */ 2590Sstevel@tonic-gate void 2600Sstevel@tonic-gate fp_restore(struct fpu_ctx *fp) 2610Sstevel@tonic-gate { 2620Sstevel@tonic-gate #if defined(__amd64) 2630Sstevel@tonic-gate fpxrestore(&fp->fpu_regs.kfpu_u.kfpu_fx); 2640Sstevel@tonic-gate #else 2650Sstevel@tonic-gate /* case 2 */ 2660Sstevel@tonic-gate if (fp_kind == __FP_SSE) 2670Sstevel@tonic-gate fpxrestore(&fp->fpu_regs.kfpu_u.kfpu_fx); 2680Sstevel@tonic-gate else 2690Sstevel@tonic-gate fprestore(&fp->fpu_regs.kfpu_u.kfpu_fn); 2700Sstevel@tonic-gate #endif 2710Sstevel@tonic-gate fp->fpu_flags &= ~FPU_VALID; 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * Seeds the initial state for the current thread. The possibilities are: 2770Sstevel@tonic-gate * 1. Another process has modified the FPU state before we have done any 2780Sstevel@tonic-gate * initialization: Load the FPU state from the LWP state. 2790Sstevel@tonic-gate * 2. The FPU state has not been externally modified: Load a clean state. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate static void 2820Sstevel@tonic-gate fp_seed(void) 2830Sstevel@tonic-gate { 2840Sstevel@tonic-gate struct fpu_ctx *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu; 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate ASSERT(curthread->t_preempt >= 1); 2870Sstevel@tonic-gate ASSERT((fp->fpu_flags & FPU_EN) == 0); 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate /* 2900Sstevel@tonic-gate * Always initialize a new context and initialize the hardware. 2910Sstevel@tonic-gate */ 2920Sstevel@tonic-gate installctx(curthread, fp, 2933446Smrj fpsave_ctxt, NULL, fp_new_lwp, fp_new_lwp, NULL, fp_free); 2940Sstevel@tonic-gate fpinit(); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate /* 2970Sstevel@tonic-gate * If FPU_VALID is set, it means someone has modified registers via 2980Sstevel@tonic-gate * /proc. In this case, restore the current lwp's state. 2990Sstevel@tonic-gate */ 3000Sstevel@tonic-gate if (fp->fpu_flags & FPU_VALID) 3010Sstevel@tonic-gate fp_restore(fp); 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate ASSERT((fp->fpu_flags & FPU_VALID) == 0); 3040Sstevel@tonic-gate fp->fpu_flags = FPU_EN; 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate /* 3080Sstevel@tonic-gate * This routine is called from trap() when User thread takes No Extension 3090Sstevel@tonic-gate * Fault. The possiblities are: 3100Sstevel@tonic-gate * 1. User thread has executed a FP instruction for the first time. 3110Sstevel@tonic-gate * Save current FPU context if any. Initialize FPU, setup FPU 3120Sstevel@tonic-gate * context for the thread and enable FP hw. 3130Sstevel@tonic-gate * 2. Thread's pcb has a valid FPU state: Restore the FPU state and 3140Sstevel@tonic-gate * enable FP hw. 3150Sstevel@tonic-gate * 3160Sstevel@tonic-gate * Note that case #2 is inlined in the trap table. 3170Sstevel@tonic-gate */ 3180Sstevel@tonic-gate int 3190Sstevel@tonic-gate fpnoextflt(struct regs *rp) 3200Sstevel@tonic-gate { 3210Sstevel@tonic-gate struct fpu_ctx *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate #if !defined(__lint) 3240Sstevel@tonic-gate ASSERT(sizeof (struct fxsave_state) == 512 && 3250Sstevel@tonic-gate sizeof (struct fnsave_state) == 108); 3260Sstevel@tonic-gate ASSERT((offsetof(struct fxsave_state, fx_xmm[0]) & 0xf) == 0); 3270Sstevel@tonic-gate #if defined(__i386) 3280Sstevel@tonic-gate ASSERT(sizeof (struct fpu) == sizeof (struct __old_fpu)); 3290Sstevel@tonic-gate #endif /* __i386 */ 3300Sstevel@tonic-gate #endif /* !__lint */ 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate /* 3330Sstevel@tonic-gate * save area MUST be 16-byte aligned, else will page fault 3340Sstevel@tonic-gate */ 3350Sstevel@tonic-gate ASSERT(((uintptr_t)(&fp->fpu_regs.kfpu_u.kfpu_fx) & 0xf) == 0); 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate kpreempt_disable(); 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * Now we can enable the interrupts. 3400Sstevel@tonic-gate * (NOTE: fp-no-coprocessor comes thru interrupt gate) 3410Sstevel@tonic-gate */ 3420Sstevel@tonic-gate sti(); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (!fpu_exists) { /* check for FPU hw exists */ 3450Sstevel@tonic-gate if (fp_kind == FP_NO) { 3460Sstevel@tonic-gate uint32_t inst; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate /* 3490Sstevel@tonic-gate * When the system has no floating point support, 3500Sstevel@tonic-gate * i.e. no FP hardware and no emulator, skip the 3510Sstevel@tonic-gate * two kinds of FP instruction that occur in 3520Sstevel@tonic-gate * fpstart. Allows processes that do no real FP 3530Sstevel@tonic-gate * to run normally. 3540Sstevel@tonic-gate */ 3550Sstevel@tonic-gate if (fuword32((void *)rp->r_pc, &inst) != -1 && 3560Sstevel@tonic-gate ((inst & 0xFFFF) == 0x7dd9 || 3570Sstevel@tonic-gate (inst & 0xFFFF) == 0x6dd9)) { 3580Sstevel@tonic-gate rp->r_pc += 3; 3590Sstevel@tonic-gate kpreempt_enable(); 3600Sstevel@tonic-gate return (0); 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* 3650Sstevel@tonic-gate * If we have neither a processor extension nor 3660Sstevel@tonic-gate * an emulator, kill the process OR panic the kernel. 3670Sstevel@tonic-gate */ 3680Sstevel@tonic-gate kpreempt_enable(); 3690Sstevel@tonic-gate return (1); /* error */ 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 372*5084Sjohnlev #if !defined(__xpv) /* XXPV Is this ifdef needed now? */ 3730Sstevel@tonic-gate /* 3740Sstevel@tonic-gate * A paranoid cross-check: for the SSE case, ensure that %cr4 is 3750Sstevel@tonic-gate * configured to enable fully fledged (%xmm) fxsave/fxrestor on 3760Sstevel@tonic-gate * this CPU. For the non-SSE case, ensure that it isn't. 3770Sstevel@tonic-gate */ 3780Sstevel@tonic-gate ASSERT((fp_kind == __FP_SSE && (getcr4() & CR4_OSFXSR) == CR4_OSFXSR) || 3790Sstevel@tonic-gate (fp_kind != __FP_SSE && 3800Sstevel@tonic-gate (getcr4() & (CR4_OSXMMEXCPT|CR4_OSFXSR)) == 0)); 381*5084Sjohnlev #endif 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate if (fp->fpu_flags & FPU_EN) { 3840Sstevel@tonic-gate /* case 2 */ 3850Sstevel@tonic-gate fp_restore(fp); 3860Sstevel@tonic-gate } else { 3870Sstevel@tonic-gate /* case 1 */ 3880Sstevel@tonic-gate fp_seed(); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate kpreempt_enable(); 3910Sstevel@tonic-gate return (0); 3920Sstevel@tonic-gate } 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate /* 3960Sstevel@tonic-gate * Handle a processor extension overrun fault 3970Sstevel@tonic-gate * Returns non zero for error. 3983446Smrj * 3993446Smrj * XXX Shouldn't this just be abolished given that we're not supporting 4003446Smrj * anything prior to Pentium? 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate /* ARGSUSED */ 4040Sstevel@tonic-gate int 4050Sstevel@tonic-gate fpextovrflt(struct regs *rp) 4060Sstevel@tonic-gate { 407*5084Sjohnlev #if !defined(__xpv) /* XXPV Do we need this ifdef either */ 4080Sstevel@tonic-gate ulong_t cur_cr0; 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate ASSERT(fp_kind != FP_NO); 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate cur_cr0 = getcr0(); 4130Sstevel@tonic-gate fpinit(); /* initialize the FPU hardware */ 4140Sstevel@tonic-gate setcr0(cur_cr0); 415*5084Sjohnlev #endif 4160Sstevel@tonic-gate sti(); 4170Sstevel@tonic-gate return (1); /* error, send SIGSEGV signal to the thread */ 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate /* 4210Sstevel@tonic-gate * Handle a processor extension error fault 4220Sstevel@tonic-gate * Returns non zero for error. 4230Sstevel@tonic-gate */ 4240Sstevel@tonic-gate 4250Sstevel@tonic-gate /*ARGSUSED*/ 4260Sstevel@tonic-gate int 4270Sstevel@tonic-gate fpexterrflt(struct regs *rp) 4280Sstevel@tonic-gate { 4290Sstevel@tonic-gate uint32_t fpcwsw; 4300Sstevel@tonic-gate fpu_ctx_t *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate ASSERT(fp_kind != FP_NO); 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate fpcwsw = fpgetcwsw(); 4350Sstevel@tonic-gate /* 4360Sstevel@tonic-gate * Now we can enable the interrupts. 4370Sstevel@tonic-gate * (NOTE: x87 fp exceptions come thru interrupt gate) 4380Sstevel@tonic-gate */ 4390Sstevel@tonic-gate sti(); 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate if ((fpcwsw & FPS_ES) == 0) 4420Sstevel@tonic-gate return (0); /* No exception */ 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate if (fpu_exists) { 4450Sstevel@tonic-gate fp_save(fp); 4460Sstevel@tonic-gate /* clear exception flags in saved state, as if by fnclex */ 4470Sstevel@tonic-gate #if defined(__amd64) 4480Sstevel@tonic-gate fp->fpu_regs.kfpu_u.kfpu_fx.fx_fsw &= ~FPS_SW_EFLAGS; 4490Sstevel@tonic-gate #else 4500Sstevel@tonic-gate switch (fp_kind) { 4510Sstevel@tonic-gate case __FP_SSE: 4520Sstevel@tonic-gate fp->fpu_regs.kfpu_u.kfpu_fx.fx_fsw &= ~FPS_SW_EFLAGS; 4530Sstevel@tonic-gate break; 4540Sstevel@tonic-gate default: 4550Sstevel@tonic-gate fp->fpu_regs.kfpu_u.kfpu_fn.f_fsw &= ~FPS_SW_EFLAGS; 4560Sstevel@tonic-gate break; 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate #endif 4590Sstevel@tonic-gate } 4600Sstevel@tonic-gate fp->fpu_regs.kfpu_status = fpcwsw & 0xffff; 4610Sstevel@tonic-gate /* 4620Sstevel@tonic-gate * "and" the exception flags with the complement of the mask 4630Sstevel@tonic-gate * bits to determine which exception occurred 4640Sstevel@tonic-gate */ 4650Sstevel@tonic-gate return (fpe_sicode(fpcwsw & ~(fpcwsw >> 16) & 0x3f)); 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate /* 4690Sstevel@tonic-gate * Handle an SSE/SSE2 precise exception. 4700Sstevel@tonic-gate * Returns a non-zero sicode for error. 4710Sstevel@tonic-gate */ 4720Sstevel@tonic-gate /*ARGSUSED*/ 4730Sstevel@tonic-gate int 4740Sstevel@tonic-gate fpsimderrflt(struct regs *rp) 4750Sstevel@tonic-gate { 4760Sstevel@tonic-gate uint32_t mxcsr, xmask; 4770Sstevel@tonic-gate fpu_ctx_t *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu; 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate ASSERT(fp_kind == __FP_SSE); 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate mxcsr = fpgetmxcsr(); 4820Sstevel@tonic-gate if (fpu_exists) { 4830Sstevel@tonic-gate fp_save(fp); /* save the FPU state */ 4840Sstevel@tonic-gate fp->fpu_regs.kfpu_status = fp->fpu_regs.kfpu_u.kfpu_fx.fx_fsw; 4850Sstevel@tonic-gate } else { 4860Sstevel@tonic-gate fp->fpu_regs.kfpu_status = fpgetcwsw() & 0xffff; 4870Sstevel@tonic-gate } 4880Sstevel@tonic-gate fp->fpu_regs.kfpu_xstatus = mxcsr; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate /* 4910Sstevel@tonic-gate * compute the mask that determines which conditions can cause 4920Sstevel@tonic-gate * a #xm exception, and use this to clean the status bits so that 4930Sstevel@tonic-gate * we can identify the true cause of this one. 4940Sstevel@tonic-gate */ 4950Sstevel@tonic-gate xmask = (mxcsr >> 7) & SSE_MXCSR_EFLAGS; 4960Sstevel@tonic-gate return (fpe_simd_sicode((mxcsr & SSE_MXCSR_EFLAGS) & ~xmask)); 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate /* 5000Sstevel@tonic-gate * In the unlikely event that someone is relying on this subcode being 5010Sstevel@tonic-gate * FPE_FLTILL for denormalize exceptions, it can always be patched back 5020Sstevel@tonic-gate * again to restore old behaviour. 5030Sstevel@tonic-gate */ 5040Sstevel@tonic-gate int fpe_fltden = FPE_FLTDEN; 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate /* 5070Sstevel@tonic-gate * Map from the FPU status word to the FP exception si_code. 5080Sstevel@tonic-gate */ 5090Sstevel@tonic-gate static int 5100Sstevel@tonic-gate fpe_sicode(uint_t sw) 5110Sstevel@tonic-gate { 5120Sstevel@tonic-gate if (sw & FPS_IE) 5130Sstevel@tonic-gate return (FPE_FLTINV); 5140Sstevel@tonic-gate if (sw & FPS_ZE) 5150Sstevel@tonic-gate return (FPE_FLTDIV); 5160Sstevel@tonic-gate if (sw & FPS_DE) 5170Sstevel@tonic-gate return (fpe_fltden); 5180Sstevel@tonic-gate if (sw & FPS_OE) 5190Sstevel@tonic-gate return (FPE_FLTOVF); 5200Sstevel@tonic-gate if (sw & FPS_UE) 5210Sstevel@tonic-gate return (FPE_FLTUND); 5220Sstevel@tonic-gate if (sw & FPS_PE) 5230Sstevel@tonic-gate return (FPE_FLTRES); 5240Sstevel@tonic-gate return (FPE_FLTINV); /* default si_code for other exceptions */ 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate /* 5280Sstevel@tonic-gate * Map from the SSE status word to the FP exception si_code. 5290Sstevel@tonic-gate */ 5300Sstevel@tonic-gate static int 5310Sstevel@tonic-gate fpe_simd_sicode(uint_t sw) 5320Sstevel@tonic-gate { 5330Sstevel@tonic-gate if (sw & SSE_IE) 5340Sstevel@tonic-gate return (FPE_FLTINV); 5350Sstevel@tonic-gate if (sw & SSE_ZE) 5360Sstevel@tonic-gate return (FPE_FLTDIV); 5370Sstevel@tonic-gate if (sw & SSE_DE) 5380Sstevel@tonic-gate return (FPE_FLTDEN); 5390Sstevel@tonic-gate if (sw & SSE_OE) 5400Sstevel@tonic-gate return (FPE_FLTOVF); 5410Sstevel@tonic-gate if (sw & SSE_UE) 5420Sstevel@tonic-gate return (FPE_FLTUND); 5430Sstevel@tonic-gate if (sw & SSE_PE) 5440Sstevel@tonic-gate return (FPE_FLTRES); 5450Sstevel@tonic-gate return (FPE_FLTINV); /* default si_code for other exceptions */ 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate /* 5490Sstevel@tonic-gate * This routine is invoked as part of libc's __fpstart implementation 5500Sstevel@tonic-gate * via sysi86(2). 5510Sstevel@tonic-gate * 5520Sstevel@tonic-gate * It may be called -before- any context has been assigned in which case 5530Sstevel@tonic-gate * we try and avoid touching the hardware. Or it may be invoked well 5540Sstevel@tonic-gate * after the context has been assigned and fiddled with, in which case 5550Sstevel@tonic-gate * just tweak it directly. 5560Sstevel@tonic-gate */ 5570Sstevel@tonic-gate void 5580Sstevel@tonic-gate fpsetcw(uint16_t fcw, uint32_t mxcsr) 5590Sstevel@tonic-gate { 5600Sstevel@tonic-gate struct fpu_ctx *fp = &curthread->t_lwp->lwp_pcb.pcb_fpu; 5610Sstevel@tonic-gate struct fxsave_state *fx; 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate if (!fpu_exists || fp_kind == FP_NO) 5640Sstevel@tonic-gate return; 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate if ((fp->fpu_flags & FPU_EN) == 0) { 5670Sstevel@tonic-gate if (fcw == FPU_CW_INIT && mxcsr == SSE_MXCSR_INIT) { 5680Sstevel@tonic-gate /* 5690Sstevel@tonic-gate * Common case. Floating point unit not yet 5700Sstevel@tonic-gate * enabled, and kernel already intends to initialize 5710Sstevel@tonic-gate * the hardware the way the caller wants. 5720Sstevel@tonic-gate */ 5730Sstevel@tonic-gate return; 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate /* 5760Sstevel@tonic-gate * Hmm. Userland wants a different default. 5770Sstevel@tonic-gate * Do a fake "first trap" to establish the context, then 5780Sstevel@tonic-gate * handle as if we already had a context before we came in. 5790Sstevel@tonic-gate */ 5800Sstevel@tonic-gate kpreempt_disable(); 5810Sstevel@tonic-gate fp_seed(); 5820Sstevel@tonic-gate kpreempt_enable(); 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate /* 5860Sstevel@tonic-gate * Ensure that the current hardware state is flushed back to the 5870Sstevel@tonic-gate * pcb, then modify that copy. Next use of the fp will 5880Sstevel@tonic-gate * restore the context. 5890Sstevel@tonic-gate */ 5900Sstevel@tonic-gate fp_save(fp); 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate #if defined(__amd64) 5930Sstevel@tonic-gate fx = &fp->fpu_regs.kfpu_u.kfpu_fx; 5940Sstevel@tonic-gate fx->fx_fcw = fcw; 5950Sstevel@tonic-gate fx->fx_mxcsr = sse_mxcsr_mask & mxcsr; 5960Sstevel@tonic-gate #else 5970Sstevel@tonic-gate switch (fp_kind) { 5980Sstevel@tonic-gate case __FP_SSE: 5990Sstevel@tonic-gate fx = &fp->fpu_regs.kfpu_u.kfpu_fx; 6000Sstevel@tonic-gate fx->fx_fcw = fcw; 6010Sstevel@tonic-gate fx->fx_mxcsr = sse_mxcsr_mask & mxcsr; 6020Sstevel@tonic-gate break; 6030Sstevel@tonic-gate default: 6040Sstevel@tonic-gate fp->fpu_regs.kfpu_u.kfpu_fn.f_fcw = fcw; 6050Sstevel@tonic-gate break; 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate #endif 6080Sstevel@tonic-gate } 609