15b81b6b3SRodney W. Grimes /*- 25b81b6b3SRodney W. Grimes * Copyright (c) 1990 William Jolitz. 35b81b6b3SRodney W. Grimes * Copyright (c) 1991 The Regents of the University of California. 45b81b6b3SRodney W. Grimes * All rights reserved. 55b81b6b3SRodney W. Grimes * 65b81b6b3SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 75b81b6b3SRodney W. Grimes * modification, are permitted provided that the following conditions 85b81b6b3SRodney W. Grimes * are met: 95b81b6b3SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 105b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 115b81b6b3SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 125b81b6b3SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 135b81b6b3SRodney W. Grimes * documentation and/or other materials provided with the distribution. 145b81b6b3SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 155b81b6b3SRodney W. Grimes * may be used to endorse or promote products derived from this software 165b81b6b3SRodney W. Grimes * without specific prior written permission. 175b81b6b3SRodney W. Grimes * 185b81b6b3SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 195b81b6b3SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 205b81b6b3SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 215b81b6b3SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 225b81b6b3SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 235b81b6b3SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 245b81b6b3SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 255b81b6b3SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 265b81b6b3SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 275b81b6b3SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 285b81b6b3SRodney W. Grimes * SUCH DAMAGE. 295b81b6b3SRodney W. Grimes * 3021616ec3SPeter Wemm * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 315b81b6b3SRodney W. Grimes */ 325b81b6b3SRodney W. Grimes 3356ae44c5SDavid E. O'Brien #include <sys/cdefs.h> 3456ae44c5SDavid E. O'Brien __FBSDID("$FreeBSD$"); 3556ae44c5SDavid E. O'Brien 36f540b106SGarrett Wollman #include <sys/param.h> 37f540b106SGarrett Wollman #include <sys/systm.h> 386182fdbdSPeter Wemm #include <sys/bus.h> 393a34a5c3SPoul-Henning Kamp #include <sys/kernel.h> 40fb919e4dSMark Murray #include <sys/lock.h> 41cd59d49dSBruce Evans #include <sys/malloc.h> 426182fdbdSPeter Wemm #include <sys/module.h> 43c1ef8aacSJake Burkholder #include <sys/mutex.h> 44fb919e4dSMark Murray #include <sys/mutex.h> 45fb919e4dSMark Murray #include <sys/proc.h> 46fb919e4dSMark Murray #include <sys/sysctl.h> 476182fdbdSPeter Wemm #include <machine/bus.h> 486182fdbdSPeter Wemm #include <sys/rman.h> 49663f1485SBruce Evans #include <sys/signalvar.h> 502f86936aSGarrett Wollman 517f47cf2fSBruce Evans #include <machine/cputypes.h> 527f47cf2fSBruce Evans #include <machine/frame.h> 530d2a2989SPeter Wemm #include <machine/intr_machdep.h> 54c673fe98SBruce Evans #include <machine/md_var.h> 555400ed3bSPeter Wemm #include <machine/pcb.h> 567f47cf2fSBruce Evans #include <machine/psl.h> 576182fdbdSPeter Wemm #include <machine/resource.h> 58f540b106SGarrett Wollman #include <machine/specialreg.h> 597f47cf2fSBruce Evans #include <machine/segments.h> 6030abe507SJonathan Mini #include <machine/ucontext.h> 612f86936aSGarrett Wollman 625b81b6b3SRodney W. Grimes /* 63bf2f09eeSPeter Wemm * Floating point support. 645b81b6b3SRodney W. Grimes */ 655b81b6b3SRodney W. Grimes 66a5f50ef9SJoerg Wunsch #if defined(__GNUCLIKE_ASM) && !defined(lint) 675b81b6b3SRodney W. Grimes 6817275403SJung-uk Kim #define fldcw(cw) __asm __volatile("fldcw %0" : : "m" (cw)) 6930402401SJung-uk Kim #define fnclex() __asm __volatile("fnclex") 7030402401SJung-uk Kim #define fninit() __asm __volatile("fninit") 711d37f051SBruce Evans #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) 722e50fa36SJung-uk Kim #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=am" (*(addr))) 7330402401SJung-uk Kim #define fxrstor(addr) __asm __volatile("fxrstor %0" : : "m" (*(addr))) 749d146ac5SPeter Wemm #define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr))) 7507c86dcfSJung-uk Kim #define ldmxcsr(csr) __asm __volatile("ldmxcsr %0" : : "m" (csr)) 7630402401SJung-uk Kim #define start_emulating() __asm __volatile( \ 7730402401SJung-uk Kim "smsw %%ax; orb %0,%%al; lmsw %%ax" \ 785b81b6b3SRodney W. Grimes : : "n" (CR0_TS) : "ax") 7930402401SJung-uk Kim #define stop_emulating() __asm __volatile("clts") 805b81b6b3SRodney W. Grimes 81cf4e1c46SPeter Wemm #else /* !(__GNUCLIKE_ASM && !lint) */ 825b81b6b3SRodney W. Grimes 8317275403SJung-uk Kim void fldcw(u_short cw); 8489c9a483SAlfred Perlstein void fnclex(void); 8589c9a483SAlfred Perlstein void fninit(void); 8689c9a483SAlfred Perlstein void fnstcw(caddr_t addr); 8789c9a483SAlfred Perlstein void fnstsw(caddr_t addr); 8889c9a483SAlfred Perlstein void fxsave(caddr_t addr); 8989c9a483SAlfred Perlstein void fxrstor(caddr_t addr); 9007c86dcfSJung-uk Kim void ldmxcsr(u_int csr); 9189c9a483SAlfred Perlstein void start_emulating(void); 9289c9a483SAlfred Perlstein void stop_emulating(void); 935b81b6b3SRodney W. Grimes 94cf4e1c46SPeter Wemm #endif /* __GNUCLIKE_ASM && !lint */ 955b81b6b3SRodney W. Grimes 966cf9a08dSKonstantin Belousov #define GET_FPU_CW(thread) ((thread)->td_pcb->pcb_save->sv_env.en_cw) 976cf9a08dSKonstantin Belousov #define GET_FPU_SW(thread) ((thread)->td_pcb->pcb_save->sv_env.en_sw) 989d146ac5SPeter Wemm 99*8c6f8f3dSKonstantin Belousov CTASSERT(sizeof(struct savefpu) == 512); 100*8c6f8f3dSKonstantin Belousov CTASSERT(sizeof(struct xstate_hdr) == 64); 101*8c6f8f3dSKonstantin Belousov CTASSERT(sizeof(struct savefpu_ymm) == 832); 102*8c6f8f3dSKonstantin Belousov 103*8c6f8f3dSKonstantin Belousov /* 104*8c6f8f3dSKonstantin Belousov * This requirement is to make it easier for asm code to calculate 105*8c6f8f3dSKonstantin Belousov * offset of the fpu save area from the pcb address. FPU save area 106*8c6f8f3dSKonstantin Belousov * must by 64-bytes aligned. 107*8c6f8f3dSKonstantin Belousov */ 108*8c6f8f3dSKonstantin Belousov CTASSERT(sizeof(struct pcb) % XSAVE_AREA_ALIGN == 0); 1095b81b6b3SRodney W. Grimes 1102652af56SColin Percival static void fpu_clean_state(void); 1112652af56SColin Percival 1120b7dc0a7SJohn Baldwin SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, 1130b7dc0a7SJohn Baldwin NULL, 1, "Floating point instructions executed in hardware"); 1143a34a5c3SPoul-Henning Kamp 115*8c6f8f3dSKonstantin Belousov int use_xsave; /* non-static for cpu_switch.S */ 116*8c6f8f3dSKonstantin Belousov uint64_t xsave_mask; /* the same */ 117*8c6f8f3dSKonstantin Belousov static struct savefpu *fpu_initialstate; 118*8c6f8f3dSKonstantin Belousov 119*8c6f8f3dSKonstantin Belousov void 120*8c6f8f3dSKonstantin Belousov fpusave(void *addr) 121*8c6f8f3dSKonstantin Belousov { 122*8c6f8f3dSKonstantin Belousov 123*8c6f8f3dSKonstantin Belousov if (use_xsave) 124*8c6f8f3dSKonstantin Belousov xsave((char *)addr, xsave_mask); 125*8c6f8f3dSKonstantin Belousov else 126*8c6f8f3dSKonstantin Belousov fxsave((char *)addr); 127*8c6f8f3dSKonstantin Belousov } 128*8c6f8f3dSKonstantin Belousov 129*8c6f8f3dSKonstantin Belousov static void 130*8c6f8f3dSKonstantin Belousov fpurestore(void *addr) 131*8c6f8f3dSKonstantin Belousov { 132*8c6f8f3dSKonstantin Belousov 133*8c6f8f3dSKonstantin Belousov if (use_xsave) 134*8c6f8f3dSKonstantin Belousov xrstor((char *)addr, xsave_mask); 135*8c6f8f3dSKonstantin Belousov else 136*8c6f8f3dSKonstantin Belousov fxrstor((char *)addr); 137*8c6f8f3dSKonstantin Belousov } 1383902c3efSSteve Passe 1395b81b6b3SRodney W. Grimes /* 140*8c6f8f3dSKonstantin Belousov * Enable XSAVE if supported and allowed by user. 141*8c6f8f3dSKonstantin Belousov * Calculate the xsave_mask. 142*8c6f8f3dSKonstantin Belousov */ 143*8c6f8f3dSKonstantin Belousov static void 144*8c6f8f3dSKonstantin Belousov fpuinit_bsp1(void) 145*8c6f8f3dSKonstantin Belousov { 146*8c6f8f3dSKonstantin Belousov u_int cp[4]; 147*8c6f8f3dSKonstantin Belousov uint64_t xsave_mask_user; 148*8c6f8f3dSKonstantin Belousov 149*8c6f8f3dSKonstantin Belousov if ((cpu_feature2 & CPUID2_XSAVE) != 0) { 150*8c6f8f3dSKonstantin Belousov use_xsave = 1; 151*8c6f8f3dSKonstantin Belousov TUNABLE_INT_FETCH("hw.use_xsave", &use_xsave); 152*8c6f8f3dSKonstantin Belousov } 153*8c6f8f3dSKonstantin Belousov if (!use_xsave) 154*8c6f8f3dSKonstantin Belousov return; 155*8c6f8f3dSKonstantin Belousov 156*8c6f8f3dSKonstantin Belousov cpuid_count(0xd, 0x0, cp); 157*8c6f8f3dSKonstantin Belousov xsave_mask = XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; 158*8c6f8f3dSKonstantin Belousov if ((cp[0] & xsave_mask) != xsave_mask) 159*8c6f8f3dSKonstantin Belousov panic("CPU0 does not support X87 or SSE: %x", cp[0]); 160*8c6f8f3dSKonstantin Belousov xsave_mask = ((uint64_t)cp[3] << 32) | cp[0]; 161*8c6f8f3dSKonstantin Belousov xsave_mask_user = xsave_mask; 162*8c6f8f3dSKonstantin Belousov TUNABLE_ULONG_FETCH("hw.xsave_mask", &xsave_mask_user); 163*8c6f8f3dSKonstantin Belousov xsave_mask_user |= XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; 164*8c6f8f3dSKonstantin Belousov xsave_mask &= xsave_mask_user; 165*8c6f8f3dSKonstantin Belousov } 166*8c6f8f3dSKonstantin Belousov 167*8c6f8f3dSKonstantin Belousov /* 168*8c6f8f3dSKonstantin Belousov * Calculate the fpu save area size. 169*8c6f8f3dSKonstantin Belousov */ 170*8c6f8f3dSKonstantin Belousov static void 171*8c6f8f3dSKonstantin Belousov fpuinit_bsp2(void) 172*8c6f8f3dSKonstantin Belousov { 173*8c6f8f3dSKonstantin Belousov u_int cp[4]; 174*8c6f8f3dSKonstantin Belousov 175*8c6f8f3dSKonstantin Belousov if (use_xsave) { 176*8c6f8f3dSKonstantin Belousov cpuid_count(0xd, 0x0, cp); 177*8c6f8f3dSKonstantin Belousov cpu_max_ext_state_size = cp[1]; 178*8c6f8f3dSKonstantin Belousov 179*8c6f8f3dSKonstantin Belousov /* 180*8c6f8f3dSKonstantin Belousov * Reload the cpu_feature2, since we enabled OSXSAVE. 181*8c6f8f3dSKonstantin Belousov */ 182*8c6f8f3dSKonstantin Belousov do_cpuid(1, cp); 183*8c6f8f3dSKonstantin Belousov cpu_feature2 = cp[2]; 184*8c6f8f3dSKonstantin Belousov } else 185*8c6f8f3dSKonstantin Belousov cpu_max_ext_state_size = sizeof(struct savefpu); 186*8c6f8f3dSKonstantin Belousov } 187*8c6f8f3dSKonstantin Belousov 188*8c6f8f3dSKonstantin Belousov /* 189*8c6f8f3dSKonstantin Belousov * Initialize the floating point unit. 190da4113b3SPeter Wemm */ 191398dbb11SPeter Wemm void 1921c89210cSPeter Wemm fpuinit(void) 193da4113b3SPeter Wemm { 1940689bdccSJohn Baldwin register_t saveintr; 19596a7759eSPeter Wemm u_int mxcsr; 196398dbb11SPeter Wemm u_short control; 197da4113b3SPeter Wemm 198*8c6f8f3dSKonstantin Belousov if (IS_BSP()) 199*8c6f8f3dSKonstantin Belousov fpuinit_bsp1(); 200*8c6f8f3dSKonstantin Belousov 201*8c6f8f3dSKonstantin Belousov if (use_xsave) { 202*8c6f8f3dSKonstantin Belousov load_cr4(rcr4() | CR4_XSAVE); 203*8c6f8f3dSKonstantin Belousov xsetbv(XCR0, xsave_mask); 204*8c6f8f3dSKonstantin Belousov } 205*8c6f8f3dSKonstantin Belousov 206*8c6f8f3dSKonstantin Belousov /* 207*8c6f8f3dSKonstantin Belousov * XCR0 shall be set up before CPU can report the save area size. 208*8c6f8f3dSKonstantin Belousov */ 209*8c6f8f3dSKonstantin Belousov if (IS_BSP()) 210*8c6f8f3dSKonstantin Belousov fpuinit_bsp2(); 211*8c6f8f3dSKonstantin Belousov 21299753495SKonstantin Belousov /* 21399753495SKonstantin Belousov * It is too early for critical_enter() to work on AP. 21499753495SKonstantin Belousov */ 2150689bdccSJohn Baldwin saveintr = intr_disable(); 2165b81b6b3SRodney W. Grimes stop_emulating(); 2175b81b6b3SRodney W. Grimes fninit(); 218398dbb11SPeter Wemm control = __INITIAL_FPUCW__; 21917275403SJung-uk Kim fldcw(control); 22096a7759eSPeter Wemm mxcsr = __INITIAL_MXCSR__; 22196a7759eSPeter Wemm ldmxcsr(mxcsr); 222a8346a98SJohn Baldwin start_emulating(); 2230689bdccSJohn Baldwin intr_restore(saveintr); 2245b81b6b3SRodney W. Grimes } 2255b81b6b3SRodney W. Grimes 2265b81b6b3SRodney W. Grimes /* 227*8c6f8f3dSKonstantin Belousov * On the boot CPU we generate a clean state that is used to 228*8c6f8f3dSKonstantin Belousov * initialize the floating point unit when it is first used by a 229*8c6f8f3dSKonstantin Belousov * process. 230*8c6f8f3dSKonstantin Belousov */ 231*8c6f8f3dSKonstantin Belousov static void 232*8c6f8f3dSKonstantin Belousov fpuinitstate(void *arg __unused) 233*8c6f8f3dSKonstantin Belousov { 234*8c6f8f3dSKonstantin Belousov register_t saveintr; 235*8c6f8f3dSKonstantin Belousov 236*8c6f8f3dSKonstantin Belousov fpu_initialstate = malloc(cpu_max_ext_state_size, M_DEVBUF, 237*8c6f8f3dSKonstantin Belousov M_WAITOK | M_ZERO); 238*8c6f8f3dSKonstantin Belousov saveintr = intr_disable(); 239*8c6f8f3dSKonstantin Belousov stop_emulating(); 240*8c6f8f3dSKonstantin Belousov 241*8c6f8f3dSKonstantin Belousov fpusave(fpu_initialstate); 242*8c6f8f3dSKonstantin Belousov if (fpu_initialstate->sv_env.en_mxcsr_mask) 243*8c6f8f3dSKonstantin Belousov cpu_mxcsr_mask = fpu_initialstate->sv_env.en_mxcsr_mask; 244*8c6f8f3dSKonstantin Belousov else 245*8c6f8f3dSKonstantin Belousov cpu_mxcsr_mask = 0xFFBF; 246*8c6f8f3dSKonstantin Belousov 247*8c6f8f3dSKonstantin Belousov /* 248*8c6f8f3dSKonstantin Belousov * The fninit instruction does not modify XMM registers. The 249*8c6f8f3dSKonstantin Belousov * fpusave call dumped the garbage contained in the registers 250*8c6f8f3dSKonstantin Belousov * after reset to the initial state saved. Clear XMM 251*8c6f8f3dSKonstantin Belousov * registers file image to make the startup program state and 252*8c6f8f3dSKonstantin Belousov * signal handler XMM register content predictable. 253*8c6f8f3dSKonstantin Belousov */ 254*8c6f8f3dSKonstantin Belousov bzero(&fpu_initialstate->sv_xmm[0], sizeof(struct xmmacc)); 255*8c6f8f3dSKonstantin Belousov 256*8c6f8f3dSKonstantin Belousov start_emulating(); 257*8c6f8f3dSKonstantin Belousov intr_restore(saveintr); 258*8c6f8f3dSKonstantin Belousov } 259*8c6f8f3dSKonstantin Belousov SYSINIT(fpuinitstate, SI_SUB_DRIVERS, SI_ORDER_ANY, fpuinitstate, NULL); 260*8c6f8f3dSKonstantin Belousov 261*8c6f8f3dSKonstantin Belousov /* 2625b81b6b3SRodney W. Grimes * Free coprocessor (if we have it). 2635b81b6b3SRodney W. Grimes */ 2645b81b6b3SRodney W. Grimes void 265bf2f09eeSPeter Wemm fpuexit(struct thread *td) 2665b81b6b3SRodney W. Grimes { 2675b81b6b3SRodney W. Grimes 26899753495SKonstantin Belousov critical_enter(); 2691c89210cSPeter Wemm if (curthread == PCPU_GET(fpcurthread)) { 2701c89210cSPeter Wemm stop_emulating(); 271*8c6f8f3dSKonstantin Belousov fpusave(PCPU_GET(curpcb)->pcb_save); 2721c89210cSPeter Wemm start_emulating(); 2731c89210cSPeter Wemm PCPU_SET(fpcurthread, 0); 2741c89210cSPeter Wemm } 27599753495SKonstantin Belousov critical_exit(); 2765b81b6b3SRodney W. Grimes } 2775b81b6b3SRodney W. Grimes 27830abe507SJonathan Mini int 279bf2f09eeSPeter Wemm fpuformat() 28030abe507SJonathan Mini { 28130abe507SJonathan Mini 28230abe507SJonathan Mini return (_MC_FPFMT_XMM); 28330abe507SJonathan Mini } 28430abe507SJonathan Mini 2855b81b6b3SRodney W. Grimes /* 286a7674320SMartin Cracauer * The following mechanism is used to ensure that the FPE_... value 287a7674320SMartin Cracauer * that is passed as a trapcode to the signal handler of the user 288a7674320SMartin Cracauer * process does not have more than one bit set. 289a7674320SMartin Cracauer * 290a7674320SMartin Cracauer * Multiple bits may be set if the user process modifies the control 291a7674320SMartin Cracauer * word while a status word bit is already set. While this is a sign 292a7674320SMartin Cracauer * of bad coding, we have no choise than to narrow them down to one 293a7674320SMartin Cracauer * bit, since we must not send a trapcode that is not exactly one of 294a7674320SMartin Cracauer * the FPE_ macros. 295a7674320SMartin Cracauer * 296a7674320SMartin Cracauer * The mechanism has a static table with 127 entries. Each combination 297a7674320SMartin Cracauer * of the 7 FPU status word exception bits directly translates to a 298a7674320SMartin Cracauer * position in this table, where a single FPE_... value is stored. 299a7674320SMartin Cracauer * This FPE_... value stored there is considered the "most important" 300a7674320SMartin Cracauer * of the exception bits and will be sent as the signal code. The 301a7674320SMartin Cracauer * precedence of the bits is based upon Intel Document "Numerical 302a7674320SMartin Cracauer * Applications", Chapter "Special Computational Situations". 303a7674320SMartin Cracauer * 304a7674320SMartin Cracauer * The macro to choose one of these values does these steps: 1) Throw 305a7674320SMartin Cracauer * away status word bits that cannot be masked. 2) Throw away the bits 306a7674320SMartin Cracauer * currently masked in the control word, assuming the user isn't 307a7674320SMartin Cracauer * interested in them anymore. 3) Reinsert status word bit 7 (stack 308a7674320SMartin Cracauer * fault) if it is set, which cannot be masked but must be presered. 309a7674320SMartin Cracauer * 4) Use the remaining bits to point into the trapcode table. 310a7674320SMartin Cracauer * 311a7674320SMartin Cracauer * The 6 maskable bits in order of their preference, as stated in the 312a7674320SMartin Cracauer * above referenced Intel manual: 313a7674320SMartin Cracauer * 1 Invalid operation (FP_X_INV) 314a7674320SMartin Cracauer * 1a Stack underflow 315a7674320SMartin Cracauer * 1b Stack overflow 316a7674320SMartin Cracauer * 1c Operand of unsupported format 317a7674320SMartin Cracauer * 1d SNaN operand. 318a7674320SMartin Cracauer * 2 QNaN operand (not an exception, irrelavant here) 319a7674320SMartin Cracauer * 3 Any other invalid-operation not mentioned above or zero divide 320a7674320SMartin Cracauer * (FP_X_INV, FP_X_DZ) 321a7674320SMartin Cracauer * 4 Denormal operand (FP_X_DNML) 322a7674320SMartin Cracauer * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) 323784648c6SMartin Cracauer * 6 Inexact result (FP_X_IMP) 324784648c6SMartin Cracauer */ 325a7674320SMartin Cracauer static char fpetable[128] = { 326a7674320SMartin Cracauer 0, 327a7674320SMartin Cracauer FPE_FLTINV, /* 1 - INV */ 328a7674320SMartin Cracauer FPE_FLTUND, /* 2 - DNML */ 329a7674320SMartin Cracauer FPE_FLTINV, /* 3 - INV | DNML */ 330a7674320SMartin Cracauer FPE_FLTDIV, /* 4 - DZ */ 331a7674320SMartin Cracauer FPE_FLTINV, /* 5 - INV | DZ */ 332a7674320SMartin Cracauer FPE_FLTDIV, /* 6 - DNML | DZ */ 333a7674320SMartin Cracauer FPE_FLTINV, /* 7 - INV | DNML | DZ */ 334a7674320SMartin Cracauer FPE_FLTOVF, /* 8 - OFL */ 335a7674320SMartin Cracauer FPE_FLTINV, /* 9 - INV | OFL */ 336a7674320SMartin Cracauer FPE_FLTUND, /* A - DNML | OFL */ 337a7674320SMartin Cracauer FPE_FLTINV, /* B - INV | DNML | OFL */ 338a7674320SMartin Cracauer FPE_FLTDIV, /* C - DZ | OFL */ 339a7674320SMartin Cracauer FPE_FLTINV, /* D - INV | DZ | OFL */ 340a7674320SMartin Cracauer FPE_FLTDIV, /* E - DNML | DZ | OFL */ 341a7674320SMartin Cracauer FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ 342a7674320SMartin Cracauer FPE_FLTUND, /* 10 - UFL */ 343a7674320SMartin Cracauer FPE_FLTINV, /* 11 - INV | UFL */ 344a7674320SMartin Cracauer FPE_FLTUND, /* 12 - DNML | UFL */ 345a7674320SMartin Cracauer FPE_FLTINV, /* 13 - INV | DNML | UFL */ 346a7674320SMartin Cracauer FPE_FLTDIV, /* 14 - DZ | UFL */ 347a7674320SMartin Cracauer FPE_FLTINV, /* 15 - INV | DZ | UFL */ 348a7674320SMartin Cracauer FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ 349a7674320SMartin Cracauer FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ 350a7674320SMartin Cracauer FPE_FLTOVF, /* 18 - OFL | UFL */ 351a7674320SMartin Cracauer FPE_FLTINV, /* 19 - INV | OFL | UFL */ 352a7674320SMartin Cracauer FPE_FLTUND, /* 1A - DNML | OFL | UFL */ 353a7674320SMartin Cracauer FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ 354a7674320SMartin Cracauer FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ 355a7674320SMartin Cracauer FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ 356a7674320SMartin Cracauer FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ 357a7674320SMartin Cracauer FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ 358a7674320SMartin Cracauer FPE_FLTRES, /* 20 - IMP */ 359a7674320SMartin Cracauer FPE_FLTINV, /* 21 - INV | IMP */ 360a7674320SMartin Cracauer FPE_FLTUND, /* 22 - DNML | IMP */ 361a7674320SMartin Cracauer FPE_FLTINV, /* 23 - INV | DNML | IMP */ 362a7674320SMartin Cracauer FPE_FLTDIV, /* 24 - DZ | IMP */ 363a7674320SMartin Cracauer FPE_FLTINV, /* 25 - INV | DZ | IMP */ 364a7674320SMartin Cracauer FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ 365a7674320SMartin Cracauer FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ 366a7674320SMartin Cracauer FPE_FLTOVF, /* 28 - OFL | IMP */ 367a7674320SMartin Cracauer FPE_FLTINV, /* 29 - INV | OFL | IMP */ 368a7674320SMartin Cracauer FPE_FLTUND, /* 2A - DNML | OFL | IMP */ 369a7674320SMartin Cracauer FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ 370a7674320SMartin Cracauer FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ 371a7674320SMartin Cracauer FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ 372a7674320SMartin Cracauer FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ 373a7674320SMartin Cracauer FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ 374a7674320SMartin Cracauer FPE_FLTUND, /* 30 - UFL | IMP */ 375a7674320SMartin Cracauer FPE_FLTINV, /* 31 - INV | UFL | IMP */ 376a7674320SMartin Cracauer FPE_FLTUND, /* 32 - DNML | UFL | IMP */ 377a7674320SMartin Cracauer FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ 378a7674320SMartin Cracauer FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ 379a7674320SMartin Cracauer FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ 380a7674320SMartin Cracauer FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ 381a7674320SMartin Cracauer FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ 382a7674320SMartin Cracauer FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ 383a7674320SMartin Cracauer FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ 384a7674320SMartin Cracauer FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ 385a7674320SMartin Cracauer FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ 386a7674320SMartin Cracauer FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ 387a7674320SMartin Cracauer FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ 388a7674320SMartin Cracauer FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ 389a7674320SMartin Cracauer FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ 390a7674320SMartin Cracauer FPE_FLTSUB, /* 40 - STK */ 391a7674320SMartin Cracauer FPE_FLTSUB, /* 41 - INV | STK */ 392a7674320SMartin Cracauer FPE_FLTUND, /* 42 - DNML | STK */ 393a7674320SMartin Cracauer FPE_FLTSUB, /* 43 - INV | DNML | STK */ 394a7674320SMartin Cracauer FPE_FLTDIV, /* 44 - DZ | STK */ 395a7674320SMartin Cracauer FPE_FLTSUB, /* 45 - INV | DZ | STK */ 396a7674320SMartin Cracauer FPE_FLTDIV, /* 46 - DNML | DZ | STK */ 397a7674320SMartin Cracauer FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ 398a7674320SMartin Cracauer FPE_FLTOVF, /* 48 - OFL | STK */ 399a7674320SMartin Cracauer FPE_FLTSUB, /* 49 - INV | OFL | STK */ 400a7674320SMartin Cracauer FPE_FLTUND, /* 4A - DNML | OFL | STK */ 401a7674320SMartin Cracauer FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ 402a7674320SMartin Cracauer FPE_FLTDIV, /* 4C - DZ | OFL | STK */ 403a7674320SMartin Cracauer FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ 404a7674320SMartin Cracauer FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ 405a7674320SMartin Cracauer FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ 406a7674320SMartin Cracauer FPE_FLTUND, /* 50 - UFL | STK */ 407a7674320SMartin Cracauer FPE_FLTSUB, /* 51 - INV | UFL | STK */ 408a7674320SMartin Cracauer FPE_FLTUND, /* 52 - DNML | UFL | STK */ 409a7674320SMartin Cracauer FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ 410a7674320SMartin Cracauer FPE_FLTDIV, /* 54 - DZ | UFL | STK */ 411a7674320SMartin Cracauer FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ 412a7674320SMartin Cracauer FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ 413a7674320SMartin Cracauer FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ 414a7674320SMartin Cracauer FPE_FLTOVF, /* 58 - OFL | UFL | STK */ 415a7674320SMartin Cracauer FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ 416a7674320SMartin Cracauer FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ 417a7674320SMartin Cracauer FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ 418a7674320SMartin Cracauer FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ 419a7674320SMartin Cracauer FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ 420a7674320SMartin Cracauer FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ 421a7674320SMartin Cracauer FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ 422a7674320SMartin Cracauer FPE_FLTRES, /* 60 - IMP | STK */ 423a7674320SMartin Cracauer FPE_FLTSUB, /* 61 - INV | IMP | STK */ 424a7674320SMartin Cracauer FPE_FLTUND, /* 62 - DNML | IMP | STK */ 425a7674320SMartin Cracauer FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ 426a7674320SMartin Cracauer FPE_FLTDIV, /* 64 - DZ | IMP | STK */ 427a7674320SMartin Cracauer FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ 428a7674320SMartin Cracauer FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ 429a7674320SMartin Cracauer FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ 430a7674320SMartin Cracauer FPE_FLTOVF, /* 68 - OFL | IMP | STK */ 431a7674320SMartin Cracauer FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ 432a7674320SMartin Cracauer FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ 433a7674320SMartin Cracauer FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ 434a7674320SMartin Cracauer FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ 435a7674320SMartin Cracauer FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ 436a7674320SMartin Cracauer FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ 437a7674320SMartin Cracauer FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ 438a7674320SMartin Cracauer FPE_FLTUND, /* 70 - UFL | IMP | STK */ 439a7674320SMartin Cracauer FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ 440a7674320SMartin Cracauer FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ 441a7674320SMartin Cracauer FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ 442a7674320SMartin Cracauer FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ 443a7674320SMartin Cracauer FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ 444a7674320SMartin Cracauer FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ 445a7674320SMartin Cracauer FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ 446a7674320SMartin Cracauer FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ 447a7674320SMartin Cracauer FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ 448a7674320SMartin Cracauer FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ 449a7674320SMartin Cracauer FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ 450a7674320SMartin Cracauer FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ 451a7674320SMartin Cracauer FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ 452a7674320SMartin Cracauer FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ 453a7674320SMartin Cracauer FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ 454a7674320SMartin Cracauer }; 455a7674320SMartin Cracauer 456a7674320SMartin Cracauer /* 45737e52b59SBruce Evans * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 4585b81b6b3SRodney W. Grimes * 45937e52b59SBruce Evans * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 46037e52b59SBruce Evans * depend on longjmp() restoring a usable state. Restoring the state 46137e52b59SBruce Evans * or examining it might fail if we didn't clear exceptions. 4625b81b6b3SRodney W. Grimes * 463a7674320SMartin Cracauer * The error code chosen will be one of the FPE_... macros. It will be 464a7674320SMartin Cracauer * sent as the second argument to old BSD-style signal handlers and as 465a7674320SMartin Cracauer * "siginfo_t->si_code" (second argument) to SA_SIGINFO signal handlers. 46637e52b59SBruce Evans * 46737e52b59SBruce Evans * XXX the FP state is not preserved across signal handlers. So signal 46837e52b59SBruce Evans * handlers cannot afford to do FP unless they preserve the state or 46937e52b59SBruce Evans * longjmp() out. Both preserving the state and longjmp()ing may be 47037e52b59SBruce Evans * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 47137e52b59SBruce Evans * solution for signals other than SIGFPE. 4725b81b6b3SRodney W. Grimes */ 4731c1771cbSBruce Evans int 474bf2f09eeSPeter Wemm fputrap() 4755b81b6b3SRodney W. Grimes { 4761c1771cbSBruce Evans u_short control, status; 4775b81b6b3SRodney W. Grimes 47899753495SKonstantin Belousov critical_enter(); 4795b81b6b3SRodney W. Grimes 4805b81b6b3SRodney W. Grimes /* 4811c1771cbSBruce Evans * Interrupt handling (for another interrupt) may have pushed the 4821c1771cbSBruce Evans * state to memory. Fetch the relevant parts of the state from 4831c1771cbSBruce Evans * wherever they are. 4845b81b6b3SRodney W. Grimes */ 4850bbc8826SJohn Baldwin if (PCPU_GET(fpcurthread) != curthread) { 486b40ce416SJulian Elischer control = GET_FPU_CW(curthread); 487b40ce416SJulian Elischer status = GET_FPU_SW(curthread); 4885b81b6b3SRodney W. Grimes } else { 4891c1771cbSBruce Evans fnstcw(&control); 4901c1771cbSBruce Evans fnstsw(&status); 4915b81b6b3SRodney W. Grimes } 4921c1771cbSBruce Evans 49330abe507SJonathan Mini if (PCPU_GET(fpcurthread) == curthread) 4941c1771cbSBruce Evans fnclex(); 49599753495SKonstantin Belousov critical_exit(); 4961c1771cbSBruce Evans return (fpetable[status & ((~control & 0x3f) | 0x40)]); 4975b81b6b3SRodney W. Grimes } 4985b81b6b3SRodney W. Grimes 4995b81b6b3SRodney W. Grimes /* 5005b81b6b3SRodney W. Grimes * Implement device not available (DNA) exception 5015b81b6b3SRodney W. Grimes * 5020bbc8826SJohn Baldwin * It would be better to switch FP context here (if curthread != fpcurthread) 50337e52b59SBruce Evans * and not necessarily for every context switch, but it is too hard to 50437e52b59SBruce Evans * access foreign pcb's. 5055b81b6b3SRodney W. Grimes */ 50630abe507SJonathan Mini 50730abe507SJonathan Mini static int err_count = 0; 50830abe507SJonathan Mini 509a8346a98SJohn Baldwin void 510a8346a98SJohn Baldwin fpudna(void) 5115b81b6b3SRodney W. Grimes { 51230abe507SJonathan Mini struct pcb *pcb; 51305f6ee66SJake Burkholder 51499753495SKonstantin Belousov critical_enter(); 51530abe507SJonathan Mini if (PCPU_GET(fpcurthread) == curthread) { 516bf2f09eeSPeter Wemm printf("fpudna: fpcurthread == curthread %d times\n", 51730abe507SJonathan Mini ++err_count); 51830abe507SJonathan Mini stop_emulating(); 51999753495SKonstantin Belousov critical_exit(); 520a8346a98SJohn Baldwin return; 52130abe507SJonathan Mini } 5220bbc8826SJohn Baldwin if (PCPU_GET(fpcurthread) != NULL) { 523bf2f09eeSPeter Wemm printf("fpudna: fpcurthread = %p (%d), curthread = %p (%d)\n", 52430abe507SJonathan Mini PCPU_GET(fpcurthread), 52530abe507SJonathan Mini PCPU_GET(fpcurthread)->td_proc->p_pid, 52630abe507SJonathan Mini curthread, curthread->td_proc->p_pid); 527bf2f09eeSPeter Wemm panic("fpudna"); 5285b81b6b3SRodney W. Grimes } 5295b81b6b3SRodney W. Grimes stop_emulating(); 5305b81b6b3SRodney W. Grimes /* 531bf2f09eeSPeter Wemm * Record new context early in case frstor causes a trap. 5325b81b6b3SRodney W. Grimes */ 5330bbc8826SJohn Baldwin PCPU_SET(fpcurthread, curthread); 53430abe507SJonathan Mini pcb = PCPU_GET(curpcb); 5359d146ac5SPeter Wemm 5362652af56SColin Percival fpu_clean_state(); 5372652af56SColin Percival 538bf2f09eeSPeter Wemm if ((pcb->pcb_flags & PCB_FPUINITDONE) == 0) { 5395b81b6b3SRodney W. Grimes /* 54063de9515SJohn Baldwin * This is the first time this thread has used the FPU or 54163de9515SJohn Baldwin * the PCB doesn't contain a clean FPU state. Explicitly 54263de9515SJohn Baldwin * load an initial state. 5435b81b6b3SRodney W. Grimes */ 544*8c6f8f3dSKonstantin Belousov fpurestore(fpu_initialstate); 5452ee8325fSJohn Baldwin if (pcb->pcb_initial_fpucw != __INITIAL_FPUCW__) 54617275403SJung-uk Kim fldcw(pcb->pcb_initial_fpucw); 54760c7c84eSKonstantin Belousov if (PCB_USER_FPU(pcb)) 548e6c006d9SJung-uk Kim set_pcb_flags(pcb, 549e6c006d9SJung-uk Kim PCB_FPUINITDONE | PCB_USERFPUINITDONE); 550e6c006d9SJung-uk Kim else 551e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_FPUINITDONE); 5521c89210cSPeter Wemm } else 553*8c6f8f3dSKonstantin Belousov fpurestore(pcb->pcb_save); 55499753495SKonstantin Belousov critical_exit(); 5555b81b6b3SRodney W. Grimes } 5565b81b6b3SRodney W. Grimes 55730abe507SJonathan Mini void 558bf2f09eeSPeter Wemm fpudrop() 55930abe507SJonathan Mini { 56030abe507SJonathan Mini struct thread *td; 56130abe507SJonathan Mini 56230abe507SJonathan Mini td = PCPU_GET(fpcurthread); 56399753495SKonstantin Belousov KASSERT(td == curthread, ("fpudrop: fpcurthread != curthread")); 5644a23ecc7SKonstantin Belousov CRITICAL_ASSERT(td); 56530abe507SJonathan Mini PCPU_SET(fpcurthread, NULL); 566e6c006d9SJung-uk Kim clear_pcb_flags(td->td_pcb, PCB_FPUINITDONE); 56730abe507SJonathan Mini start_emulating(); 56830abe507SJonathan Mini } 56930abe507SJonathan Mini 57030abe507SJonathan Mini /* 5715c6eb037SKonstantin Belousov * Get the user state of the FPU into pcb->pcb_user_save without 5725c6eb037SKonstantin Belousov * dropping ownership (if possible). It returns the FPU ownership 5735c6eb037SKonstantin Belousov * status. 57430abe507SJonathan Mini */ 57530abe507SJonathan Mini int 5765c6eb037SKonstantin Belousov fpugetregs(struct thread *td) 5776cf9a08dSKonstantin Belousov { 5786cf9a08dSKonstantin Belousov struct pcb *pcb; 5796cf9a08dSKonstantin Belousov 5806cf9a08dSKonstantin Belousov pcb = td->td_pcb; 5816cf9a08dSKonstantin Belousov if ((pcb->pcb_flags & PCB_USERFPUINITDONE) == 0) { 582*8c6f8f3dSKonstantin Belousov bcopy(fpu_initialstate, get_pcb_user_save_pcb(pcb), 583*8c6f8f3dSKonstantin Belousov cpu_max_ext_state_size); 584*8c6f8f3dSKonstantin Belousov get_pcb_user_save_pcb(pcb)->sv_env.en_cw = 585*8c6f8f3dSKonstantin Belousov pcb->pcb_initial_fpucw; 5865c6eb037SKonstantin Belousov fpuuserinited(td); 5875c6eb037SKonstantin Belousov return (_MC_FPOWNED_PCB); 5886cf9a08dSKonstantin Belousov } 58999753495SKonstantin Belousov critical_enter(); 5906cf9a08dSKonstantin Belousov if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) { 591*8c6f8f3dSKonstantin Belousov fpusave(get_pcb_user_save_pcb(pcb)); 59299753495SKonstantin Belousov critical_exit(); 5936cf9a08dSKonstantin Belousov return (_MC_FPOWNED_FPU); 5946cf9a08dSKonstantin Belousov } else { 59599753495SKonstantin Belousov critical_exit(); 5966cf9a08dSKonstantin Belousov return (_MC_FPOWNED_PCB); 5976cf9a08dSKonstantin Belousov } 5986cf9a08dSKonstantin Belousov } 5996cf9a08dSKonstantin Belousov 6005c6eb037SKonstantin Belousov void 6015c6eb037SKonstantin Belousov fpuuserinited(struct thread *td) 60230abe507SJonathan Mini { 6036cf9a08dSKonstantin Belousov struct pcb *pcb; 60430abe507SJonathan Mini 6056cf9a08dSKonstantin Belousov pcb = td->td_pcb; 6065c6eb037SKonstantin Belousov if (PCB_USER_FPU(pcb)) 607e6c006d9SJung-uk Kim set_pcb_flags(pcb, 608e6c006d9SJung-uk Kim PCB_FPUINITDONE | PCB_USERFPUINITDONE); 609e6c006d9SJung-uk Kim else 610e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_FPUINITDONE); 61130abe507SJonathan Mini } 61230abe507SJonathan Mini 613*8c6f8f3dSKonstantin Belousov int 614*8c6f8f3dSKonstantin Belousov fpusetxstate(struct thread *td, char *xfpustate, size_t xfpustate_size) 615*8c6f8f3dSKonstantin Belousov { 616*8c6f8f3dSKonstantin Belousov struct xstate_hdr *hdr, *ehdr; 617*8c6f8f3dSKonstantin Belousov size_t len, max_len; 618*8c6f8f3dSKonstantin Belousov uint64_t bv; 619*8c6f8f3dSKonstantin Belousov 620*8c6f8f3dSKonstantin Belousov /* XXXKIB should we clear all extended state in xstate_bv instead ? */ 621*8c6f8f3dSKonstantin Belousov if (xfpustate == NULL) 622*8c6f8f3dSKonstantin Belousov return (0); 623*8c6f8f3dSKonstantin Belousov if (!use_xsave) 624*8c6f8f3dSKonstantin Belousov return (EOPNOTSUPP); 625*8c6f8f3dSKonstantin Belousov 626*8c6f8f3dSKonstantin Belousov len = xfpustate_size; 627*8c6f8f3dSKonstantin Belousov if (len < sizeof(struct xstate_hdr)) 628*8c6f8f3dSKonstantin Belousov return (EINVAL); 629*8c6f8f3dSKonstantin Belousov max_len = cpu_max_ext_state_size - sizeof(struct savefpu); 630*8c6f8f3dSKonstantin Belousov if (len > max_len) 631*8c6f8f3dSKonstantin Belousov return (EINVAL); 632*8c6f8f3dSKonstantin Belousov 633*8c6f8f3dSKonstantin Belousov ehdr = (struct xstate_hdr *)xfpustate; 634*8c6f8f3dSKonstantin Belousov bv = ehdr->xstate_bv; 635*8c6f8f3dSKonstantin Belousov 636*8c6f8f3dSKonstantin Belousov /* 637*8c6f8f3dSKonstantin Belousov * Avoid #gp. 638*8c6f8f3dSKonstantin Belousov */ 639*8c6f8f3dSKonstantin Belousov if (bv & ~xsave_mask) 640*8c6f8f3dSKonstantin Belousov return (EINVAL); 641*8c6f8f3dSKonstantin Belousov if ((bv & (XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE)) != 642*8c6f8f3dSKonstantin Belousov (XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE)) 643*8c6f8f3dSKonstantin Belousov return (EINVAL); 644*8c6f8f3dSKonstantin Belousov 645*8c6f8f3dSKonstantin Belousov hdr = (struct xstate_hdr *)(get_pcb_user_save_td(td) + 1); 646*8c6f8f3dSKonstantin Belousov 647*8c6f8f3dSKonstantin Belousov hdr->xstate_bv = bv; 648*8c6f8f3dSKonstantin Belousov bcopy(xfpustate + sizeof(struct xstate_hdr), 649*8c6f8f3dSKonstantin Belousov (char *)(hdr + 1), len - sizeof(struct xstate_hdr)); 650*8c6f8f3dSKonstantin Belousov 651*8c6f8f3dSKonstantin Belousov return (0); 652*8c6f8f3dSKonstantin Belousov } 653*8c6f8f3dSKonstantin Belousov 65430abe507SJonathan Mini /* 65530abe507SJonathan Mini * Set the state of the FPU. 65630abe507SJonathan Mini */ 657*8c6f8f3dSKonstantin Belousov int 658*8c6f8f3dSKonstantin Belousov fpusetregs(struct thread *td, struct savefpu *addr, char *xfpustate, 659*8c6f8f3dSKonstantin Belousov size_t xfpustate_size) 6606cf9a08dSKonstantin Belousov { 6616cf9a08dSKonstantin Belousov struct pcb *pcb; 662*8c6f8f3dSKonstantin Belousov int error; 6636cf9a08dSKonstantin Belousov 6646cf9a08dSKonstantin Belousov pcb = td->td_pcb; 66599753495SKonstantin Belousov critical_enter(); 6666cf9a08dSKonstantin Belousov if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) { 667*8c6f8f3dSKonstantin Belousov error = fpusetxstate(td, xfpustate, xfpustate_size); 668*8c6f8f3dSKonstantin Belousov if (error != 0) { 669*8c6f8f3dSKonstantin Belousov critical_exit(); 670*8c6f8f3dSKonstantin Belousov return (error); 671*8c6f8f3dSKonstantin Belousov } 672*8c6f8f3dSKonstantin Belousov bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); 673*8c6f8f3dSKonstantin Belousov fpurestore(get_pcb_user_save_td(td)); 67499753495SKonstantin Belousov critical_exit(); 675e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_FPUINITDONE | PCB_USERFPUINITDONE); 6766cf9a08dSKonstantin Belousov } else { 67799753495SKonstantin Belousov critical_exit(); 678*8c6f8f3dSKonstantin Belousov error = fpusetxstate(td, xfpustate, xfpustate_size); 679*8c6f8f3dSKonstantin Belousov if (error != 0) 680*8c6f8f3dSKonstantin Belousov return (error); 681*8c6f8f3dSKonstantin Belousov bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); 6825c6eb037SKonstantin Belousov fpuuserinited(td); 6836cf9a08dSKonstantin Belousov } 684*8c6f8f3dSKonstantin Belousov return (0); 6856cf9a08dSKonstantin Belousov } 6866cf9a08dSKonstantin Belousov 6876182fdbdSPeter Wemm /* 6882652af56SColin Percival * On AuthenticAMD processors, the fxrstor instruction does not restore 6892652af56SColin Percival * the x87's stored last instruction pointer, last data pointer, and last 6902652af56SColin Percival * opcode values, except in the rare case in which the exception summary 6912652af56SColin Percival * (ES) bit in the x87 status word is set to 1. 6922652af56SColin Percival * 6932652af56SColin Percival * In order to avoid leaking this information across processes, we clean 6942652af56SColin Percival * these values by performing a dummy load before executing fxrstor(). 6952652af56SColin Percival */ 6962652af56SColin Percival static void 6972652af56SColin Percival fpu_clean_state(void) 6982652af56SColin Percival { 699b9dda9d6SJohn Baldwin static float dummy_variable = 0.0; 7002652af56SColin Percival u_short status; 7012652af56SColin Percival 7022652af56SColin Percival /* 7032652af56SColin Percival * Clear the ES bit in the x87 status word if it is currently 7042652af56SColin Percival * set, in order to avoid causing a fault in the upcoming load. 7052652af56SColin Percival */ 7062652af56SColin Percival fnstsw(&status); 7072652af56SColin Percival if (status & 0x80) 7082652af56SColin Percival fnclex(); 7092652af56SColin Percival 7102652af56SColin Percival /* 7112652af56SColin Percival * Load the dummy variable into the x87 stack. This mangles 7122652af56SColin Percival * the x87 stack, but we don't care since we're about to call 7132652af56SColin Percival * fxrstor() anyway. 7142652af56SColin Percival */ 71514965052SDimitry Andric __asm __volatile("ffree %%st(7); flds %0" : : "m" (dummy_variable)); 7162652af56SColin Percival } 7172652af56SColin Percival 7182652af56SColin Percival /* 719398dbb11SPeter Wemm * This really sucks. We want the acpi version only, but it requires 720398dbb11SPeter Wemm * the isa_if.h file in order to get the definitions. 7216182fdbdSPeter Wemm */ 722398dbb11SPeter Wemm #include "opt_isa.h" 723afa88623SPeter Wemm #ifdef DEV_ISA 724398dbb11SPeter Wemm #include <isa/isavar.h> 72554f1d0ceSGarrett Wollman /* 7265f063c7bSMike Smith * This sucks up the legacy ISA support assignments from PNPBIOS/ACPI. 72754f1d0ceSGarrett Wollman */ 728398dbb11SPeter Wemm static struct isa_pnp_id fpupnp_ids[] = { 72954f1d0ceSGarrett Wollman { 0x040cd041, "Legacy ISA coprocessor support" }, /* PNP0C04 */ 73054f1d0ceSGarrett Wollman { 0 } 73154f1d0ceSGarrett Wollman }; 73254f1d0ceSGarrett Wollman 73354f1d0ceSGarrett Wollman static int 734398dbb11SPeter Wemm fpupnp_probe(device_t dev) 73554f1d0ceSGarrett Wollman { 736bb9c06c1SMike Smith int result; 737bf2f09eeSPeter Wemm 738398dbb11SPeter Wemm result = ISA_PNP_PROBE(device_get_parent(dev), dev, fpupnp_ids); 739bf2f09eeSPeter Wemm if (result <= 0) 740bb9c06c1SMike Smith device_quiet(dev); 741bb9c06c1SMike Smith return (result); 74254f1d0ceSGarrett Wollman } 74354f1d0ceSGarrett Wollman 74454f1d0ceSGarrett Wollman static int 745398dbb11SPeter Wemm fpupnp_attach(device_t dev) 74654f1d0ceSGarrett Wollman { 747bf2f09eeSPeter Wemm 74854f1d0ceSGarrett Wollman return (0); 74954f1d0ceSGarrett Wollman } 75054f1d0ceSGarrett Wollman 751398dbb11SPeter Wemm static device_method_t fpupnp_methods[] = { 75254f1d0ceSGarrett Wollman /* Device interface */ 753398dbb11SPeter Wemm DEVMETHOD(device_probe, fpupnp_probe), 754398dbb11SPeter Wemm DEVMETHOD(device_attach, fpupnp_attach), 75554f1d0ceSGarrett Wollman DEVMETHOD(device_detach, bus_generic_detach), 75654f1d0ceSGarrett Wollman DEVMETHOD(device_shutdown, bus_generic_shutdown), 75754f1d0ceSGarrett Wollman DEVMETHOD(device_suspend, bus_generic_suspend), 75854f1d0ceSGarrett Wollman DEVMETHOD(device_resume, bus_generic_resume), 75954f1d0ceSGarrett Wollman 76054f1d0ceSGarrett Wollman { 0, 0 } 76154f1d0ceSGarrett Wollman }; 76254f1d0ceSGarrett Wollman 763398dbb11SPeter Wemm static driver_t fpupnp_driver = { 764398dbb11SPeter Wemm "fpupnp", 765398dbb11SPeter Wemm fpupnp_methods, 76654f1d0ceSGarrett Wollman 1, /* no softc */ 76754f1d0ceSGarrett Wollman }; 76854f1d0ceSGarrett Wollman 769398dbb11SPeter Wemm static devclass_t fpupnp_devclass; 77054f1d0ceSGarrett Wollman 771398dbb11SPeter Wemm DRIVER_MODULE(fpupnp, acpi, fpupnp_driver, fpupnp_devclass, 0, 0); 772586079ccSBruce Evans #endif /* DEV_ISA */ 7736cf9a08dSKonstantin Belousov 774*8c6f8f3dSKonstantin Belousov static MALLOC_DEFINE(M_FPUKERN_CTX, "fpukern_ctx", 775*8c6f8f3dSKonstantin Belousov "Kernel contexts for FPU state"); 776*8c6f8f3dSKonstantin Belousov 777*8c6f8f3dSKonstantin Belousov #define FPU_KERN_CTX_FPUINITDONE 0x01 778*8c6f8f3dSKonstantin Belousov 779*8c6f8f3dSKonstantin Belousov struct fpu_kern_ctx { 780*8c6f8f3dSKonstantin Belousov struct savefpu *prev; 781*8c6f8f3dSKonstantin Belousov uint32_t flags; 782*8c6f8f3dSKonstantin Belousov char hwstate1[]; 783*8c6f8f3dSKonstantin Belousov }; 784*8c6f8f3dSKonstantin Belousov 785*8c6f8f3dSKonstantin Belousov struct fpu_kern_ctx * 786*8c6f8f3dSKonstantin Belousov fpu_kern_alloc_ctx(u_int flags) 787*8c6f8f3dSKonstantin Belousov { 788*8c6f8f3dSKonstantin Belousov struct fpu_kern_ctx *res; 789*8c6f8f3dSKonstantin Belousov size_t sz; 790*8c6f8f3dSKonstantin Belousov 791*8c6f8f3dSKonstantin Belousov sz = sizeof(struct fpu_kern_ctx) + XSAVE_AREA_ALIGN + 792*8c6f8f3dSKonstantin Belousov cpu_max_ext_state_size; 793*8c6f8f3dSKonstantin Belousov res = malloc(sz, M_FPUKERN_CTX, ((flags & FPU_KERN_NOWAIT) ? 794*8c6f8f3dSKonstantin Belousov M_NOWAIT : M_WAITOK) | M_ZERO); 795*8c6f8f3dSKonstantin Belousov return (res); 796*8c6f8f3dSKonstantin Belousov } 797*8c6f8f3dSKonstantin Belousov 798*8c6f8f3dSKonstantin Belousov void 799*8c6f8f3dSKonstantin Belousov fpu_kern_free_ctx(struct fpu_kern_ctx *ctx) 800*8c6f8f3dSKonstantin Belousov { 801*8c6f8f3dSKonstantin Belousov 802*8c6f8f3dSKonstantin Belousov /* XXXKIB clear the memory ? */ 803*8c6f8f3dSKonstantin Belousov free(ctx, M_FPUKERN_CTX); 804*8c6f8f3dSKonstantin Belousov } 805*8c6f8f3dSKonstantin Belousov 806*8c6f8f3dSKonstantin Belousov static struct savefpu * 807*8c6f8f3dSKonstantin Belousov fpu_kern_ctx_savefpu(struct fpu_kern_ctx *ctx) 808*8c6f8f3dSKonstantin Belousov { 809*8c6f8f3dSKonstantin Belousov vm_offset_t p; 810*8c6f8f3dSKonstantin Belousov 811*8c6f8f3dSKonstantin Belousov p = (vm_offset_t)&ctx->hwstate1; 812*8c6f8f3dSKonstantin Belousov p = roundup2(p, XSAVE_AREA_ALIGN); 813*8c6f8f3dSKonstantin Belousov return ((struct savefpu *)p); 814*8c6f8f3dSKonstantin Belousov } 815*8c6f8f3dSKonstantin Belousov 8166cf9a08dSKonstantin Belousov int 8176cf9a08dSKonstantin Belousov fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags) 8186cf9a08dSKonstantin Belousov { 8196cf9a08dSKonstantin Belousov struct pcb *pcb; 8206cf9a08dSKonstantin Belousov 8216cf9a08dSKonstantin Belousov pcb = td->td_pcb; 822*8c6f8f3dSKonstantin Belousov KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save == 823*8c6f8f3dSKonstantin Belousov get_pcb_user_save_pcb(pcb), ("mangled pcb_save")); 8246cf9a08dSKonstantin Belousov ctx->flags = 0; 8256cf9a08dSKonstantin Belousov if ((pcb->pcb_flags & PCB_FPUINITDONE) != 0) 8266cf9a08dSKonstantin Belousov ctx->flags |= FPU_KERN_CTX_FPUINITDONE; 8276cf9a08dSKonstantin Belousov fpuexit(td); 8286cf9a08dSKonstantin Belousov ctx->prev = pcb->pcb_save; 829*8c6f8f3dSKonstantin Belousov pcb->pcb_save = fpu_kern_ctx_savefpu(ctx); 830e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_KERNFPU); 831e6c006d9SJung-uk Kim clear_pcb_flags(pcb, PCB_FPUINITDONE); 8326cf9a08dSKonstantin Belousov return (0); 8336cf9a08dSKonstantin Belousov } 8346cf9a08dSKonstantin Belousov 8356cf9a08dSKonstantin Belousov int 8366cf9a08dSKonstantin Belousov fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx) 8376cf9a08dSKonstantin Belousov { 8386cf9a08dSKonstantin Belousov struct pcb *pcb; 8396cf9a08dSKonstantin Belousov 8406cf9a08dSKonstantin Belousov pcb = td->td_pcb; 84199753495SKonstantin Belousov critical_enter(); 8426cf9a08dSKonstantin Belousov if (curthread == PCPU_GET(fpcurthread)) 8436cf9a08dSKonstantin Belousov fpudrop(); 84499753495SKonstantin Belousov critical_exit(); 8456cf9a08dSKonstantin Belousov pcb->pcb_save = ctx->prev; 846*8c6f8f3dSKonstantin Belousov if (pcb->pcb_save == get_pcb_user_save_pcb(pcb)) { 847e6c006d9SJung-uk Kim if ((pcb->pcb_flags & PCB_USERFPUINITDONE) != 0) { 848e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_FPUINITDONE); 849e6c006d9SJung-uk Kim clear_pcb_flags(pcb, PCB_KERNFPU); 850e6c006d9SJung-uk Kim } else 851e6c006d9SJung-uk Kim clear_pcb_flags(pcb, PCB_FPUINITDONE | PCB_KERNFPU); 8526cf9a08dSKonstantin Belousov } else { 8536cf9a08dSKonstantin Belousov if ((ctx->flags & FPU_KERN_CTX_FPUINITDONE) != 0) 854e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_FPUINITDONE); 8556cf9a08dSKonstantin Belousov else 856e6c006d9SJung-uk Kim clear_pcb_flags(pcb, PCB_FPUINITDONE); 8576cf9a08dSKonstantin Belousov KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave")); 8586cf9a08dSKonstantin Belousov } 8596cf9a08dSKonstantin Belousov return (0); 8606cf9a08dSKonstantin Belousov } 8616cf9a08dSKonstantin Belousov 8626cf9a08dSKonstantin Belousov int 8636cf9a08dSKonstantin Belousov fpu_kern_thread(u_int flags) 8646cf9a08dSKonstantin Belousov { 8656cf9a08dSKonstantin Belousov struct pcb *pcb; 8666cf9a08dSKonstantin Belousov 8676cf9a08dSKonstantin Belousov pcb = PCPU_GET(curpcb); 8686cf9a08dSKonstantin Belousov KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0, 8696cf9a08dSKonstantin Belousov ("Only kthread may use fpu_kern_thread")); 870*8c6f8f3dSKonstantin Belousov KASSERT(pcb->pcb_save == get_pcb_user_save_pcb(pcb), 871*8c6f8f3dSKonstantin Belousov ("mangled pcb_save")); 8726cf9a08dSKonstantin Belousov KASSERT(PCB_USER_FPU(pcb), ("recursive call")); 8736cf9a08dSKonstantin Belousov 874e6c006d9SJung-uk Kim set_pcb_flags(pcb, PCB_KERNFPU); 8756cf9a08dSKonstantin Belousov return (0); 8766cf9a08dSKonstantin Belousov } 8776cf9a08dSKonstantin Belousov 8786cf9a08dSKonstantin Belousov int 8796cf9a08dSKonstantin Belousov is_fpu_kern_thread(u_int flags) 8806cf9a08dSKonstantin Belousov { 8816cf9a08dSKonstantin Belousov 8826cf9a08dSKonstantin Belousov if ((curthread->td_pflags & TDP_KTHREAD) == 0) 8836cf9a08dSKonstantin Belousov return (0); 8846cf9a08dSKonstantin Belousov return ((PCPU_GET(curpcb)->pcb_flags & PCB_KERNFPU) != 0); 8856cf9a08dSKonstantin Belousov } 886