fpu.c revision 91328
137535Sdes/*- 237535Sdes * Copyright (c) 1990 William Jolitz. 337535Sdes * Copyright (c) 1991 The Regents of the University of California. 437535Sdes * All rights reserved. 537535Sdes * 637535Sdes * Redistribution and use in source and binary forms, with or without 737535Sdes * modification, are permitted provided that the following conditions 837535Sdes * are met: 937535Sdes * 1. Redistributions of source code must retain the above copyright 1037535Sdes * notice, this list of conditions and the following disclaimer. 1137535Sdes * 2. Redistributions in binary form must reproduce the above copyright 1237535Sdes * notice, this list of conditions and the following disclaimer in the 1337535Sdes * documentation and/or other materials provided with the distribution. 1437535Sdes * 3. All advertising materials mentioning features or use of this software 1537535Sdes * must display the following acknowledgement: 1637535Sdes * This product includes software developed by the University of 1737535Sdes * California, Berkeley and its contributors. 1837535Sdes * 4. Neither the name of the University nor the names of its contributors 1937535Sdes * may be used to endorse or promote products derived from this software 2037535Sdes * without specific prior written permission. 2137535Sdes * 2237535Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2337535Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2437535Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2537535Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2637535Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2737535Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2850476Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2937535Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3037535Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3137535Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3237571Sdes * SUCH DAMAGE. 3337535Sdes * 3437535Sdes * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 3537535Sdes * $FreeBSD: head/sys/amd64/amd64/fpu.c 91328 2002-02-26 20:33:41Z dillon $ 3637535Sdes */ 3737535Sdes 3837535Sdes#include "opt_cpu.h" 3937535Sdes#include "opt_debug_npx.h" 4037535Sdes#include "opt_isa.h" 4137535Sdes#include "opt_math_emulate.h" 4237535Sdes#include "opt_npx.h" 4337535Sdes 4437535Sdes#include <sys/param.h> 4537535Sdes#include <sys/systm.h> 4637535Sdes#include <sys/bus.h> 4737535Sdes#include <sys/kernel.h> 4837535Sdes#include <sys/lock.h> 4937535Sdes#include <sys/malloc.h> 5037535Sdes#include <sys/module.h> 5137535Sdes#include <sys/mutex.h> 5237535Sdes#include <sys/mutex.h> 5337535Sdes#include <sys/proc.h> 5437535Sdes#include <sys/sysctl.h> 5537535Sdes#include <machine/bus.h> 5637535Sdes#include <sys/rman.h> 5737535Sdes#ifdef NPX_DEBUG 5841862Sdes#include <sys/syslog.h> 5937535Sdes#endif 6037535Sdes#include <sys/signalvar.h> 6137535Sdes#include <sys/user.h> 6237535Sdes 6375891Sarchie#ifndef SMP 6455557Sdes#include <machine/asmacros.h> 6567430Sdes#endif 6660188Sdes#include <machine/cputypes.h> 6737573Sdes#include <machine/frame.h> 6837535Sdes#include <machine/md_var.h> 6937571Sdes#include <machine/pcb.h> 7037535Sdes#include <machine/psl.h> 7141869Sdes#ifndef SMP 7237571Sdes#include <machine/clock.h> 7337535Sdes#endif 7437535Sdes#include <machine/resource.h> 7540939Sdes#include <machine/specialreg.h> 7641862Sdes#include <machine/segments.h> 7737535Sdes 7870795Sdes#ifndef SMP 7937535Sdes#include <i386/isa/icu.h> 8064883Sdes#ifdef PC98 8137573Sdes#include <pc98/pc98/pc98.h> 8237573Sdes#else 8341869Sdes#include <i386/isa/isa.h> 8441863Sdes#endif 8567890Sdes#endif 8637573Sdes#include <i386/isa/intr_machdep.h> 8760737Sume#ifdef DEV_ISA 8860737Sume#include <isa/isavar.h> 8937573Sdes#endif 9037573Sdes 9137573Sdes/* 9237573Sdes * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 9360188Sdes */ 9455557Sdes 9563336Sdes/* Configuration flags. */ 9637573Sdes#define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) 9740975Sdes#define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) 9855557Sdes#define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) 9937535Sdes#define NPX_PREFER_EMULATOR (1 << 3) 10055557Sdes 10155557Sdes#ifdef __GNUC__ 10255557Sdes 10337571Sdes#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 10455557Sdes#define fnclex() __asm("fnclex") 10560707Sdes#define fninit() __asm("fninit") 10660707Sdes#define fnsave(addr) __asm __volatile("fnsave %0" : "=m" (*(addr))) 10755557Sdes#define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) 10855557Sdes#define fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) 10955557Sdes#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 11060737Sume#define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 11160737Sume#ifdef CPU_ENABLE_SSE 11260737Sume#define fxrstor(addr) __asm("fxrstor %0" : : "m" (*(addr))) 11360737Sume#define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr))) 11460737Sume#endif 11560737Sume#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 11660737Sume : : "n" (CR0_TS) : "ax") 11760737Sume#define stop_emulating() __asm("clts") 11860737Sume 11960737Sume#else /* not __GNUC__ */ 12060737Sume 12160737Sumevoid fldcw __P((caddr_t addr)); 12260737Sumevoid fnclex __P((void)); 12360737Sumevoid fninit __P((void)); 12460737Sumevoid fnsave __P((caddr_t addr)); 12560737Sumevoid fnstcw __P((caddr_t addr)); 12660737Sumevoid fnstsw __P((caddr_t addr)); 12760737Sumevoid fp_divide_by_0 __P((void)); 12860737Sumevoid frstor __P((caddr_t addr)); 12960737Sume#ifdef CPU_ENABLE_SSE 13060737Sumevoid fxsave __P((caddr_t addr)); 13137571Sdesvoid fxrstor __P((caddr_t addr)); 13255557Sdes#endif 13337535Sdesvoid start_emulating __P((void)); 13437535Sdesvoid stop_emulating __P((void)); 13555557Sdes 13637535Sdes#endif /* __GNUC__ */ 13762215Sdes 13862215Sdes#ifdef CPU_ENABLE_SSE 13962215Sdes#define GET_FPU_CW(thread) \ 14062215Sdes (cpu_fxsr ? \ 14162215Sdes (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_cw : \ 14269044Sdes (thread)->td_pcb->pcb_save.sv_87.sv_env.en_cw) 14369043Sdes#define GET_FPU_SW(thread) \ 14462215Sdes (cpu_fxsr ? \ 14562215Sdes (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_sw : \ 14662215Sdes (thread)->td_pcb->pcb_save.sv_87.sv_env.en_sw) 14762215Sdes#define GET_FPU_EXSW_PTR(pcb) \ 14862215Sdes (cpu_fxsr ? \ 14955557Sdes &(pcb)->pcb_save.sv_xmm.sv_ex_sw : \ 15055557Sdes &(pcb)->pcb_save.sv_87.sv_ex_sw) 15155557Sdes#else /* CPU_ENABLE_SSE */ 15255557Sdes#define GET_FPU_CW(thread) \ 15337573Sdes (thread->td_pcb->pcb_save.sv_87.sv_env.en_cw) 15455557Sdes#define GET_FPU_SW(thread) \ 15563336Sdes (thread->td_pcb->pcb_save.sv_87.sv_env.en_sw) 15637535Sdes#define GET_FPU_EXSW_PTR(pcb) \ 15737571Sdes (&(pcb)->pcb_save.sv_87.sv_ex_sw) 15837535Sdes#endif /* CPU_ENABLE_SSE */ 15955557Sdes 16055557Sdestypedef u_char bool_t; 16155557Sdes 16255557Sdesstatic int npx_attach __P((device_t dev)); 16355557Sdesstatic void npx_identify __P((driver_t *driver, device_t parent)); 16437535Sdes#ifndef SMP 16537535Sdesstatic void npx_intr __P((void *)); 16637535Sdes#endif 16737573Sdesstatic int npx_probe __P((device_t dev)); 16837535Sdesstatic void fpusave __P((union savefpu *)); 16937535Sdesstatic void fpurstor __P((union savefpu *)); 17075891Sarchie#ifdef I586_CPU_XXX 17137535Sdesstatic long timezero __P((const char *funcname, 17237573Sdes void (*func)(void *buf, size_t len))); 17362982Sdes#endif /* I586_CPU */ 17455557Sdes 17555557Sdesint hw_float; /* XXX currently just alias for npx_exists */ 17637573Sdes 17737573SdesSYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 17862982Sdes CTLFLAG_RD, &hw_float, 0, 17955557Sdes "Floatingpoint instructions executed in hardware"); 18055557Sdes 18155557Sdes#ifndef SMP 18255557Sdesstatic volatile u_int npx_intrs_while_probing; 18355557Sdesstatic volatile u_int npx_traps_while_probing; 18455557Sdes#endif 18555557Sdes 18662982Sdesstatic bool_t npx_ex16; 18762982Sdesstatic bool_t npx_exists; 18855557Sdesstatic bool_t npx_irq13; 18962982Sdes 19055557Sdes#ifndef SMP 19155557Sdesalias_for_inthand_t probetrap; 19255557Sdes__asm(" \n\ 19355557Sdes .text \n\ 19437571Sdes .p2align 2,0x90 \n\ 19555557Sdes .type " __XSTRING(CNAME(probetrap)) ",@function \n\ 19637535Sdes" __XSTRING(CNAME(probetrap)) ": \n\ 19737535Sdes ss \n\ 19837535Sdes incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ 19963340Sdes fnclex \n\ 20063340Sdes iret \n\ 20175891Sarchie"); 20275891Sarchie#endif /* SMP */ 20363340Sdes 20463340Sdes/* 20563340Sdes * Identify routine. Create a connection point on our parent for probing. 20663340Sdes */ 20763340Sdesstatic void 20863340Sdesnpx_identify(driver, parent) 20963340Sdes driver_t *driver; 21063340Sdes device_t parent; 21163340Sdes{ 21263340Sdes device_t child; 21363340Sdes 21463340Sdes child = BUS_ADD_CHILD(parent, 0, "npx", 0); 21563340Sdes if (child == NULL) 21663340Sdes panic("npx_identify"); 21775891Sarchie} 21863340Sdes 21963340Sdes#ifndef SMP 22063340Sdes/* 22163340Sdes * Do minimal handling of npx interrupts to convert them to traps. 22263585Sdes */ 22363340Sdesstatic void 22463340Sdesnpx_intr(dummy) 22563340Sdes void *dummy; 22663340Sdes{ 22763340Sdes struct thread *td; 22863340Sdes 22963340Sdes#ifndef SMP 23063340Sdes npx_intrs_while_probing++; 23163340Sdes#endif 23263340Sdes 23363340Sdes /* 23463340Sdes * The BUSY# latch must be cleared in all cases so that the next 23563340Sdes * unmasked npx exception causes an interrupt. 23663340Sdes */ 23763340Sdes#ifdef PC98 23875891Sarchie outb(0xf8, 0); 23963340Sdes#else 24075891Sarchie outb(0xf0, 0); 24175891Sarchie#endif 24263340Sdes 24363340Sdes /* 24463340Sdes * fpcurthread is normally non-null here. In that case, schedule an 24563340Sdes * AST to finish the exception handling in the correct context 24663392Sdes * (this interrupt may occur after the thread has entered the 24763392Sdes * kernel via a syscall or an interrupt). Otherwise, the npx 24863392Sdes * state of the thread that caused this interrupt must have been 24963340Sdes * pushed to the thread's pcb, and clearing of the busy latch 25063340Sdes * above has finished the (essentially null) handling of this 25163340Sdes * interrupt. Control will eventually return to the instruction 25263340Sdes * that caused it and it will repeat. We will eventually (usually 25363340Sdes * soon) win the race to handle the interrupt properly. 25463340Sdes */ 25563340Sdes td = PCPU_GET(fpcurthread); 25663340Sdes if (td != NULL) { 25763340Sdes td->td_pcb->pcb_flags |= PCB_NPXTRAP; 25863340Sdes mtx_lock_spin(&sched_lock); 25963340Sdes td->td_kse->ke_flags |= KEF_ASTPENDING; 26063340Sdes mtx_unlock_spin(&sched_lock); 26163340Sdes } 26263340Sdes} 26363340Sdes#endif /* !SMP */ 26475292Sdes 26563340Sdes/* 26663340Sdes * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 26763847Sdes * whether the device exists or not (XXX should be elsewhere). Set flags 26863847Sdes * to tell npxattach() what to do. Modify device struct if npx doesn't 26963340Sdes * need to use interrupts. Return 0 if device exists. 27063340Sdes */ 27163340Sdesstatic int 27263340Sdesnpx_probe(dev) 27363340Sdes device_t dev; 27463340Sdes{ 27563340Sdes#ifndef SMP 27663340Sdes struct gate_descriptor save_idt_npxtrap; 27763340Sdes struct resource *ioport_res, *irq_res; 27863340Sdes void *irq_cookie; 27963340Sdes int ioport_rid, irq_num, irq_rid; 28063340Sdes u_short control; 28163340Sdes u_short status; 28263340Sdes 28363340Sdes save_idt_npxtrap = idt[16]; 28463340Sdes setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 28563340Sdes ioport_rid = 0; 28663340Sdes ioport_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &ioport_rid, 28763340Sdes IO_NPX, IO_NPX, IO_NPXSIZE, RF_ACTIVE); 28863340Sdes if (ioport_res == NULL) 28963340Sdes panic("npx: can't get ports"); 29063340Sdes#ifdef PC98 29163340Sdes if (resource_int_value("npx", 0, "irq", &irq_num) != 0) 29263340Sdes irq_num = 8; 29363340Sdes#else 29463340Sdes if (resource_int_value("npx", 0, "irq", &irq_num) != 0) 29563340Sdes irq_num = 13; 29663340Sdes#endif 29763340Sdes irq_rid = 0; 29863340Sdes irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &ioport_rid, irq_num, 29963340Sdes irq_num, 1, RF_ACTIVE); 30063340Sdes if (irq_res == NULL) 30163340Sdes panic("npx: can't get IRQ"); 30263340Sdes if (bus_setup_intr(dev, irq_res, INTR_TYPE_MISC | INTR_FAST, npx_intr, 30363340Sdes NULL, &irq_cookie) != 0) 30463340Sdes panic("npx: can't create intr"); 30563340Sdes#endif /* !SMP */ 30663340Sdes 30763340Sdes /* 30863340Sdes * Partially reset the coprocessor, if any. Some BIOS's don't reset 30963340Sdes * it after a warm boot. 31063340Sdes */ 31167430Sdes#ifdef PC98 31267430Sdes outb(0xf8,0); 31367430Sdes#else 31467430Sdes outb(0xf1, 0); /* full reset on some systems, NOP on others */ 31567430Sdes outb(0xf0, 0); /* clear BUSY# latch */ 31667430Sdes#endif 31767430Sdes /* 31867430Sdes * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 31967430Sdes * instructions. We must set the CR0_MP bit and use the CR0_TS 32067430Sdes * bit to control the trap, because setting the CR0_EM bit does 32167430Sdes * not cause WAIT instructions to trap. It's important to trap 32267430Sdes * WAIT instructions - otherwise the "wait" variants of no-wait 32367430Sdes * control instructions would degenerate to the "no-wait" variants 32467430Sdes * after FP context switches but work correctly otherwise. It's 32567430Sdes * particularly important to trap WAITs when there is no NPX - 32667430Sdes * otherwise the "wait" variants would always degenerate. 32767430Sdes * 32867430Sdes * Try setting CR0_NE to get correct error reporting on 486DX's. 32967430Sdes * Setting it should fail or do nothing on lesser processors. 33067430Sdes */ 33167430Sdes load_cr0(rcr0() | CR0_MP | CR0_NE); 33267430Sdes /* 33367890Sdes * But don't trap while we're probing. 33467890Sdes */ 33567890Sdes stop_emulating(); 33667890Sdes /* 33767430Sdes * Finish resetting the coprocessor, if any. If there is an error 33867430Sdes * pending, then we may get a bogus IRQ13, but npx_intr() will handle 33967430Sdes * it OK. Bogus halts have never been observed, but we enabled 34067430Sdes * IRQ13 and cleared the BUSY# latch early to handle them anyway. 34167430Sdes */ 34267430Sdes fninit(); 34367430Sdes 34467430Sdes device_set_desc(dev, "math processor"); 34567430Sdes 34667430Sdes#ifdef SMP 34767430Sdes 34867430Sdes /* 34967430Sdes * Exception 16 MUST work for SMP. 35067430Sdes */ 35167430Sdes npx_ex16 = hw_float = npx_exists = 1; 35267430Sdes return (0); 35367430Sdes 35473934Sdes#else /* !SMP */ 35573934Sdes 35667430Sdes /* 35767430Sdes * Don't use fwait here because it might hang. 35867430Sdes * Don't use fnop here because it usually hangs if there is no FPU. 35967430Sdes */ 36067430Sdes DELAY(1000); /* wait for any IRQ13 */ 36167430Sdes#ifdef DIAGNOSTIC 36267430Sdes if (npx_intrs_while_probing != 0) 36367430Sdes printf("fninit caused %u bogus npx interrupt(s)\n", 36467430Sdes npx_intrs_while_probing); 36567430Sdes if (npx_traps_while_probing != 0) 36667890Sdes printf("fninit caused %u bogus npx trap(s)\n", 36767890Sdes npx_traps_while_probing); 36867890Sdes#endif 36967890Sdes /* 37067430Sdes * Check for a status of mostly zero. 37167430Sdes */ 37267430Sdes status = 0x5a5a; 37367430Sdes fnstsw(&status); 37467430Sdes if ((status & 0xb8ff) == 0) { 37567430Sdes /* 37667430Sdes * Good, now check for a proper control word. 37767430Sdes */ 37867430Sdes control = 0x5a5a; 37967430Sdes fnstcw(&control); 38067430Sdes if ((control & 0x1f3f) == 0x033f) { 38173934Sdes hw_float = npx_exists = 1; 38273934Sdes /* 38367430Sdes * We have an npx, now divide by 0 to see if exception 38467430Sdes * 16 works. 38567430Sdes */ 38667430Sdes control &= ~(1 << 2); /* enable divide by 0 trap */ 38767430Sdes fldcw(&control); 38867430Sdes#ifdef FPU_ERROR_BROKEN 38967890Sdes /* 39067890Sdes * FPU error signal doesn't work on some CPU 39167890Sdes * accelerator board. 39267890Sdes */ 39367890Sdes npx_ex16 = 1; 39467890Sdes return (0); 39567890Sdes#endif 39667430Sdes npx_traps_while_probing = npx_intrs_while_probing = 0; 39767430Sdes fp_divide_by_0(); 39867430Sdes if (npx_traps_while_probing != 0) { 39967430Sdes /* 40067430Sdes * Good, exception 16 works. 40167430Sdes */ 40267430Sdes npx_ex16 = 1; 40367430Sdes goto no_irq13; 40467890Sdes } 40567430Sdes if (npx_intrs_while_probing != 0) { 40667430Sdes /* 40767890Sdes * Bad, we are stuck with IRQ13. 40867890Sdes */ 40967890Sdes npx_irq13 = 1; 41067890Sdes idt[16] = save_idt_npxtrap; 41167430Sdes return (0); 41267430Sdes } 41367430Sdes /* 41467430Sdes * Worse, even IRQ13 is broken. Use emulator. 41567430Sdes */ 41667430Sdes } 41767707Sdes } 41867430Sdes /* 41967430Sdes * Probe failed, but we want to get to npxattach to initialize the 42067707Sdes * emulator and say that it has been installed. XXX handle devices 42177234Sdes * that aren't really devices better. 42267430Sdes */ 42377234Sdes /* FALLTHROUGH */ 42477234Sdesno_irq13: 42567430Sdes idt[16] = save_idt_npxtrap; 42667430Sdes bus_teardown_intr(dev, irq_res, irq_cookie); 42767430Sdes 42867430Sdes /* 42967430Sdes * XXX hack around brokenness of bus_teardown_intr(). If we left the 43067430Sdes * irq active then we would get it instead of exception 16. 43167430Sdes */ 43267430Sdes mtx_lock_spin(&icu_lock); 43367430Sdes INTRDIS(1 << irq_num); 43467430Sdes mtx_unlock_spin(&icu_lock); 43567430Sdes 43667430Sdes bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res); 43767430Sdes bus_release_resource(dev, SYS_RES_IOPORT, ioport_rid, ioport_res); 43867430Sdes return (0); 43967430Sdes 44067430Sdes#endif /* SMP */ 44167430Sdes} 44267430Sdes 44367430Sdes/* 44467430Sdes * Attach routine - announce which it is, and wire into system 44567430Sdes */ 44637608Sdesint 44737535Sdesnpx_attach(dev) 44837535Sdes device_t dev; 44975891Sarchie{ 45075891Sarchie int flags; 45137535Sdes 45260737Sume if (resource_int_value("npx", 0, "flags", &flags) != 0) 45360737Sume flags = 0; 45460737Sume 45574716Sdes if (flags) 45655544Sdes device_printf(dev, "flags 0x%x ", flags); 45755544Sdes if (npx_irq13) { 45837573Sdes device_printf(dev, "using IRQ 13 interface\n"); 45937573Sdes } else { 46055544Sdes#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 46155544Sdes if (npx_ex16) { 46274716Sdes if (!(flags & NPX_PREFER_EMULATOR)) 46367892Sdes device_printf(dev, "INT 16 interface\n"); 46467892Sdes else { 46555544Sdes device_printf(dev, "FPU exists, but flags request " 46660951Sdes "emulator\n"); 46767259Sdes hw_float = npx_exists = 0; 46869670Sdes } 46967259Sdes } else if (npx_exists) { 47060951Sdes device_printf(dev, "error reporting broken; using 387 emulator\n"); 47160737Sume hw_float = npx_exists = 0; 47260737Sume } else 47360737Sume device_printf(dev, "387 emulator\n"); 47460737Sume#else 47560737Sume if (npx_ex16) { 47660737Sume device_printf(dev, "INT 16 interface\n"); 47760737Sume if (flags & NPX_PREFER_EMULATOR) { 47837573Sdes device_printf(dev, "emulator requested, but none compiled " 47960737Sume "into kernel, using FPU\n"); 48040939Sdes } 48137573Sdes } else 48237573Sdes device_printf(dev, "no 387 emulator in kernel and no FPU!\n"); 48337573Sdes#endif 48437573Sdes } 48560737Sume npxinit(__INITIAL_NPXCW__); 48637573Sdes 48737573Sdes#ifdef I586_CPU_XXX 48860737Sume if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && 48937573Sdes timezero("i586_bzero()", i586_bzero) < 49037573Sdes timezero("bzero()", bzero) * 4 / 5) { 49155544Sdes if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { 49255544Sdes bcopy_vector = i586_bcopy; 49360737Sume ovbcopy_vector = i586_bcopy; 49460737Sume } 49560737Sume if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) 49660737Sume bzero = i586_bzero; 49760737Sume if (!(flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { 49860737Sume copyin_vector = i586_copyin; 49960737Sume copyout_vector = i586_copyout; 50060737Sume } 50160737Sume } 50260737Sume#endif 50360737Sume 50460737Sume return (0); /* XXX unused */ 50560737Sume} 50660737Sume 50763336Sdes/* 50837573Sdes * Initialize floating point unit. 50960737Sume */ 51037573Sdesvoid 51155544Sdesnpxinit(control) 51255544Sdes u_short control; 51355544Sdes{ 51455544Sdes static union savefpu dummy; 51555557Sdes critical_t savecrit; 51662888Sume 51760737Sume if (!npx_exists) 51860737Sume return; 51962888Sume /* 52062888Sume * fninit has the same h/w bugs as fnsave. Use the detoxified 52162888Sume * fnsave to throw away any junk in the fpu. npxsave() initializes 52263336Sdes * the fpu and sets fpcurthread = NULL as important side effects. 52362888Sume */ 52462888Sume savecrit = cpu_critical_enter(); 52560737Sume npxsave(&dummy); 52660737Sume stop_emulating(); 52760737Sume#ifdef CPU_ENABLE_SSE 52860737Sume /* XXX npxsave() doesn't actually initialize the fpu in the SSE case. */ 52963336Sdes if (cpu_fxsr) 53060737Sume fninit(); 53160737Sume#endif 53260737Sume fldcw(&control); 53360737Sume if (PCPU_GET(curpcb) != NULL) 53462888Sume fpusave(&PCPU_GET(curpcb)->pcb_save); 53562888Sume start_emulating(); 53662888Sume cpu_critical_exit(savecrit); 53763336Sdes} 53862888Sume 53962888Sume/* 54062888Sume * Free coprocessor (if we have it). 54160737Sume */ 54260737Sumevoid 54360737Sumenpxexit(td) 54460737Sume struct thread *td; 54563336Sdes{ 54660737Sume critical_t savecrit; 54760737Sume 54860737Sume savecrit = cpu_critical_enter(); 54960737Sume if (td == PCPU_GET(fpcurthread)) 55037573Sdes npxsave(&PCPU_GET(curpcb)->pcb_save); 55160188Sdes cpu_critical_exit(savecrit); 55260188Sdes#ifdef NPX_DEBUG 55360188Sdes if (npx_exists) { 55460188Sdes u_int masked_exceptions; 55560188Sdes 55637573Sdes masked_exceptions = PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_cw 55760188Sdes & PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_sw & 0x7f; 55855557Sdes /* 55937573Sdes * Log exceptions that would have trapped with the old 56060737Sume * control word (overflow, divide by 0, and invalid operand). 56160737Sume */ 56260737Sume if (masked_exceptions & 0x0d) 56360737Sume log(LOG_ERR, 56460737Sume "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 56560737Sume td->td_proc->p_pid, td->td_proc->p_comm, 56660737Sume masked_exceptions); 56760737Sume } 56860737Sume#endif 56960737Sume} 57060737Sume 57160737Sume/* 57260737Sume * The following mechanism is used to ensure that the FPE_... value 57360737Sume * that is passed as a trapcode to the signal handler of the user 57460737Sume * process does not have more than one bit set. 57560737Sume * 57660737Sume * Multiple bits may be set if the user process modifies the control 57760737Sume * word while a status word bit is already set. While this is a sign 57860737Sume * of bad coding, we have no choise than to narrow them down to one 57960737Sume * bit, since we must not send a trapcode that is not exactly one of 58060737Sume * the FPE_ macros. 58160737Sume * 58263336Sdes * The mechanism has a static table with 127 entries. Each combination 58360737Sume * of the 7 FPU status word exception bits directly translates to a 58460737Sume * position in this table, where a single FPE_... value is stored. 58537573Sdes * This FPE_... value stored there is considered the "most important" 58637573Sdes * of the exception bits and will be sent as the signal code. The 58755544Sdes * precedence of the bits is based upon Intel Document "Numerical 58855544Sdes * Applications", Chapter "Special Computational Situations". 58960737Sume * 59037573Sdes * The macro to choose one of these values does these steps: 1) Throw 59160188Sdes * away status word bits that cannot be masked. 2) Throw away the bits 59237573Sdes * currently masked in the control word, assuming the user isn't 59361866Sdes * interested in them anymore. 3) Reinsert status word bit 7 (stack 59461866Sdes * fault) if it is set, which cannot be masked but must be presered. 59563340Sdes * 4) Use the remaining bits to point into the trapcode table. 59664883Sdes * 59737573Sdes * The 6 maskable bits in order of their preference, as stated in the 59837573Sdes * above referenced Intel manual: 59937573Sdes * 1 Invalid operation (FP_X_INV) 60037573Sdes * 1a Stack underflow 60137573Sdes * 1b Stack overflow 60255544Sdes * 1c Operand of unsupported format 60360737Sume * 1d SNaN operand. 60460737Sume * 2 QNaN operand (not an exception, irrelavant here) 60537573Sdes * 3 Any other invalid-operation not mentioned above or zero divide 60660737Sume * (FP_X_INV, FP_X_DZ) 60760737Sume * 4 Denormal operand (FP_X_DNML) 60860737Sume * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) 60960737Sume * 6 Inexact result (FP_X_IMP) 61074716Sdes */ 61160737Sumestatic char fpetable[128] = { 61260737Sume 0, 61360737Sume FPE_FLTINV, /* 1 - INV */ 61460737Sume FPE_FLTUND, /* 2 - DNML */ 61560737Sume FPE_FLTINV, /* 3 - INV | DNML */ 61660737Sume FPE_FLTDIV, /* 4 - DZ */ 61760737Sume FPE_FLTINV, /* 5 - INV | DZ */ 61874716Sdes FPE_FLTDIV, /* 6 - DNML | DZ */ 61960737Sume FPE_FLTINV, /* 7 - INV | DNML | DZ */ 62060737Sume FPE_FLTOVF, /* 8 - OFL */ 62160737Sume FPE_FLTINV, /* 9 - INV | OFL */ 62260737Sume FPE_FLTUND, /* A - DNML | OFL */ 62360737Sume FPE_FLTINV, /* B - INV | DNML | OFL */ 62455544Sdes FPE_FLTDIV, /* C - DZ | OFL */ 62555544Sdes FPE_FLTINV, /* D - INV | DZ | OFL */ 62660737Sume FPE_FLTDIV, /* E - DNML | DZ | OFL */ 62737573Sdes FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ 62838394Sdes FPE_FLTUND, /* 10 - UFL */ 62937573Sdes FPE_FLTINV, /* 11 - INV | UFL */ 63037573Sdes FPE_FLTUND, /* 12 - DNML | UFL */ 63137573Sdes FPE_FLTINV, /* 13 - INV | DNML | UFL */ 63238394Sdes FPE_FLTDIV, /* 14 - DZ | UFL */ 63337573Sdes FPE_FLTINV, /* 15 - INV | DZ | UFL */ 63460737Sume FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ 63560737Sume FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ 63660737Sume FPE_FLTOVF, /* 18 - OFL | UFL */ 63760737Sume FPE_FLTINV, /* 19 - INV | OFL | UFL */ 63860737Sume FPE_FLTUND, /* 1A - DNML | OFL | UFL */ 63960737Sume FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ 64060737Sume FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ 64160737Sume FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ 64260737Sume FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ 64360737Sume FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ 64460737Sume FPE_FLTRES, /* 20 - IMP */ 64560737Sume FPE_FLTINV, /* 21 - INV | IMP */ 64660737Sume FPE_FLTUND, /* 22 - DNML | IMP */ 64760737Sume FPE_FLTINV, /* 23 - INV | DNML | IMP */ 64860737Sume FPE_FLTDIV, /* 24 - DZ | IMP */ 64960737Sume FPE_FLTINV, /* 25 - INV | DZ | IMP */ 65060737Sume FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ 65160737Sume FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ 65260737Sume FPE_FLTOVF, /* 28 - OFL | IMP */ 65360737Sume FPE_FLTINV, /* 29 - INV | OFL | IMP */ 65460737Sume FPE_FLTUND, /* 2A - DNML | OFL | IMP */ 65560737Sume FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ 65660737Sume FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ 65760737Sume FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ 65860737Sume FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ 65960737Sume FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ 66060737Sume FPE_FLTUND, /* 30 - UFL | IMP */ 66160737Sume FPE_FLTINV, /* 31 - INV | UFL | IMP */ 66260737Sume FPE_FLTUND, /* 32 - DNML | UFL | IMP */ 66360737Sume FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ 66460737Sume FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ 66560737Sume FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ 66660737Sume FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ 66760737Sume FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ 66860737Sume FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ 66960737Sume FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ 67060737Sume FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ 67163336Sdes FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ 67260737Sume FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ 67360737Sume FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ 67441869Sdes FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ 67537573Sdes FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ 67637573Sdes FPE_FLTSUB, /* 40 - STK */ 67762256Sdes FPE_FLTSUB, /* 41 - INV | STK */ 67862256Sdes FPE_FLTUND, /* 42 - DNML | STK */ 67962256Sdes FPE_FLTSUB, /* 43 - INV | DNML | STK */ 68062256Sdes FPE_FLTDIV, /* 44 - DZ | STK */ 68162256Sdes FPE_FLTSUB, /* 45 - INV | DZ | STK */ 68237573Sdes FPE_FLTDIV, /* 46 - DNML | DZ | STK */ 68355544Sdes FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ 68455544Sdes FPE_FLTOVF, /* 48 - OFL | STK */ 68563340Sdes FPE_FLTSUB, /* 49 - INV | OFL | STK */ 68641869Sdes FPE_FLTUND, /* 4A - DNML | OFL | STK */ 68737573Sdes FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ 68837573Sdes FPE_FLTDIV, /* 4C - DZ | OFL | STK */ 68937573Sdes FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ 69038394Sdes FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ 69137573Sdes FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ 69237573Sdes FPE_FLTUND, /* 50 - UFL | STK */ 69337573Sdes FPE_FLTSUB, /* 51 - INV | UFL | STK */ 69437573Sdes FPE_FLTUND, /* 52 - DNML | UFL | STK */ 69537573Sdes FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ 69667430Sdes FPE_FLTDIV, /* 54 - DZ | UFL | STK */ 69737573Sdes FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ 69837573Sdes FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ 69937573Sdes FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ 70037573Sdes FPE_FLTOVF, /* 58 - OFL | UFL | STK */ 70140939Sdes FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ 70260737Sume FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ 70360737Sume FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ 70441869Sdes FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ 70541869Sdes FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ 70637573Sdes FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ 70755557Sdes FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ 70855557Sdes FPE_FLTRES, /* 60 - IMP | STK */ 70960737Sume FPE_FLTSUB, /* 61 - INV | IMP | STK */ 71060737Sume FPE_FLTUND, /* 62 - DNML | IMP | STK */ 71137535Sdes FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ 71237535Sdes FPE_FLTDIV, /* 64 - DZ | IMP | STK */ 71337535Sdes FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ 71437571Sdes FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ 71577238Sdes FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ 71677238Sdes FPE_FLTOVF, /* 68 - OFL | IMP | STK */ 71777238Sdes FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ 71877238Sdes FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ 71977238Sdes FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ 72077238Sdes FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ 72177238Sdes FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ 72277238Sdes FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ 72377238Sdes FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ 72477238Sdes FPE_FLTUND, /* 70 - UFL | IMP | STK */ 72577238Sdes FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ 72677238Sdes FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ 72777238Sdes FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ 72877238Sdes FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ 72977238Sdes FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ 73077238Sdes FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ 73177238Sdes FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ 73277238Sdes FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ 73377238Sdes FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ 73477238Sdes FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ 73577238Sdes FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ 73677238Sdes FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ 73777238Sdes FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ 73877238Sdes FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ 73977238Sdes FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ 74077238Sdes}; 74177238Sdes 74277238Sdes/* 74377238Sdes * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 74477238Sdes * 74577238Sdes * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 74677238Sdes * depend on longjmp() restoring a usable state. Restoring the state 74777238Sdes * or examining it might fail if we didn't clear exceptions. 74877238Sdes * 74977238Sdes * The error code chosen will be one of the FPE_... macros. It will be 75077238Sdes * sent as the second argument to old BSD-style signal handlers and as 75177238Sdes * "siginfo_t->si_code" (second argument) to SA_SIGINFO signal handlers. 75277238Sdes * 75377238Sdes * XXX the FP state is not preserved across signal handlers. So signal 75477238Sdes * handlers cannot afford to do FP unless they preserve the state or 75577238Sdes * longjmp() out. Both preserving the state and longjmp()ing may be 75677238Sdes * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 75777238Sdes * solution for signals other than SIGFPE. 75837571Sdes */ 75937535Sdesint 76055557Sdesnpxtrap() 76175891Sarchie{ 76237571Sdes critical_t savecrit; 76367043Sdes u_short control, status; 76460737Sume u_long *exstat; 76560737Sume 76660737Sume if (!npx_exists) { 76760737Sume printf("npxtrap: fpcurthread = %p, curthread = %p, npx_exists = %d\n", 76860737Sume PCPU_GET(fpcurthread), curthread, npx_exists); 76937571Sdes panic("npxtrap from nowhere"); 77067892Sdes } 77167892Sdes savecrit = cpu_critical_enter(); 77267892Sdes 77360737Sume /* 77467892Sdes * Interrupt handling (for another interrupt) may have pushed the 77560737Sume * state to memory. Fetch the relevant parts of the state from 77660737Sume * wherever they are. 77767043Sdes */ 77867043Sdes if (PCPU_GET(fpcurthread) != curthread) { 77967043Sdes control = GET_FPU_CW(curthread); 78037608Sdes status = GET_FPU_SW(curthread); 78167043Sdes } else { 78267043Sdes fnstcw(&control); 78367043Sdes fnstsw(&status); 78437608Sdes } 78537608Sdes 78667043Sdes exstat = GET_FPU_EXSW_PTR(curthread->td_pcb); 78767043Sdes *exstat = status; 78837608Sdes if (PCPU_GET(fpcurthread) != curthread) 78937608Sdes GET_FPU_SW(curthread) &= ~0x80bf; 79037608Sdes else 79155557Sdes fnclex(); 79240939Sdes cpu_critical_exit(savecrit); 79337571Sdes return (fpetable[status & ((~control & 0x3f) | 0x40)]); 79437571Sdes} 79537608Sdes 79637571Sdes/* 79755557Sdes * Implement device not available (DNA) exception 79837571Sdes * 79937571Sdes * It would be better to switch FP context here (if curthread != fpcurthread) 80077238Sdes * and not necessarily for every context switch, but it is too hard to 80177238Sdes * access foreign pcb's. 80241863Sdes */ 80337608Sdesint 80437571Sdesnpxdna() 80537571Sdes{ 80655557Sdes u_long *exstat; 80741869Sdes critical_t s; 80837571Sdes 80955557Sdes if (!npx_exists) 81041869Sdes return (0); 81137571Sdes if (PCPU_GET(fpcurthread) != NULL) { 81237571Sdes printf("npxdna: fpcurthread = %p, curthread = %p\n", 81355557Sdes PCPU_GET(fpcurthread), curthread); 81437571Sdes panic("npxdna"); 81537571Sdes } 81655557Sdes s = cpu_critical_enter(); 81755557Sdes stop_emulating(); 81855557Sdes /* 81937571Sdes * Record new context early in case frstor causes an IRQ13. 82037571Sdes */ 82137571Sdes PCPU_SET(fpcurthread, curthread); 82237571Sdes 82337571Sdes exstat = GET_FPU_EXSW_PTR(PCPU_GET(curpcb)); 82437571Sdes *exstat = 0; 82537571Sdes /* 82655557Sdes * The following frstor may cause an IRQ13 when the state being 82737571Sdes * restored has a pending error. The error will appear to have been 82855557Sdes * triggered by the current (npx) user instruction even when that 82955557Sdes * instruction is a no-wait instruction that should not trigger an 83037571Sdes * error (e.g., fnclex). On at least one 486 system all of the 83137571Sdes * no-wait instructions are broken the same as frstor, so our 83237571Sdes * treatment does not amplify the breakage. On at least one 83337571Sdes * 386/Cyrix 387 system, fnclex works correctly while frstor and 83437571Sdes * fnsave are broken, so our treatment breaks fnclex if it is the 83537571Sdes * first FPU instruction after a context switch. 83640975Sdes */ 83737571Sdes fpurstor(&PCPU_GET(curpcb)->pcb_save); 83837571Sdes cpu_critical_exit(s); 83937571Sdes 84037571Sdes return (1); 84137571Sdes} 84237571Sdes 84337571Sdes/* 84437571Sdes * Wrapper for fnsave instruction, partly to handle hardware bugs. When npx 84537608Sdes * exceptions are reported via IRQ13, spurious IRQ13's may be triggered by 84641869Sdes * no-wait npx instructions. See the Intel application note AP-578 for 84737608Sdes * details. This doesn't cause any additional complications here. IRQ13's 84855557Sdes * are inherently asynchronous unless the CPU is frozen to deliver them -- 84975891Sarchie * one that started in userland may be delivered many instructions later, 85037535Sdes * after the process has entered the kernel. It may even be delivered after 85155557Sdes * the fnsave here completes. A spurious IRQ13 for the fnsave is handled in 85237535Sdes * the same way as a very-late-arriving non-spurious IRQ13 from user mode: 85355557Sdes * it is normally ignored at first because we set fpcurthread to NULL; it is 85441869Sdes * normally retriggered in npxdna() after return to user mode. 85537571Sdes * 85663842Sdes * npxsave() must be called with interrupts disabled, so that it clears 85768551Sdes * fpcurthread atomically with saving the state. We require callers to do the 85837535Sdes * disabling, since most callers need to disable interrupts anyway to call 85941863Sdes * npxsave() atomically with checking fpcurthread. 86055557Sdes * 86155557Sdes * A previous version of npxsave() went to great lengths to excecute fnsave 86255557Sdes * with interrupts enabled in case executing it froze the CPU. This case 86367043Sdes * can't happen, at least for Intel CPU/NPX's. Spurious IRQ13's don't imply 86455557Sdes * spurious freezes. 86537571Sdes */ 86637571Sdesvoid 86767043Sdesnpxsave(addr) 86867043Sdes union savefpu *addr; 86967043Sdes{ 87067043Sdes 87167043Sdes stop_emulating(); 87267043Sdes fpusave(addr); 87355557Sdes 87437535Sdes start_emulating(); 87537535Sdes PCPU_SET(fpcurthread, NULL); 87637571Sdes} 87767043Sdes 87863713Sdesstatic void 87967043Sdesfpusave(addr) 88067043Sdes union savefpu *addr; 88163713Sdes{ 88267043Sdes 88363713Sdes#ifdef CPU_ENABLE_SSE 88467043Sdes if (cpu_fxsr) 88573932Sdes fxsave(addr); 88673932Sdes else 88767043Sdes#endif 88869272Sdes fnsave(addr); 88973932Sdes} 89069272Sdes 89169272Sdesstatic void 89269272Sdesfpurstor(addr) 89369272Sdes union savefpu *addr; 89467043Sdes{ 89568551Sdes 89667043Sdes#ifdef CPU_ENABLE_SSE 89767043Sdes if (cpu_fxsr) 89867043Sdes fxrstor(addr); 89967043Sdes else 90067043Sdes#endif 90167043Sdes frstor(addr); 90263713Sdes} 90363713Sdes 90463713Sdes#ifdef I586_CPU_XXX 90563340Sdesstatic long 90637571Sdestimezero(funcname, func) 90737535Sdes const char *funcname; 90875891Sarchie void (*func) __P((void *buf, size_t len)); 90937608Sdes 91067043Sdes{ 91155557Sdes void *buf; 91263713Sdes#define BUFSIZE 1048576 91367043Sdes long usec; 91467892Sdes struct timeval finish, start; 91567043Sdes 91667043Sdes buf = malloc(BUFSIZE, M_TEMP, M_NOWAIT); 91767043Sdes if (buf == NULL) 91867043Sdes return (BUFSIZE); 91967043Sdes microtime(&start); 92055557Sdes (*func)(buf, BUFSIZE); 92141869Sdes microtime(&finish); 92267043Sdes usec = 1000000 * (finish.tv_sec - start.tv_sec) + 92367043Sdes finish.tv_usec - start.tv_usec; 92467043Sdes if (usec <= 0) 92567043Sdes usec = 1; 92641869Sdes if (bootverbose) 92741869Sdes printf("%s bandwidth = %u kBps\n", funcname, 92863340Sdes (u_int32_t)(((BUFSIZE >> 10) * 1000000) / usec)); 92963340Sdes free(buf, M_TEMP); 93063340Sdes return (usec); 93163340Sdes} 93263340Sdes#endif /* I586_CPU */ 93363392Sdes 93463910Sdesstatic device_method_t npx_methods[] = { 93563392Sdes /* Device interface */ 93663340Sdes DEVMETHOD(device_identify, npx_identify), 93763340Sdes DEVMETHOD(device_probe, npx_probe), 93841869Sdes DEVMETHOD(device_attach, npx_attach), 93967430Sdes DEVMETHOD(device_detach, bus_generic_detach), 94037608Sdes DEVMETHOD(device_shutdown, bus_generic_shutdown), 94137608Sdes DEVMETHOD(device_suspend, bus_generic_suspend), 94241869Sdes DEVMETHOD(device_resume, bus_generic_resume), 94363340Sdes 94463340Sdes { 0, 0 } 94563340Sdes}; 94675891Sarchie 94763340Sdesstatic driver_t npx_driver = { 94863340Sdes "npx", 94963340Sdes npx_methods, 95063340Sdes 1, /* no softc */ 95163340Sdes}; 95241869Sdes 95341869Sdesstatic devclass_t npx_devclass; 95437608Sdes 95575891Sarchie#ifdef DEV_ISA 95637535Sdes/* 95767043Sdes * We prefer to attach to the root nexus so that the usual case (exception 16) 95855557Sdes * doesn't describe the processor as being `on isa'. 95941869Sdes */ 96067043SdesDRIVER_MODULE(npx, nexus, npx_driver, npx_devclass, 0, 0); 96167892Sdes 96267043Sdes/* 96367043Sdes * This sucks up the legacy ISA support assignments from PNPBIOS/ACPI. 96467043Sdes */ 96567043Sdesstatic struct isa_pnp_id npxisa_ids[] = { 96667043Sdes { 0x040cd041, "Legacy ISA coprocessor support" }, /* PNP0C04 */ 96767043Sdes { 0 } 96863713Sdes}; 96941869Sdes 97067043Sdesstatic int 97167043Sdesnpxisa_probe(device_t dev) 97267043Sdes{ 97367043Sdes int result; 97441869Sdes if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, npxisa_ids)) <= 0) { 97541869Sdes device_quiet(dev); 97663340Sdes } 97763340Sdes return(result); 97863340Sdes} 97963340Sdes 98041869Sdesstatic int 98167892Sdesnpxisa_attach(device_t dev) 98267430Sdes{ 98337535Sdes return (0); 98440975Sdes} 98541869Sdes 98641869Sdesstatic device_method_t npxisa_methods[] = { 98741869Sdes /* Device interface */ 98840975Sdes DEVMETHOD(device_probe, npxisa_probe), 98975891Sarchie DEVMETHOD(device_attach, npxisa_attach), 99040975Sdes DEVMETHOD(device_detach, bus_generic_detach), 99167043Sdes DEVMETHOD(device_shutdown, bus_generic_shutdown), 99263340Sdes DEVMETHOD(device_suspend, bus_generic_suspend), 99341869Sdes DEVMETHOD(device_resume, bus_generic_resume), 99467043Sdes 99567892Sdes { 0, 0 } 99667043Sdes}; 99767043Sdes 99867043Sdesstatic driver_t npxisa_driver = { 99967043Sdes "npxisa", 100067043Sdes npxisa_methods, 100167043Sdes 1, /* no softc */ 100267043Sdes}; 100367043Sdes 100467043Sdesstatic devclass_t npxisa_devclass; 100567043Sdes 100667043SdesDRIVER_MODULE(npxisa, isa, npxisa_driver, npxisa_devclass, 0, 0); 100763713Sdes#ifndef PC98 100841869SdesDRIVER_MODULE(npxisa, acpi, npxisa_driver, npxisa_devclass, 0, 0); 100967043Sdes#endif 101067043Sdes#endif /* DEV_ISA */ 101167043Sdes