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 * 3. All advertising materials mentioning features or use of this software 155b81b6b3SRodney W. Grimes * must display the following acknowledgement: 165b81b6b3SRodney W. Grimes * This product includes software developed by the University of 175b81b6b3SRodney W. Grimes * California, Berkeley and its contributors. 185b81b6b3SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 195b81b6b3SRodney W. Grimes * may be used to endorse or promote products derived from this software 205b81b6b3SRodney W. Grimes * without specific prior written permission. 215b81b6b3SRodney W. Grimes * 225b81b6b3SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 235b81b6b3SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 245b81b6b3SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 255b81b6b3SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 265b81b6b3SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 275b81b6b3SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 285b81b6b3SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 295b81b6b3SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 305b81b6b3SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 315b81b6b3SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 325b81b6b3SRodney W. Grimes * SUCH DAMAGE. 335b81b6b3SRodney W. Grimes * 345c644711SRodney W. Grimes * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 357f47cf2fSBruce Evans * $Id: npx.c,v 1.52 1997/08/21 06:32:58 charnier Exp $ 365b81b6b3SRodney W. Grimes */ 375b81b6b3SRodney W. Grimes 385b81b6b3SRodney W. Grimes #include "npx.h" 395b81b6b3SRodney W. Grimes #if NNPX > 0 405b81b6b3SRodney W. Grimes 411fe04850SBruce Evans #include "opt_cpu.h" 42d9256606SPeter Wemm #include "opt_math_emulate.h" 43a73af3a2SGarrett Wollman 44f540b106SGarrett Wollman #include <sys/param.h> 45f540b106SGarrett Wollman #include <sys/systm.h> 463a34a5c3SPoul-Henning Kamp #include <sys/kernel.h> 473a34a5c3SPoul-Henning Kamp #include <sys/sysctl.h> 48f540b106SGarrett Wollman #include <sys/conf.h> 49f540b106SGarrett Wollman #include <sys/proc.h> 5026add149SBruce Evans #ifdef NPX_DEBUG 51663f1485SBruce Evans #include <sys/syslog.h> 5226add149SBruce Evans #endif 53663f1485SBruce Evans #include <sys/signalvar.h> 542f86936aSGarrett Wollman 559081eec1SJohn Polstra #include <machine/asmacros.h> 567f47cf2fSBruce Evans #include <machine/cputypes.h> 577f47cf2fSBruce Evans #include <machine/frame.h> 585400ed3bSPeter Wemm #include <machine/ipl.h> 59c673fe98SBruce Evans #include <machine/md_var.h> 605400ed3bSPeter Wemm #include <machine/pcb.h> 617f47cf2fSBruce Evans #include <machine/psl.h> 62663f1485SBruce Evans #include <machine/clock.h> 63f540b106SGarrett Wollman #include <machine/specialreg.h> 647f47cf2fSBruce Evans #include <machine/segments.h> 652f86936aSGarrett Wollman 66f540b106SGarrett Wollman #include <i386/isa/icu.h> 67f540b106SGarrett Wollman #include <i386/isa/isa_device.h> 6868352337SDoug Rabson #include <i386/isa/intr_machdep.h> 69f540b106SGarrett Wollman #include <i386/isa/isa.h> 705b81b6b3SRodney W. Grimes 715b81b6b3SRodney W. Grimes /* 725b81b6b3SRodney W. Grimes * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 735b81b6b3SRodney W. Grimes */ 745b81b6b3SRodney W. Grimes 751fe04850SBruce Evans /* Configuration flags. */ 761fe04850SBruce Evans #define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) 771fe04850SBruce Evans #define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) 781fe04850SBruce Evans #define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) 791fe04850SBruce Evans 801fe04850SBruce Evans /* XXX - should be in header file. */ 811fe04850SBruce Evans extern void (*bcopy_vector) __P((const void *from, void *to, size_t len)); 821fe04850SBruce Evans extern void (*ovbcopy_vector) __P((const void *from, void *to, size_t len)); 831fe04850SBruce Evans extern int (*copyin_vector) __P((const void *udaddr, void *kaddr, size_t len)); 841fe04850SBruce Evans extern int (*copyout_vector) __P((const void *kaddr, void *udaddr, size_t len)); 851fe04850SBruce Evans 861fe04850SBruce Evans void i586_bcopy __P((const void *from, void *to, size_t len)); 871fe04850SBruce Evans void i586_bzero __P((void *buf, size_t len)); 881fe04850SBruce Evans int i586_copyin __P((const void *udaddr, void *kaddr, size_t len)); 891fe04850SBruce Evans int i586_copyout __P((const void *kaddr, void *udaddr, size_t len)); 901fe04850SBruce Evans 915b81b6b3SRodney W. Grimes #ifdef __GNUC__ 925b81b6b3SRodney W. Grimes 9337e52b59SBruce Evans #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 945b81b6b3SRodney W. Grimes #define fnclex() __asm("fnclex") 955b81b6b3SRodney W. Grimes #define fninit() __asm("fninit") 9637e52b59SBruce Evans #define fnop() __asm("fnop") 9737e52b59SBruce Evans #define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 9837e52b59SBruce Evans #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 9937e52b59SBruce Evans #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 10037e52b59SBruce Evans #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 10137e52b59SBruce Evans #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 1025b81b6b3SRodney W. Grimes #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 1035b81b6b3SRodney W. Grimes : : "n" (CR0_TS) : "ax") 1045b81b6b3SRodney W. Grimes #define stop_emulating() __asm("clts") 1055b81b6b3SRodney W. Grimes 1065b81b6b3SRodney W. Grimes #else /* not __GNUC__ */ 1075b81b6b3SRodney W. Grimes 1085b81b6b3SRodney W. Grimes void fldcw __P((caddr_t addr)); 1095b81b6b3SRodney W. Grimes void fnclex __P((void)); 1105b81b6b3SRodney W. Grimes void fninit __P((void)); 11137e52b59SBruce Evans void fnop __P((void)); 1125b81b6b3SRodney W. Grimes void fnsave __P((caddr_t addr)); 1135b81b6b3SRodney W. Grimes void fnstcw __P((caddr_t addr)); 1145b81b6b3SRodney W. Grimes void fnstsw __P((caddr_t addr)); 1155b81b6b3SRodney W. Grimes void fp_divide_by_0 __P((void)); 1165b81b6b3SRodney W. Grimes void frstor __P((caddr_t addr)); 1175b81b6b3SRodney W. Grimes void start_emulating __P((void)); 1185b81b6b3SRodney W. Grimes void stop_emulating __P((void)); 1195b81b6b3SRodney W. Grimes 1205b81b6b3SRodney W. Grimes #endif /* __GNUC__ */ 1215b81b6b3SRodney W. Grimes 1225b81b6b3SRodney W. Grimes typedef u_char bool_t; 1235b81b6b3SRodney W. Grimes 1245b81b6b3SRodney W. Grimes static int npxattach __P((struct isa_device *dvp)); 1255b81b6b3SRodney W. Grimes static int npxprobe __P((struct isa_device *dvp)); 1265b81b6b3SRodney W. Grimes static int npxprobe1 __P((struct isa_device *dvp)); 1275b81b6b3SRodney W. Grimes 1285b81b6b3SRodney W. Grimes struct isa_driver npxdriver = { 1295b81b6b3SRodney W. Grimes npxprobe, npxattach, "npx", 1305b81b6b3SRodney W. Grimes }; 1315b81b6b3SRodney W. Grimes 13237e52b59SBruce Evans int hw_float; /* XXX currently just alias for npx_exists */ 1333a34a5c3SPoul-Henning Kamp 1343a34a5c3SPoul-Henning Kamp SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 1353a34a5c3SPoul-Henning Kamp CTLFLAG_RD, &hw_float, 0, 1363a34a5c3SPoul-Henning Kamp "Floatingpoint instructions executed in hardware"); 1373a34a5c3SPoul-Henning Kamp 1386f4e0bebSPoul-Henning Kamp static u_int npx0_imask = SWI_CLOCK_MASK; 139b3196e4bSPeter Wemm 140b3196e4bSPeter Wemm #ifndef SMP /* XXX per-cpu on smp */ 1415b81b6b3SRodney W. Grimes struct proc *npxproc; 142477a642cSPeter Wemm #endif 1435b81b6b3SRodney W. Grimes 1445b81b6b3SRodney W. Grimes static bool_t npx_ex16; 1455b81b6b3SRodney W. Grimes static bool_t npx_exists; 1465b81b6b3SRodney W. Grimes static struct gate_descriptor npx_idt_probeintr; 1475b81b6b3SRodney W. Grimes static int npx_intrno; 1485b81b6b3SRodney W. Grimes static volatile u_int npx_intrs_while_probing; 1495b81b6b3SRodney W. Grimes static bool_t npx_irq13; 1505b81b6b3SRodney W. Grimes static volatile u_int npx_traps_while_probing; 1515b81b6b3SRodney W. Grimes 1523902c3efSSteve Passe #ifndef SMP 1535b81b6b3SRodney W. Grimes /* 1545b81b6b3SRodney W. Grimes * Special interrupt handlers. Someday intr0-intr15 will be used to count 1555b81b6b3SRodney W. Grimes * interrupts. We'll still need a special exception 16 handler. The busy 15637e52b59SBruce Evans * latch stuff in probeintr() can be moved to npxprobe(). 1575b81b6b3SRodney W. Grimes */ 158663f1485SBruce Evans inthand_t probeintr; 159477a642cSPeter Wemm 1605b81b6b3SRodney W. Grimes asm 1615b81b6b3SRodney W. Grimes (" 1625b81b6b3SRodney W. Grimes .text 1639081eec1SJohn Polstra .p2align 2,0x90 1649081eec1SJohn Polstra " __XSTRING(CNAME(probeintr)) ": 1655b81b6b3SRodney W. Grimes ss 1669081eec1SJohn Polstra incl " __XSTRING(CNAME(npx_intrs_while_probing)) " 1675b81b6b3SRodney W. Grimes pushl %eax 16819bcb5e4SPaul Richards movb $0x20,%al # EOI (asm in strings loses cpp features) 16919bcb5e4SPaul Richards outb %al,$0xa0 # IO_ICU2 17019bcb5e4SPaul Richards outb %al,$0x20 # IO_ICU1 1715b81b6b3SRodney W. Grimes movb $0,%al 17219bcb5e4SPaul Richards outb %al,$0xf0 # clear BUSY# latch 1735b81b6b3SRodney W. Grimes popl %eax 1745b81b6b3SRodney W. Grimes iret 1755b81b6b3SRodney W. Grimes "); 1765b81b6b3SRodney W. Grimes 177663f1485SBruce Evans inthand_t probetrap; 1785b81b6b3SRodney W. Grimes asm 1795b81b6b3SRodney W. Grimes (" 1805b81b6b3SRodney W. Grimes .text 1819081eec1SJohn Polstra .p2align 2,0x90 1829081eec1SJohn Polstra " __XSTRING(CNAME(probetrap)) ": 1835b81b6b3SRodney W. Grimes ss 1849081eec1SJohn Polstra incl " __XSTRING(CNAME(npx_traps_while_probing)) " 1855b81b6b3SRodney W. Grimes fnclex 1865b81b6b3SRodney W. Grimes iret 1875b81b6b3SRodney W. Grimes "); 1883902c3efSSteve Passe #endif /* SMP */ 1893902c3efSSteve Passe 1905b81b6b3SRodney W. Grimes 1915b81b6b3SRodney W. Grimes /* 1925b81b6b3SRodney W. Grimes * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 1935b81b6b3SRodney W. Grimes * whether the device exists or not (XXX should be elsewhere). Set flags 1945b81b6b3SRodney W. Grimes * to tell npxattach() what to do. Modify device struct if npx doesn't 1955b81b6b3SRodney W. Grimes * need to use interrupts. Return 1 if device exists. 1965b81b6b3SRodney W. Grimes */ 1975b81b6b3SRodney W. Grimes static int 1985b81b6b3SRodney W. Grimes npxprobe(dvp) 1995b81b6b3SRodney W. Grimes struct isa_device *dvp; 2005b81b6b3SRodney W. Grimes { 2013902c3efSSteve Passe #ifdef SMP 2023902c3efSSteve Passe 2033902c3efSSteve Passe return npxprobe1(dvp); 2043902c3efSSteve Passe 2053902c3efSSteve Passe #else /* SMP */ 2063902c3efSSteve Passe 2075b81b6b3SRodney W. Grimes int result; 2085b81b6b3SRodney W. Grimes u_long save_eflags; 2095b81b6b3SRodney W. Grimes u_char save_icu1_mask; 2105b81b6b3SRodney W. Grimes u_char save_icu2_mask; 2115b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 2125b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxtrap; 2135b81b6b3SRodney W. Grimes /* 2145b81b6b3SRodney W. Grimes * This routine is now just a wrapper for npxprobe1(), to install 2155b81b6b3SRodney W. Grimes * special npx interrupt and trap handlers, to enable npx interrupts 2165b81b6b3SRodney W. Grimes * and to disable other interrupts. Someday isa_configure() will 2175b81b6b3SRodney W. Grimes * install suitable handlers and run with interrupts enabled so we 2185b81b6b3SRodney W. Grimes * won't need to do so much here. 2195b81b6b3SRodney W. Grimes */ 2205b81b6b3SRodney W. Grimes npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 2215b81b6b3SRodney W. Grimes save_eflags = read_eflags(); 2225b81b6b3SRodney W. Grimes disable_intr(); 2235b81b6b3SRodney W. Grimes save_icu1_mask = inb(IO_ICU1 + 1); 2245b81b6b3SRodney W. Grimes save_icu2_mask = inb(IO_ICU2 + 1); 2255b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 2265b81b6b3SRodney W. Grimes save_idt_npxtrap = idt[16]; 2275b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 2285b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 2292838c968SDavid Greenman setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 2302838c968SDavid Greenman setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 2315b81b6b3SRodney W. Grimes npx_idt_probeintr = idt[npx_intrno]; 2325b81b6b3SRodney W. Grimes enable_intr(); 2335b81b6b3SRodney W. Grimes result = npxprobe1(dvp); 2345b81b6b3SRodney W. Grimes disable_intr(); 2355b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, save_icu1_mask); 2365b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, save_icu2_mask); 2375b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 2385b81b6b3SRodney W. Grimes idt[16] = save_idt_npxtrap; 2395b81b6b3SRodney W. Grimes write_eflags(save_eflags); 2405b81b6b3SRodney W. Grimes return (result); 2413902c3efSSteve Passe 2423902c3efSSteve Passe #endif /* SMP */ 2435b81b6b3SRodney W. Grimes } 2445b81b6b3SRodney W. Grimes 2455b81b6b3SRodney W. Grimes static int 2465b81b6b3SRodney W. Grimes npxprobe1(dvp) 2475b81b6b3SRodney W. Grimes struct isa_device *dvp; 2485b81b6b3SRodney W. Grimes { 24937e52b59SBruce Evans u_short control; 25037e52b59SBruce Evans u_short status; 25137e52b59SBruce Evans 2525b81b6b3SRodney W. Grimes /* 2535b81b6b3SRodney W. Grimes * Partially reset the coprocessor, if any. Some BIOS's don't reset 2545b81b6b3SRodney W. Grimes * it after a warm boot. 2555b81b6b3SRodney W. Grimes */ 2565b81b6b3SRodney W. Grimes outb(0xf1, 0); /* full reset on some systems, NOP on others */ 2575b81b6b3SRodney W. Grimes outb(0xf0, 0); /* clear BUSY# latch */ 2585b81b6b3SRodney W. Grimes /* 2595b81b6b3SRodney W. Grimes * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 2605b81b6b3SRodney W. Grimes * instructions. We must set the CR0_MP bit and use the CR0_TS 2615b81b6b3SRodney W. Grimes * bit to control the trap, because setting the CR0_EM bit does 2625b81b6b3SRodney W. Grimes * not cause WAIT instructions to trap. It's important to trap 2635b81b6b3SRodney W. Grimes * WAIT instructions - otherwise the "wait" variants of no-wait 2645b81b6b3SRodney W. Grimes * control instructions would degenerate to the "no-wait" variants 2655b81b6b3SRodney W. Grimes * after FP context switches but work correctly otherwise. It's 2665b81b6b3SRodney W. Grimes * particularly important to trap WAITs when there is no NPX - 2675b81b6b3SRodney W. Grimes * otherwise the "wait" variants would always degenerate. 2685b81b6b3SRodney W. Grimes * 2695b81b6b3SRodney W. Grimes * Try setting CR0_NE to get correct error reporting on 486DX's. 2705b81b6b3SRodney W. Grimes * Setting it should fail or do nothing on lesser processors. 2715b81b6b3SRodney W. Grimes */ 2725b81b6b3SRodney W. Grimes load_cr0(rcr0() | CR0_MP | CR0_NE); 2735b81b6b3SRodney W. Grimes /* 2745b81b6b3SRodney W. Grimes * But don't trap while we're probing. 2755b81b6b3SRodney W. Grimes */ 2765b81b6b3SRodney W. Grimes stop_emulating(); 2775b81b6b3SRodney W. Grimes /* 2785b81b6b3SRodney W. Grimes * Finish resetting the coprocessor, if any. If there is an error 2795b81b6b3SRodney W. Grimes * pending, then we may get a bogus IRQ13, but probeintr() will handle 2805b81b6b3SRodney W. Grimes * it OK. Bogus halts have never been observed, but we enabled 2815b81b6b3SRodney W. Grimes * IRQ13 and cleared the BUSY# latch early to handle them anyway. 2825b81b6b3SRodney W. Grimes */ 2835b81b6b3SRodney W. Grimes fninit(); 2843902c3efSSteve Passe 2853902c3efSSteve Passe #ifdef SMP 2863902c3efSSteve Passe 2873902c3efSSteve Passe /* 2883902c3efSSteve Passe * Exception 16 MUST work for SMP. 2893902c3efSSteve Passe */ 2903902c3efSSteve Passe npx_irq13 = 0; 2913902c3efSSteve Passe npx_ex16 = hw_float = npx_exists = 1; 2923902c3efSSteve Passe dvp->id_irq = 0; /* zap the interrupt */ 2933902c3efSSteve Passe /* 2943902c3efSSteve Passe * special return value to flag that we do not 2953902c3efSSteve Passe * actually use any I/O registers 2963902c3efSSteve Passe */ 2973902c3efSSteve Passe return (-1); 2983902c3efSSteve Passe 2993902c3efSSteve Passe #else /* SMP */ 3003902c3efSSteve Passe 3017f3ae831SBruce Evans /* 3027f3ae831SBruce Evans * Don't use fwait here because it might hang. 3037f3ae831SBruce Evans * Don't use fnop here because it usually hangs if there is no FPU. 3047f3ae831SBruce Evans */ 30537e52b59SBruce Evans DELAY(1000); /* wait for any IRQ13 */ 3065b81b6b3SRodney W. Grimes #ifdef DIAGNOSTIC 3075b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) 3085b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx interrupt(s)\n", 3095b81b6b3SRodney W. Grimes npx_intrs_while_probing); 3105b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) 3115b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx trap(s)\n", 3125b81b6b3SRodney W. Grimes npx_traps_while_probing); 3135b81b6b3SRodney W. Grimes #endif 3145b81b6b3SRodney W. Grimes /* 3155b81b6b3SRodney W. Grimes * Check for a status of mostly zero. 3165b81b6b3SRodney W. Grimes */ 3175b81b6b3SRodney W. Grimes status = 0x5a5a; 3185b81b6b3SRodney W. Grimes fnstsw(&status); 3195b81b6b3SRodney W. Grimes if ((status & 0xb8ff) == 0) { 3205b81b6b3SRodney W. Grimes /* 3215b81b6b3SRodney W. Grimes * Good, now check for a proper control word. 3225b81b6b3SRodney W. Grimes */ 3235b81b6b3SRodney W. Grimes control = 0x5a5a; 3245b81b6b3SRodney W. Grimes fnstcw(&control); 3255b81b6b3SRodney W. Grimes if ((control & 0x1f3f) == 0x033f) { 326501c2393SGarrett Wollman hw_float = npx_exists = 1; 3275b81b6b3SRodney W. Grimes /* 3285b81b6b3SRodney W. Grimes * We have an npx, now divide by 0 to see if exception 3295b81b6b3SRodney W. Grimes * 16 works. 3305b81b6b3SRodney W. Grimes */ 3315b81b6b3SRodney W. Grimes control &= ~(1 << 2); /* enable divide by 0 trap */ 3325b81b6b3SRodney W. Grimes fldcw(&control); 3335b81b6b3SRodney W. Grimes npx_traps_while_probing = npx_intrs_while_probing = 0; 3345b81b6b3SRodney W. Grimes fp_divide_by_0(); 3355b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) { 3365b81b6b3SRodney W. Grimes /* 3375b81b6b3SRodney W. Grimes * Good, exception 16 works. 3385b81b6b3SRodney W. Grimes */ 3395b81b6b3SRodney W. Grimes npx_ex16 = 1; 3405b81b6b3SRodney W. Grimes dvp->id_irq = 0; /* zap the interrupt */ 3415b81b6b3SRodney W. Grimes /* 3425b81b6b3SRodney W. Grimes * special return value to flag that we do not 3435b81b6b3SRodney W. Grimes * actually use any I/O registers 3445b81b6b3SRodney W. Grimes */ 3455b81b6b3SRodney W. Grimes return (-1); 3465b81b6b3SRodney W. Grimes } 3475b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) { 3485b81b6b3SRodney W. Grimes /* 3495b81b6b3SRodney W. Grimes * Bad, we are stuck with IRQ13. 3505b81b6b3SRodney W. Grimes */ 3515b81b6b3SRodney W. Grimes npx_irq13 = 1; 35237e52b59SBruce Evans /* 35337e52b59SBruce Evans * npxattach would be too late to set npx0_imask. 35437e52b59SBruce Evans */ 35537e52b59SBruce Evans npx0_imask |= dvp->id_irq; 3565b81b6b3SRodney W. Grimes return (IO_NPXSIZE); 3575b81b6b3SRodney W. Grimes } 3585b81b6b3SRodney W. Grimes /* 3595b81b6b3SRodney W. Grimes * Worse, even IRQ13 is broken. Use emulator. 3605b81b6b3SRodney W. Grimes */ 3615b81b6b3SRodney W. Grimes } 3625b81b6b3SRodney W. Grimes } 3635b81b6b3SRodney W. Grimes /* 3645b81b6b3SRodney W. Grimes * Probe failed, but we want to get to npxattach to initialize the 3655b81b6b3SRodney W. Grimes * emulator and say that it has been installed. XXX handle devices 3665b81b6b3SRodney W. Grimes * that aren't really devices better. 3675b81b6b3SRodney W. Grimes */ 3685b81b6b3SRodney W. Grimes dvp->id_irq = 0; 3695c644711SRodney W. Grimes /* 3705c644711SRodney W. Grimes * special return value to flag that we do not 3715c644711SRodney W. Grimes * actually use any I/O registers 3725c644711SRodney W. Grimes */ 3735c644711SRodney W. Grimes return (-1); 3743902c3efSSteve Passe 3753902c3efSSteve Passe #endif /* SMP */ 3765b81b6b3SRodney W. Grimes } 3775b81b6b3SRodney W. Grimes 3785b81b6b3SRodney W. Grimes /* 3795b81b6b3SRodney W. Grimes * Attach routine - announce which it is, and wire into system 3805b81b6b3SRodney W. Grimes */ 3815b81b6b3SRodney W. Grimes int 3825b81b6b3SRodney W. Grimes npxattach(dvp) 3835b81b6b3SRodney W. Grimes struct isa_device *dvp; 3845b81b6b3SRodney W. Grimes { 3851fe04850SBruce Evans /* The caller has printed "irq 13" for the npx_irq13 case. */ 3861fe04850SBruce Evans if (!npx_irq13) { 3871fe04850SBruce Evans printf("npx%d: ", dvp->id_unit); 38837e52b59SBruce Evans if (npx_ex16) 3891fe04850SBruce Evans printf("INT 16 interface\n"); 39037e52b59SBruce Evans #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 39137e52b59SBruce Evans else if (npx_exists) { 3921fe04850SBruce Evans printf("error reporting broken; using 387 emulator\n"); 3931fe04850SBruce Evans hw_float = npx_exists = 0; 39437e52b59SBruce Evans } else 3951fe04850SBruce Evans printf("387 emulator\n"); 39637e52b59SBruce Evans #else 39737e52b59SBruce Evans else 3981fe04850SBruce Evans printf("no 387 emulator in kernel!\n"); 39937e52b59SBruce Evans #endif 4001fe04850SBruce Evans } 4015b81b6b3SRodney W. Grimes npxinit(__INITIAL_NPXCW__); 4021fe04850SBruce Evans 4035611a020SPeter Wemm #if defined(I586_CPU) 4041fe04850SBruce Evans if (cpu_class == CPUCLASS_586 && npx_ex16) { 4051fe04850SBruce Evans if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { 4061fe04850SBruce Evans bcopy_vector = i586_bcopy; 4071fe04850SBruce Evans ovbcopy_vector = i586_bcopy; 4081fe04850SBruce Evans } 4091fe04850SBruce Evans if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) 4101fe04850SBruce Evans bzero = i586_bzero; 4111fe04850SBruce Evans if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { 4121fe04850SBruce Evans copyin_vector = i586_copyin; 4131fe04850SBruce Evans copyout_vector = i586_copyout; 4141fe04850SBruce Evans } 4151fe04850SBruce Evans } 4161fe04850SBruce Evans #endif 4171fe04850SBruce Evans 4185b81b6b3SRodney W. Grimes return (1); /* XXX unused */ 4195b81b6b3SRodney W. Grimes } 4205b81b6b3SRodney W. Grimes 4215b81b6b3SRodney W. Grimes /* 4225b81b6b3SRodney W. Grimes * Initialize floating point unit. 4235b81b6b3SRodney W. Grimes */ 4245b81b6b3SRodney W. Grimes void 4255b81b6b3SRodney W. Grimes npxinit(control) 42637e52b59SBruce Evans u_short control; 4275b81b6b3SRodney W. Grimes { 4285b81b6b3SRodney W. Grimes struct save87 dummy; 4295b81b6b3SRodney W. Grimes 4305b81b6b3SRodney W. Grimes if (!npx_exists) 4315b81b6b3SRodney W. Grimes return; 4325b81b6b3SRodney W. Grimes /* 4335b81b6b3SRodney W. Grimes * fninit has the same h/w bugs as fnsave. Use the detoxified 43437e52b59SBruce Evans * fnsave to throw away any junk in the fpu. npxsave() initializes 4355b81b6b3SRodney W. Grimes * the fpu and sets npxproc = NULL as important side effects. 4365b81b6b3SRodney W. Grimes */ 4375b81b6b3SRodney W. Grimes npxsave(&dummy); 4385b81b6b3SRodney W. Grimes stop_emulating(); 4395b81b6b3SRodney W. Grimes fldcw(&control); 4405b81b6b3SRodney W. Grimes if (curpcb != NULL) 4415b81b6b3SRodney W. Grimes fnsave(&curpcb->pcb_savefpu); 4425b81b6b3SRodney W. Grimes start_emulating(); 4435b81b6b3SRodney W. Grimes } 4445b81b6b3SRodney W. Grimes 4455b81b6b3SRodney W. Grimes /* 4465b81b6b3SRodney W. Grimes * Free coprocessor (if we have it). 4475b81b6b3SRodney W. Grimes */ 4485b81b6b3SRodney W. Grimes void 4495b81b6b3SRodney W. Grimes npxexit(p) 4505b81b6b3SRodney W. Grimes struct proc *p; 4515b81b6b3SRodney W. Grimes { 4525b81b6b3SRodney W. Grimes 453663f1485SBruce Evans if (p == npxproc) 454663f1485SBruce Evans npxsave(&curpcb->pcb_savefpu); 45526add149SBruce Evans #ifdef NPX_DEBUG 456663f1485SBruce Evans if (npx_exists) { 457663f1485SBruce Evans u_int masked_exceptions; 458663f1485SBruce Evans 459663f1485SBruce Evans masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 460663f1485SBruce Evans & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 461663f1485SBruce Evans /* 46226add149SBruce Evans * Log exceptions that would have trapped with the old 46326add149SBruce Evans * control word (overflow, divide by 0, and invalid operand). 464663f1485SBruce Evans */ 465663f1485SBruce Evans if (masked_exceptions & 0x0d) 466663f1485SBruce Evans log(LOG_ERR, 467663f1485SBruce Evans "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 468663f1485SBruce Evans p->p_pid, p->p_comm, masked_exceptions); 4695b81b6b3SRodney W. Grimes } 47026add149SBruce Evans #endif 4715b81b6b3SRodney W. Grimes } 4725b81b6b3SRodney W. Grimes 4735b81b6b3SRodney W. Grimes /* 47437e52b59SBruce Evans * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 4755b81b6b3SRodney W. Grimes * 47637e52b59SBruce Evans * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 47737e52b59SBruce Evans * depend on longjmp() restoring a usable state. Restoring the state 47837e52b59SBruce Evans * or examining it might fail if we didn't clear exceptions. 4795b81b6b3SRodney W. Grimes * 48037e52b59SBruce Evans * XXX there is no standard way to tell SIGFPE handlers about the error 48137e52b59SBruce Evans * state. The old interface: 48237e52b59SBruce Evans * 48337e52b59SBruce Evans * void handler(int sig, int code, struct sigcontext *scp); 48437e52b59SBruce Evans * 48537e52b59SBruce Evans * is broken because it is non-ANSI and because the FP state is not in 48637e52b59SBruce Evans * struct sigcontext. 48737e52b59SBruce Evans * 48837e52b59SBruce Evans * XXX the FP state is not preserved across signal handlers. So signal 48937e52b59SBruce Evans * handlers cannot afford to do FP unless they preserve the state or 49037e52b59SBruce Evans * longjmp() out. Both preserving the state and longjmp()ing may be 49137e52b59SBruce Evans * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 49237e52b59SBruce Evans * solution for signals other than SIGFPE. 4935b81b6b3SRodney W. Grimes */ 4945b81b6b3SRodney W. Grimes void 4952e69f359SBruce Evans npxintr(unit) 4962e69f359SBruce Evans int unit; 4975b81b6b3SRodney W. Grimes { 4985b81b6b3SRodney W. Grimes int code; 4992e69f359SBruce Evans struct intrframe *frame; 5005b81b6b3SRodney W. Grimes 5015b81b6b3SRodney W. Grimes if (npxproc == NULL || !npx_exists) { 50237e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 50337e52b59SBruce Evans npxproc, curproc, npx_exists); 5045b81b6b3SRodney W. Grimes panic("npxintr from nowhere"); 5055b81b6b3SRodney W. Grimes } 5065b81b6b3SRodney W. Grimes if (npxproc != curproc) { 50737e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 50837e52b59SBruce Evans npxproc, curproc, npx_exists); 5095b81b6b3SRodney W. Grimes panic("npxintr from non-current process"); 5105b81b6b3SRodney W. Grimes } 51137e52b59SBruce Evans 512390784fbSBruce Evans outb(0xf0, 0); 51337e52b59SBruce Evans fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 51437e52b59SBruce Evans fnclex(); 515390784fbSBruce Evans fnop(); 5165b81b6b3SRodney W. Grimes 5175b81b6b3SRodney W. Grimes /* 5185b81b6b3SRodney W. Grimes * Pass exception to process. 5195b81b6b3SRodney W. Grimes */ 5202e69f359SBruce Evans frame = (struct intrframe *)&unit; /* XXX */ 52140d50994SPhilippe Charnier if ((ISPL(frame->if_cs) == SEL_UPL) || (frame->if_eflags & PSL_VM)) { 5225b81b6b3SRodney W. Grimes /* 5235b81b6b3SRodney W. Grimes * Interrupt is essentially a trap, so we can afford to call 5245b81b6b3SRodney W. Grimes * the SIGFPE handler (if any) as soon as the interrupt 5255b81b6b3SRodney W. Grimes * returns. 5265b81b6b3SRodney W. Grimes * 5275b81b6b3SRodney W. Grimes * XXX little or nothing is gained from this, and plenty is 5285b81b6b3SRodney W. Grimes * lost - the interrupt frame has to contain the trap frame 5295b81b6b3SRodney W. Grimes * (this is otherwise only necessary for the rescheduling trap 5305b81b6b3SRodney W. Grimes * in doreti, and the frame for that could easily be set up 5315b81b6b3SRodney W. Grimes * just before it is used). 5325b81b6b3SRodney W. Grimes */ 53306884fd0SPeter Wemm curproc->p_md.md_regs = (struct trapframe *)&frame->if_es; 5345b81b6b3SRodney W. Grimes #ifdef notyet 5355b81b6b3SRodney W. Grimes /* 5365b81b6b3SRodney W. Grimes * Encode the appropriate code for detailed information on 5375b81b6b3SRodney W. Grimes * this exception. 5385b81b6b3SRodney W. Grimes */ 5395b81b6b3SRodney W. Grimes code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 5405b81b6b3SRodney W. Grimes #else 5415b81b6b3SRodney W. Grimes code = 0; /* XXX */ 5425b81b6b3SRodney W. Grimes #endif 5435b81b6b3SRodney W. Grimes trapsignal(curproc, SIGFPE, code); 5445b81b6b3SRodney W. Grimes } else { 5455b81b6b3SRodney W. Grimes /* 5465b81b6b3SRodney W. Grimes * Nested interrupt. These losers occur when: 5475b81b6b3SRodney W. Grimes * o an IRQ13 is bogusly generated at a bogus time, e.g.: 5485b81b6b3SRodney W. Grimes * o immediately after an fnsave or frstor of an 5495b81b6b3SRodney W. Grimes * error state. 5505b81b6b3SRodney W. Grimes * o a couple of 386 instructions after 5515b81b6b3SRodney W. Grimes * "fstpl _memvar" causes a stack overflow. 5525b81b6b3SRodney W. Grimes * These are especially nasty when combined with a 5535b81b6b3SRodney W. Grimes * trace trap. 5545b81b6b3SRodney W. Grimes * o an IRQ13 occurs at the same time as another higher- 5555b81b6b3SRodney W. Grimes * priority interrupt. 5565b81b6b3SRodney W. Grimes * 5575b81b6b3SRodney W. Grimes * Treat them like a true async interrupt. 5585b81b6b3SRodney W. Grimes */ 55937e52b59SBruce Evans psignal(curproc, SIGFPE); 5605b81b6b3SRodney W. Grimes } 5615b81b6b3SRodney W. Grimes } 5625b81b6b3SRodney W. Grimes 5635b81b6b3SRodney W. Grimes /* 5645b81b6b3SRodney W. Grimes * Implement device not available (DNA) exception 5655b81b6b3SRodney W. Grimes * 56637e52b59SBruce Evans * It would be better to switch FP context here (if curproc != npxproc) 56737e52b59SBruce Evans * and not necessarily for every context switch, but it is too hard to 56837e52b59SBruce Evans * access foreign pcb's. 5695b81b6b3SRodney W. Grimes */ 5705b81b6b3SRodney W. Grimes int 5715b81b6b3SRodney W. Grimes npxdna() 5725b81b6b3SRodney W. Grimes { 5735b81b6b3SRodney W. Grimes if (!npx_exists) 5745b81b6b3SRodney W. Grimes return (0); 5755b81b6b3SRodney W. Grimes if (npxproc != NULL) { 57637e52b59SBruce Evans printf("npxdna: npxproc = %p, curproc = %p\n", 57737e52b59SBruce Evans npxproc, curproc); 5785b81b6b3SRodney W. Grimes panic("npxdna"); 5795b81b6b3SRodney W. Grimes } 5805b81b6b3SRodney W. Grimes stop_emulating(); 5815b81b6b3SRodney W. Grimes /* 5825b81b6b3SRodney W. Grimes * Record new context early in case frstor causes an IRQ13. 5835b81b6b3SRodney W. Grimes */ 5845b81b6b3SRodney W. Grimes npxproc = curproc; 58537e52b59SBruce Evans curpcb->pcb_savefpu.sv_ex_sw = 0; 5865b81b6b3SRodney W. Grimes /* 5875b81b6b3SRodney W. Grimes * The following frstor may cause an IRQ13 when the state being 5885b81b6b3SRodney W. Grimes * restored has a pending error. The error will appear to have been 5895b81b6b3SRodney W. Grimes * triggered by the current (npx) user instruction even when that 5905b81b6b3SRodney W. Grimes * instruction is a no-wait instruction that should not trigger an 5915b81b6b3SRodney W. Grimes * error (e.g., fnclex). On at least one 486 system all of the 5925b81b6b3SRodney W. Grimes * no-wait instructions are broken the same as frstor, so our 5935b81b6b3SRodney W. Grimes * treatment does not amplify the breakage. On at least one 5945b81b6b3SRodney W. Grimes * 386/Cyrix 387 system, fnclex works correctly while frstor and 5955b81b6b3SRodney W. Grimes * fnsave are broken, so our treatment breaks fnclex if it is the 5965b81b6b3SRodney W. Grimes * first FPU instruction after a context switch. 5975b81b6b3SRodney W. Grimes */ 5985b81b6b3SRodney W. Grimes frstor(&curpcb->pcb_savefpu); 5995b81b6b3SRodney W. Grimes 6005b81b6b3SRodney W. Grimes return (1); 6015b81b6b3SRodney W. Grimes } 6025b81b6b3SRodney W. Grimes 6035b81b6b3SRodney W. Grimes /* 6045b81b6b3SRodney W. Grimes * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 6055b81b6b3SRodney W. Grimes * pending, then fnsave generates a bogus IRQ13 on some systems. Force 6065b81b6b3SRodney W. Grimes * any IRQ13 to be handled immediately, and then ignore it. This routine is 6075b81b6b3SRodney W. Grimes * often called at splhigh so it must not use many system services. In 6085b81b6b3SRodney W. Grimes * particular, it's much easier to install a special handler than to 6095b81b6b3SRodney W. Grimes * guarantee that it's safe to use npxintr() and its supporting code. 6105b81b6b3SRodney W. Grimes */ 6115b81b6b3SRodney W. Grimes void 6125b81b6b3SRodney W. Grimes npxsave(addr) 6135b81b6b3SRodney W. Grimes struct save87 *addr; 6145b81b6b3SRodney W. Grimes { 6153902c3efSSteve Passe #ifdef SMP 6163902c3efSSteve Passe 6173902c3efSSteve Passe stop_emulating(); 6183902c3efSSteve Passe fnsave(addr); 6193902c3efSSteve Passe /* fnop(); */ 6203902c3efSSteve Passe start_emulating(); 6213902c3efSSteve Passe npxproc = NULL; 6223902c3efSSteve Passe 6233902c3efSSteve Passe #else /* SMP */ 6243902c3efSSteve Passe 6255b81b6b3SRodney W. Grimes u_char icu1_mask; 6265b81b6b3SRodney W. Grimes u_char icu2_mask; 6275b81b6b3SRodney W. Grimes u_char old_icu1_mask; 6285b81b6b3SRodney W. Grimes u_char old_icu2_mask; 6295b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 6305b81b6b3SRodney W. Grimes 6315b81b6b3SRodney W. Grimes disable_intr(); 6325b81b6b3SRodney W. Grimes old_icu1_mask = inb(IO_ICU1 + 1); 6335b81b6b3SRodney W. Grimes old_icu2_mask = inb(IO_ICU2 + 1); 6345b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 635d2306226SDavid Greenman outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 636d2306226SDavid Greenman outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 6375b81b6b3SRodney W. Grimes idt[npx_intrno] = npx_idt_probeintr; 6385b81b6b3SRodney W. Grimes enable_intr(); 6395b81b6b3SRodney W. Grimes stop_emulating(); 6405b81b6b3SRodney W. Grimes fnsave(addr); 64137e52b59SBruce Evans fnop(); 6425b81b6b3SRodney W. Grimes start_emulating(); 6435b81b6b3SRodney W. Grimes npxproc = NULL; 6445b81b6b3SRodney W. Grimes disable_intr(); 6455b81b6b3SRodney W. Grimes icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 6465b81b6b3SRodney W. Grimes icu2_mask = inb(IO_ICU2 + 1); 6475b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, 648d2306226SDavid Greenman (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 6495b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, 650d2306226SDavid Greenman (icu2_mask & ~(npx0_imask >> 8)) 651d2306226SDavid Greenman | (old_icu2_mask & (npx0_imask >> 8))); 6525b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 6535b81b6b3SRodney W. Grimes enable_intr(); /* back to usual state */ 6543902c3efSSteve Passe 6553902c3efSSteve Passe #endif /* SMP */ 6565b81b6b3SRodney W. Grimes } 6575b81b6b3SRodney W. Grimes 6585b81b6b3SRodney W. Grimes #endif /* NNPX > 0 */ 659