/* $OpenBSD: fpu.c,v 1.44 2023/05/22 00:39:57 guenther Exp $ */ /* $NetBSD: fpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */ /*- * Copyright (c) 1994, 1995, 1998 Charles M. Hannum. All rights reserved. * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include /* * The mask of enabled XSAVE features. */ uint64_t xsave_mask; static int x86fpflags_to_siginfo(u_int32_t); /* * Size of the area needed to save the FPU state and other * XSAVE-supported state components. */ size_t fpu_save_len = sizeof(struct fxsave64); /* * The mxcsr_mask for this host, taken from fxsave() on the primary CPU */ uint32_t fpu_mxcsr_mask; /* * Init the FPU. */ void fpuinit(struct cpu_info *ci) { fninit(); if (fpu_mxcsr_mask == 0) { struct fxsave64 fx __attribute__((aligned(16))); bzero(&fx, sizeof(fx)); fxsave(&fx); if (fx.fx_mxcsr_mask) fpu_mxcsr_mask = fx.fx_mxcsr_mask; else fpu_mxcsr_mask = __INITIAL_MXCSR_MASK__; } } /* * Record the FPU state and reinitialize it all except for the control word. * Returns the code to include in an SIGFPE. * * Reinitializing the state allows naive SIGFPE handlers to longjmp without * doing any fixups. */ int fputrap(int type) { struct cpu_info *ci = curcpu(); struct proc *p = curproc; struct savefpu *sfp = &p->p_addr->u_pcb.pcb_savefpu; u_int32_t mxcsr, statbits; u_int16_t cw; KASSERT(ci->ci_pflags & CPUPF_USERXSTATE); ci->ci_pflags &= ~CPUPF_USERXSTATE; fpusavereset(sfp); if (type == T_XMM) { mxcsr = sfp->fp_fxsave.fx_mxcsr; statbits = mxcsr; mxcsr &= ~0x3f; ldmxcsr(&mxcsr); } else { fninit(); fwait(); cw = sfp->fp_fxsave.fx_fcw; fldcw(&cw); fwait(); statbits = sfp->fp_fxsave.fx_fsw; } return x86fpflags_to_siginfo(statbits); } static int x86fpflags_to_siginfo(u_int32_t flags) { int i; static int x86fp_siginfo_table[] = { FPE_FLTINV, /* bit 0 - invalid operation */ FPE_FLTRES, /* bit 1 - denormal operand */ FPE_FLTDIV, /* bit 2 - divide by zero */ FPE_FLTOVF, /* bit 3 - fp overflow */ FPE_FLTUND, /* bit 4 - fp underflow */ FPE_FLTRES, /* bit 5 - fp precision */ FPE_FLTINV, /* bit 6 - stack fault */ }; for (i = 0; i < nitems(x86fp_siginfo_table); i++) { if (flags & (1 << i)) return (x86fp_siginfo_table[i]); } /* punt if flags not set */ return (FPE_FLTINV); } void fpu_kernel_enter(void) { struct cpu_info *ci = curcpu(); /* save curproc's FPU state if we haven't already */ if (ci->ci_pflags & CPUPF_USERXSTATE) { ci->ci_pflags &= ~CPUPF_USERXSTATE; fpusavereset(&curproc->p_addr->u_pcb.pcb_savefpu); } else { fpureset(); } } void fpu_kernel_exit(void) { /* make sure we don't leave anything in the registers */ fpureset(); }