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 35c673fe98SBruce Evans * $Id: npx.c,v 1.29 1996/01/06 23:10:52 peter Exp $ 365b81b6b3SRodney W. Grimes */ 375b81b6b3SRodney W. Grimes 385b81b6b3SRodney W. Grimes #include "npx.h" 395b81b6b3SRodney W. Grimes #if NNPX > 0 405b81b6b3SRodney W. Grimes 41d9256606SPeter Wemm #include "opt_math_emulate.h" 42a73af3a2SGarrett Wollman 43f540b106SGarrett Wollman #include <sys/param.h> 44f540b106SGarrett Wollman #include <sys/systm.h> 453a34a5c3SPoul-Henning Kamp #include <sys/kernel.h> 463a34a5c3SPoul-Henning Kamp #include <sys/sysctl.h> 47f540b106SGarrett Wollman #include <sys/conf.h> 48f540b106SGarrett Wollman #include <sys/file.h> 49f540b106SGarrett Wollman #include <sys/proc.h> 502f86936aSGarrett Wollman #include <sys/devconf.h> 512f86936aSGarrett Wollman #include <sys/ioctl.h> 52663f1485SBruce Evans #include <sys/syslog.h> 53663f1485SBruce Evans #include <sys/signalvar.h> 542f86936aSGarrett Wollman 55f540b106SGarrett Wollman #include <machine/cpu.h> 56f540b106SGarrett Wollman #include <machine/pcb.h> 57c673fe98SBruce Evans #include <machine/md_var.h> 58f540b106SGarrett Wollman #include <machine/trap.h> 59663f1485SBruce Evans #include <machine/clock.h> 60f540b106SGarrett Wollman #include <machine/specialreg.h> 612f86936aSGarrett Wollman 62f540b106SGarrett Wollman #include <i386/isa/icu.h> 63f540b106SGarrett Wollman #include <i386/isa/isa_device.h> 64f540b106SGarrett Wollman #include <i386/isa/isa.h> 655b81b6b3SRodney W. Grimes 665b81b6b3SRodney W. Grimes /* 675b81b6b3SRodney W. Grimes * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 685b81b6b3SRodney W. Grimes */ 695b81b6b3SRodney W. Grimes 705b81b6b3SRodney W. Grimes #ifdef __GNUC__ 715b81b6b3SRodney W. Grimes 7237e52b59SBruce Evans #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 735b81b6b3SRodney W. Grimes #define fnclex() __asm("fnclex") 745b81b6b3SRodney W. Grimes #define fninit() __asm("fninit") 7537e52b59SBruce Evans #define fnop() __asm("fnop") 7637e52b59SBruce Evans #define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 7737e52b59SBruce Evans #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 7837e52b59SBruce Evans #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 7937e52b59SBruce Evans #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 8037e52b59SBruce Evans #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 815b81b6b3SRodney W. Grimes #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 825b81b6b3SRodney W. Grimes : : "n" (CR0_TS) : "ax") 835b81b6b3SRodney W. Grimes #define stop_emulating() __asm("clts") 845b81b6b3SRodney W. Grimes 855b81b6b3SRodney W. Grimes #else /* not __GNUC__ */ 865b81b6b3SRodney W. Grimes 875b81b6b3SRodney W. Grimes void fldcw __P((caddr_t addr)); 885b81b6b3SRodney W. Grimes void fnclex __P((void)); 895b81b6b3SRodney W. Grimes void fninit __P((void)); 9037e52b59SBruce Evans void fnop __P((void)); 915b81b6b3SRodney W. Grimes void fnsave __P((caddr_t addr)); 925b81b6b3SRodney W. Grimes void fnstcw __P((caddr_t addr)); 935b81b6b3SRodney W. Grimes void fnstsw __P((caddr_t addr)); 945b81b6b3SRodney W. Grimes void fp_divide_by_0 __P((void)); 955b81b6b3SRodney W. Grimes void frstor __P((caddr_t addr)); 965b81b6b3SRodney W. Grimes void start_emulating __P((void)); 975b81b6b3SRodney W. Grimes void stop_emulating __P((void)); 985b81b6b3SRodney W. Grimes 995b81b6b3SRodney W. Grimes #endif /* __GNUC__ */ 1005b81b6b3SRodney W. Grimes 1015b81b6b3SRodney W. Grimes typedef u_char bool_t; 1025b81b6b3SRodney W. Grimes 1035b81b6b3SRodney W. Grimes static int npxattach __P((struct isa_device *dvp)); 1045b81b6b3SRodney W. Grimes static int npxprobe __P((struct isa_device *dvp)); 1055b81b6b3SRodney W. Grimes static int npxprobe1 __P((struct isa_device *dvp)); 1065b81b6b3SRodney W. Grimes 1075b81b6b3SRodney W. Grimes struct isa_driver npxdriver = { 1085b81b6b3SRodney W. Grimes npxprobe, npxattach, "npx", 1095b81b6b3SRodney W. Grimes }; 1105b81b6b3SRodney W. Grimes 11137e52b59SBruce Evans int hw_float; /* XXX currently just alias for npx_exists */ 1123a34a5c3SPoul-Henning Kamp 1133a34a5c3SPoul-Henning Kamp SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 1143a34a5c3SPoul-Henning Kamp CTLFLAG_RD, &hw_float, 0, 1153a34a5c3SPoul-Henning Kamp "Floatingpoint instructions executed in hardware"); 1163a34a5c3SPoul-Henning Kamp 1176f4e0bebSPoul-Henning Kamp static u_int npx0_imask = SWI_CLOCK_MASK; 1185b81b6b3SRodney W. Grimes struct proc *npxproc; 1195b81b6b3SRodney W. Grimes 1205b81b6b3SRodney W. Grimes static bool_t npx_ex16; 1215b81b6b3SRodney W. Grimes static bool_t npx_exists; 1225b81b6b3SRodney W. Grimes static struct gate_descriptor npx_idt_probeintr; 1235b81b6b3SRodney W. Grimes static int npx_intrno; 1245b81b6b3SRodney W. Grimes static volatile u_int npx_intrs_while_probing; 1255b81b6b3SRodney W. Grimes static bool_t npx_irq13; 1265b81b6b3SRodney W. Grimes static volatile u_int npx_traps_while_probing; 1275b81b6b3SRodney W. Grimes 1285b81b6b3SRodney W. Grimes /* 1295b81b6b3SRodney W. Grimes * Special interrupt handlers. Someday intr0-intr15 will be used to count 1305b81b6b3SRodney W. Grimes * interrupts. We'll still need a special exception 16 handler. The busy 13137e52b59SBruce Evans * latch stuff in probeintr() can be moved to npxprobe(). 1325b81b6b3SRodney W. Grimes */ 133663f1485SBruce Evans inthand_t probeintr; 1345b81b6b3SRodney W. Grimes asm 1355b81b6b3SRodney W. Grimes (" 1365b81b6b3SRodney W. Grimes .text 1375b81b6b3SRodney W. Grimes _probeintr: 1385b81b6b3SRodney W. Grimes ss 1395b81b6b3SRodney W. Grimes incl _npx_intrs_while_probing 1405b81b6b3SRodney W. Grimes pushl %eax 14119bcb5e4SPaul Richards movb $0x20,%al # EOI (asm in strings loses cpp features) 14219bcb5e4SPaul Richards outb %al,$0xa0 # IO_ICU2 14319bcb5e4SPaul Richards outb %al,$0x20 # IO_ICU1 1445b81b6b3SRodney W. Grimes movb $0,%al 14519bcb5e4SPaul Richards outb %al,$0xf0 # clear BUSY# latch 1465b81b6b3SRodney W. Grimes popl %eax 1475b81b6b3SRodney W. Grimes iret 1485b81b6b3SRodney W. Grimes "); 1495b81b6b3SRodney W. Grimes 150663f1485SBruce Evans inthand_t probetrap; 1515b81b6b3SRodney W. Grimes asm 1525b81b6b3SRodney W. Grimes (" 1535b81b6b3SRodney W. Grimes .text 1545b81b6b3SRodney W. Grimes _probetrap: 1555b81b6b3SRodney W. Grimes ss 1565b81b6b3SRodney W. Grimes incl _npx_traps_while_probing 1575b81b6b3SRodney W. Grimes fnclex 1585b81b6b3SRodney W. Grimes iret 1595b81b6b3SRodney W. Grimes "); 1605b81b6b3SRodney W. Grimes 1616c0081e9SGarrett Wollman static struct kern_devconf kdc_npx[NNPX] = { { 1626c0081e9SGarrett Wollman 0, 0, 0, /* filled in by dev_attach */ 1636c0081e9SGarrett Wollman "npx", 0, { MDDT_ISA, 0 }, 1646c0081e9SGarrett Wollman isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 1656c0081e9SGarrett Wollman &kdc_isa0, /* parent */ 1666c0081e9SGarrett Wollman 0, /* parentdata */ 1676c0081e9SGarrett Wollman DC_UNCONFIGURED, /* state */ 1686c0081e9SGarrett Wollman "Floating-point unit", 1696c0081e9SGarrett Wollman DC_CLS_MISC /* class */ 1706c0081e9SGarrett Wollman } }; 1716c0081e9SGarrett Wollman 1726c0081e9SGarrett Wollman static inline void 1736c0081e9SGarrett Wollman npx_registerdev(struct isa_device *id) 1746c0081e9SGarrett Wollman { 1756c0081e9SGarrett Wollman int unit; 1766c0081e9SGarrett Wollman 1776c0081e9SGarrett Wollman unit = id->id_unit; 1786c0081e9SGarrett Wollman if (unit != 0) 1796c0081e9SGarrett Wollman kdc_npx[unit] = kdc_npx[0]; 1806c0081e9SGarrett Wollman kdc_npx[unit].kdc_unit = unit; 1816c0081e9SGarrett Wollman kdc_npx[unit].kdc_isa = id; 1826c0081e9SGarrett Wollman dev_attach(&kdc_npx[unit]); 1836c0081e9SGarrett Wollman } 1846c0081e9SGarrett Wollman 1855b81b6b3SRodney W. Grimes /* 1865b81b6b3SRodney W. Grimes * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 1875b81b6b3SRodney W. Grimes * whether the device exists or not (XXX should be elsewhere). Set flags 1885b81b6b3SRodney W. Grimes * to tell npxattach() what to do. Modify device struct if npx doesn't 1895b81b6b3SRodney W. Grimes * need to use interrupts. Return 1 if device exists. 1905b81b6b3SRodney W. Grimes */ 1915b81b6b3SRodney W. Grimes static int 1925b81b6b3SRodney W. Grimes npxprobe(dvp) 1935b81b6b3SRodney W. Grimes struct isa_device *dvp; 1945b81b6b3SRodney W. Grimes { 1955b81b6b3SRodney W. Grimes int result; 1965b81b6b3SRodney W. Grimes u_long save_eflags; 1975b81b6b3SRodney W. Grimes u_char save_icu1_mask; 1985b81b6b3SRodney W. Grimes u_char save_icu2_mask; 1995b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 2005b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxtrap; 2015b81b6b3SRodney W. Grimes /* 2025b81b6b3SRodney W. Grimes * This routine is now just a wrapper for npxprobe1(), to install 2035b81b6b3SRodney W. Grimes * special npx interrupt and trap handlers, to enable npx interrupts 2045b81b6b3SRodney W. Grimes * and to disable other interrupts. Someday isa_configure() will 2055b81b6b3SRodney W. Grimes * install suitable handlers and run with interrupts enabled so we 2065b81b6b3SRodney W. Grimes * won't need to do so much here. 2075b81b6b3SRodney W. Grimes */ 2086c0081e9SGarrett Wollman npx_registerdev(dvp); 2095b81b6b3SRodney W. Grimes npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 2105b81b6b3SRodney W. Grimes save_eflags = read_eflags(); 2115b81b6b3SRodney W. Grimes disable_intr(); 2125b81b6b3SRodney W. Grimes save_icu1_mask = inb(IO_ICU1 + 1); 2135b81b6b3SRodney W. Grimes save_icu2_mask = inb(IO_ICU2 + 1); 2145b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 2155b81b6b3SRodney W. Grimes save_idt_npxtrap = idt[16]; 2165b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 2175b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 2182838c968SDavid Greenman setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 2192838c968SDavid Greenman setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 2205b81b6b3SRodney W. Grimes npx_idt_probeintr = idt[npx_intrno]; 2215b81b6b3SRodney W. Grimes enable_intr(); 2225b81b6b3SRodney W. Grimes result = npxprobe1(dvp); 2235b81b6b3SRodney W. Grimes disable_intr(); 2245b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, save_icu1_mask); 2255b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, save_icu2_mask); 2265b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 2275b81b6b3SRodney W. Grimes idt[16] = save_idt_npxtrap; 2285b81b6b3SRodney W. Grimes write_eflags(save_eflags); 2295b81b6b3SRodney W. Grimes return (result); 2305b81b6b3SRodney W. Grimes } 2315b81b6b3SRodney W. Grimes 2325b81b6b3SRodney W. Grimes static int 2335b81b6b3SRodney W. Grimes npxprobe1(dvp) 2345b81b6b3SRodney W. Grimes struct isa_device *dvp; 2355b81b6b3SRodney W. Grimes { 23637e52b59SBruce Evans u_short control; 23737e52b59SBruce Evans u_short status; 23837e52b59SBruce Evans 2395b81b6b3SRodney W. Grimes /* 2405b81b6b3SRodney W. Grimes * Partially reset the coprocessor, if any. Some BIOS's don't reset 2415b81b6b3SRodney W. Grimes * it after a warm boot. 2425b81b6b3SRodney W. Grimes */ 2435b81b6b3SRodney W. Grimes outb(0xf1, 0); /* full reset on some systems, NOP on others */ 2445b81b6b3SRodney W. Grimes outb(0xf0, 0); /* clear BUSY# latch */ 2455b81b6b3SRodney W. Grimes /* 2465b81b6b3SRodney W. Grimes * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 2475b81b6b3SRodney W. Grimes * instructions. We must set the CR0_MP bit and use the CR0_TS 2485b81b6b3SRodney W. Grimes * bit to control the trap, because setting the CR0_EM bit does 2495b81b6b3SRodney W. Grimes * not cause WAIT instructions to trap. It's important to trap 2505b81b6b3SRodney W. Grimes * WAIT instructions - otherwise the "wait" variants of no-wait 2515b81b6b3SRodney W. Grimes * control instructions would degenerate to the "no-wait" variants 2525b81b6b3SRodney W. Grimes * after FP context switches but work correctly otherwise. It's 2535b81b6b3SRodney W. Grimes * particularly important to trap WAITs when there is no NPX - 2545b81b6b3SRodney W. Grimes * otherwise the "wait" variants would always degenerate. 2555b81b6b3SRodney W. Grimes * 2565b81b6b3SRodney W. Grimes * Try setting CR0_NE to get correct error reporting on 486DX's. 2575b81b6b3SRodney W. Grimes * Setting it should fail or do nothing on lesser processors. 2585b81b6b3SRodney W. Grimes */ 2595b81b6b3SRodney W. Grimes load_cr0(rcr0() | CR0_MP | CR0_NE); 2605b81b6b3SRodney W. Grimes /* 2615b81b6b3SRodney W. Grimes * But don't trap while we're probing. 2625b81b6b3SRodney W. Grimes */ 2635b81b6b3SRodney W. Grimes stop_emulating(); 2645b81b6b3SRodney W. Grimes /* 2655b81b6b3SRodney W. Grimes * Finish resetting the coprocessor, if any. If there is an error 2665b81b6b3SRodney W. Grimes * pending, then we may get a bogus IRQ13, but probeintr() will handle 2675b81b6b3SRodney W. Grimes * it OK. Bogus halts have never been observed, but we enabled 2685b81b6b3SRodney W. Grimes * IRQ13 and cleared the BUSY# latch early to handle them anyway. 2695b81b6b3SRodney W. Grimes */ 2705b81b6b3SRodney W. Grimes fninit(); 2717f3ae831SBruce Evans /* 2727f3ae831SBruce Evans * Don't use fwait here because it might hang. 2737f3ae831SBruce Evans * Don't use fnop here because it usually hangs if there is no FPU. 2747f3ae831SBruce Evans */ 27537e52b59SBruce Evans DELAY(1000); /* wait for any IRQ13 */ 2765b81b6b3SRodney W. Grimes #ifdef DIAGNOSTIC 2775b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) 2785b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx interrupt(s)\n", 2795b81b6b3SRodney W. Grimes npx_intrs_while_probing); 2805b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) 2815b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx trap(s)\n", 2825b81b6b3SRodney W. Grimes npx_traps_while_probing); 2835b81b6b3SRodney W. Grimes #endif 2845b81b6b3SRodney W. Grimes /* 2855b81b6b3SRodney W. Grimes * Check for a status of mostly zero. 2865b81b6b3SRodney W. Grimes */ 2875b81b6b3SRodney W. Grimes status = 0x5a5a; 2885b81b6b3SRodney W. Grimes fnstsw(&status); 2895b81b6b3SRodney W. Grimes if ((status & 0xb8ff) == 0) { 2905b81b6b3SRodney W. Grimes /* 2915b81b6b3SRodney W. Grimes * Good, now check for a proper control word. 2925b81b6b3SRodney W. Grimes */ 2935b81b6b3SRodney W. Grimes control = 0x5a5a; 2945b81b6b3SRodney W. Grimes fnstcw(&control); 2955b81b6b3SRodney W. Grimes if ((control & 0x1f3f) == 0x033f) { 296501c2393SGarrett Wollman hw_float = npx_exists = 1; 2975b81b6b3SRodney W. Grimes /* 2985b81b6b3SRodney W. Grimes * We have an npx, now divide by 0 to see if exception 2995b81b6b3SRodney W. Grimes * 16 works. 3005b81b6b3SRodney W. Grimes */ 3015b81b6b3SRodney W. Grimes control &= ~(1 << 2); /* enable divide by 0 trap */ 3025b81b6b3SRodney W. Grimes fldcw(&control); 3035b81b6b3SRodney W. Grimes npx_traps_while_probing = npx_intrs_while_probing = 0; 3045b81b6b3SRodney W. Grimes fp_divide_by_0(); 3055b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) { 3065b81b6b3SRodney W. Grimes /* 3075b81b6b3SRodney W. Grimes * Good, exception 16 works. 3085b81b6b3SRodney W. Grimes */ 3095b81b6b3SRodney W. Grimes npx_ex16 = 1; 3105b81b6b3SRodney W. Grimes dvp->id_irq = 0; /* zap the interrupt */ 3115b81b6b3SRodney W. Grimes /* 3125b81b6b3SRodney W. Grimes * special return value to flag that we do not 3135b81b6b3SRodney W. Grimes * actually use any I/O registers 3145b81b6b3SRodney W. Grimes */ 3155b81b6b3SRodney W. Grimes return (-1); 3165b81b6b3SRodney W. Grimes } 3175b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) { 3185b81b6b3SRodney W. Grimes /* 3195b81b6b3SRodney W. Grimes * Bad, we are stuck with IRQ13. 3205b81b6b3SRodney W. Grimes */ 3215b81b6b3SRodney W. Grimes npx_irq13 = 1; 32237e52b59SBruce Evans /* 32337e52b59SBruce Evans * npxattach would be too late to set npx0_imask. 32437e52b59SBruce Evans */ 32537e52b59SBruce Evans npx0_imask |= dvp->id_irq; 3265b81b6b3SRodney W. Grimes return (IO_NPXSIZE); 3275b81b6b3SRodney W. Grimes } 3285b81b6b3SRodney W. Grimes /* 3295b81b6b3SRodney W. Grimes * Worse, even IRQ13 is broken. Use emulator. 3305b81b6b3SRodney W. Grimes */ 3315b81b6b3SRodney W. Grimes } 3325b81b6b3SRodney W. Grimes } 3335b81b6b3SRodney W. Grimes /* 3345b81b6b3SRodney W. Grimes * Probe failed, but we want to get to npxattach to initialize the 3355b81b6b3SRodney W. Grimes * emulator and say that it has been installed. XXX handle devices 3365b81b6b3SRodney W. Grimes * that aren't really devices better. 3375b81b6b3SRodney W. Grimes */ 3385b81b6b3SRodney W. Grimes dvp->id_irq = 0; 3395c644711SRodney W. Grimes /* 3405c644711SRodney W. Grimes * special return value to flag that we do not 3415c644711SRodney W. Grimes * actually use any I/O registers 3425c644711SRodney W. Grimes */ 3435c644711SRodney W. Grimes return (-1); 3445b81b6b3SRodney W. Grimes } 3455b81b6b3SRodney W. Grimes 3465b81b6b3SRodney W. Grimes /* 3475b81b6b3SRodney W. Grimes * Attach routine - announce which it is, and wire into system 3485b81b6b3SRodney W. Grimes */ 3495b81b6b3SRodney W. Grimes int 3505b81b6b3SRodney W. Grimes npxattach(dvp) 3515b81b6b3SRodney W. Grimes struct isa_device *dvp; 3525b81b6b3SRodney W. Grimes { 35337e52b59SBruce Evans if (npx_ex16) 354bf3e3428SBill Paul printf("npx%d: INT 16 interface\n", dvp->id_unit); 35537e52b59SBruce Evans else if (npx_irq13) 356390784fbSBruce Evans ; /* higher level has printed "irq 13" */ 35737e52b59SBruce Evans #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 35837e52b59SBruce Evans else if (npx_exists) { 359bf3e3428SBill Paul printf("npx%d: error reporting broken; using 387 emulator\n", 360bf3e3428SBill Paul dvp->id_unit); 36137e52b59SBruce Evans npx_exists = 0; 36237e52b59SBruce Evans } else 363bf3e3428SBill Paul printf("npx%d: 387 emulator\n",dvp->id_unit); 36437e52b59SBruce Evans #else 36537e52b59SBruce Evans else 366bf3e3428SBill Paul printf("npx%d: no 387 emulator in kernel!\n", dvp->id_unit); 36737e52b59SBruce Evans #endif 3685b81b6b3SRodney W. Grimes npxinit(__INITIAL_NPXCW__); 3696c0081e9SGarrett Wollman if (npx_exists) { 3706c0081e9SGarrett Wollman kdc_npx[dvp->id_unit].kdc_state = DC_BUSY; 3716c0081e9SGarrett Wollman } 3725b81b6b3SRodney W. Grimes return (1); /* XXX unused */ 3735b81b6b3SRodney W. Grimes } 3745b81b6b3SRodney W. Grimes 3755b81b6b3SRodney W. Grimes /* 3765b81b6b3SRodney W. Grimes * Initialize floating point unit. 3775b81b6b3SRodney W. Grimes */ 3785b81b6b3SRodney W. Grimes void 3795b81b6b3SRodney W. Grimes npxinit(control) 38037e52b59SBruce Evans u_short control; 3815b81b6b3SRodney W. Grimes { 3825b81b6b3SRodney W. Grimes struct save87 dummy; 3835b81b6b3SRodney W. Grimes 3845b81b6b3SRodney W. Grimes if (!npx_exists) 3855b81b6b3SRodney W. Grimes return; 3865b81b6b3SRodney W. Grimes /* 3875b81b6b3SRodney W. Grimes * fninit has the same h/w bugs as fnsave. Use the detoxified 38837e52b59SBruce Evans * fnsave to throw away any junk in the fpu. npxsave() initializes 3895b81b6b3SRodney W. Grimes * the fpu and sets npxproc = NULL as important side effects. 3905b81b6b3SRodney W. Grimes */ 3915b81b6b3SRodney W. Grimes npxsave(&dummy); 3925b81b6b3SRodney W. Grimes stop_emulating(); 3935b81b6b3SRodney W. Grimes fldcw(&control); 3945b81b6b3SRodney W. Grimes if (curpcb != NULL) 3955b81b6b3SRodney W. Grimes fnsave(&curpcb->pcb_savefpu); 3965b81b6b3SRodney W. Grimes start_emulating(); 3975b81b6b3SRodney W. Grimes } 3985b81b6b3SRodney W. Grimes 3995b81b6b3SRodney W. Grimes /* 4005b81b6b3SRodney W. Grimes * Free coprocessor (if we have it). 4015b81b6b3SRodney W. Grimes */ 4025b81b6b3SRodney W. Grimes void 4035b81b6b3SRodney W. Grimes npxexit(p) 4045b81b6b3SRodney W. Grimes struct proc *p; 4055b81b6b3SRodney W. Grimes { 4065b81b6b3SRodney W. Grimes 407663f1485SBruce Evans if (p == npxproc) 408663f1485SBruce Evans npxsave(&curpcb->pcb_savefpu); 409663f1485SBruce Evans if (npx_exists) { 410663f1485SBruce Evans u_int masked_exceptions; 411663f1485SBruce Evans 412663f1485SBruce Evans masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 413663f1485SBruce Evans & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 414663f1485SBruce Evans /* 415663f1485SBruce Evans * Overflow, divde by 0, and invalid operand would have 416663f1485SBruce Evans * caused a trap in 1.1.5. 417663f1485SBruce Evans */ 418663f1485SBruce Evans if (masked_exceptions & 0x0d) 419663f1485SBruce Evans log(LOG_ERR, 420663f1485SBruce Evans "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 421663f1485SBruce Evans p->p_pid, p->p_comm, masked_exceptions); 4225b81b6b3SRodney W. Grimes } 4235b81b6b3SRodney W. Grimes } 4245b81b6b3SRodney W. Grimes 4255b81b6b3SRodney W. Grimes /* 42637e52b59SBruce Evans * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 4275b81b6b3SRodney W. Grimes * 42837e52b59SBruce Evans * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 42937e52b59SBruce Evans * depend on longjmp() restoring a usable state. Restoring the state 43037e52b59SBruce Evans * or examining it might fail if we didn't clear exceptions. 4315b81b6b3SRodney W. Grimes * 43237e52b59SBruce Evans * XXX there is no standard way to tell SIGFPE handlers about the error 43337e52b59SBruce Evans * state. The old interface: 43437e52b59SBruce Evans * 43537e52b59SBruce Evans * void handler(int sig, int code, struct sigcontext *scp); 43637e52b59SBruce Evans * 43737e52b59SBruce Evans * is broken because it is non-ANSI and because the FP state is not in 43837e52b59SBruce Evans * struct sigcontext. 43937e52b59SBruce Evans * 44037e52b59SBruce Evans * XXX the FP state is not preserved across signal handlers. So signal 44137e52b59SBruce Evans * handlers cannot afford to do FP unless they preserve the state or 44237e52b59SBruce Evans * longjmp() out. Both preserving the state and longjmp()ing may be 44337e52b59SBruce Evans * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 44437e52b59SBruce Evans * solution for signals other than SIGFPE. 4455b81b6b3SRodney W. Grimes */ 4465b81b6b3SRodney W. Grimes void 4472e69f359SBruce Evans npxintr(unit) 4482e69f359SBruce Evans int unit; 4495b81b6b3SRodney W. Grimes { 4505b81b6b3SRodney W. Grimes int code; 4512e69f359SBruce Evans struct intrframe *frame; 4525b81b6b3SRodney W. Grimes 4535b81b6b3SRodney W. Grimes if (npxproc == NULL || !npx_exists) { 45437e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 45537e52b59SBruce Evans npxproc, curproc, npx_exists); 4565b81b6b3SRodney W. Grimes panic("npxintr from nowhere"); 4575b81b6b3SRodney W. Grimes } 4585b81b6b3SRodney W. Grimes if (npxproc != curproc) { 45937e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 46037e52b59SBruce Evans npxproc, curproc, npx_exists); 4615b81b6b3SRodney W. Grimes panic("npxintr from non-current process"); 4625b81b6b3SRodney W. Grimes } 46337e52b59SBruce Evans 464390784fbSBruce Evans outb(0xf0, 0); 46537e52b59SBruce Evans fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 46637e52b59SBruce Evans fnclex(); 467390784fbSBruce Evans fnop(); 4685b81b6b3SRodney W. Grimes 4695b81b6b3SRodney W. Grimes /* 4705b81b6b3SRodney W. Grimes * Pass exception to process. 4715b81b6b3SRodney W. Grimes */ 4722e69f359SBruce Evans frame = (struct intrframe *)&unit; /* XXX */ 4732e69f359SBruce Evans if (ISPL(frame->if_cs) == SEL_UPL) { 4745b81b6b3SRodney W. Grimes /* 4755b81b6b3SRodney W. Grimes * Interrupt is essentially a trap, so we can afford to call 4765b81b6b3SRodney W. Grimes * the SIGFPE handler (if any) as soon as the interrupt 4775b81b6b3SRodney W. Grimes * returns. 4785b81b6b3SRodney W. Grimes * 4795b81b6b3SRodney W. Grimes * XXX little or nothing is gained from this, and plenty is 4805b81b6b3SRodney W. Grimes * lost - the interrupt frame has to contain the trap frame 4815b81b6b3SRodney W. Grimes * (this is otherwise only necessary for the rescheduling trap 4825b81b6b3SRodney W. Grimes * in doreti, and the frame for that could easily be set up 4835b81b6b3SRodney W. Grimes * just before it is used). 4845b81b6b3SRodney W. Grimes */ 4852e69f359SBruce Evans curproc->p_md.md_regs = &frame->if_es; 4865b81b6b3SRodney W. Grimes #ifdef notyet 4875b81b6b3SRodney W. Grimes /* 4885b81b6b3SRodney W. Grimes * Encode the appropriate code for detailed information on 4895b81b6b3SRodney W. Grimes * this exception. 4905b81b6b3SRodney W. Grimes */ 4915b81b6b3SRodney W. Grimes code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 4925b81b6b3SRodney W. Grimes #else 4935b81b6b3SRodney W. Grimes code = 0; /* XXX */ 4945b81b6b3SRodney W. Grimes #endif 4955b81b6b3SRodney W. Grimes trapsignal(curproc, SIGFPE, code); 4965b81b6b3SRodney W. Grimes } else { 4975b81b6b3SRodney W. Grimes /* 4985b81b6b3SRodney W. Grimes * Nested interrupt. These losers occur when: 4995b81b6b3SRodney W. Grimes * o an IRQ13 is bogusly generated at a bogus time, e.g.: 5005b81b6b3SRodney W. Grimes * o immediately after an fnsave or frstor of an 5015b81b6b3SRodney W. Grimes * error state. 5025b81b6b3SRodney W. Grimes * o a couple of 386 instructions after 5035b81b6b3SRodney W. Grimes * "fstpl _memvar" causes a stack overflow. 5045b81b6b3SRodney W. Grimes * These are especially nasty when combined with a 5055b81b6b3SRodney W. Grimes * trace trap. 5065b81b6b3SRodney W. Grimes * o an IRQ13 occurs at the same time as another higher- 5075b81b6b3SRodney W. Grimes * priority interrupt. 5085b81b6b3SRodney W. Grimes * 5095b81b6b3SRodney W. Grimes * Treat them like a true async interrupt. 5105b81b6b3SRodney W. Grimes */ 51137e52b59SBruce Evans psignal(curproc, SIGFPE); 5125b81b6b3SRodney W. Grimes } 5135b81b6b3SRodney W. Grimes } 5145b81b6b3SRodney W. Grimes 5155b81b6b3SRodney W. Grimes /* 5165b81b6b3SRodney W. Grimes * Implement device not available (DNA) exception 5175b81b6b3SRodney W. Grimes * 51837e52b59SBruce Evans * It would be better to switch FP context here (if curproc != npxproc) 51937e52b59SBruce Evans * and not necessarily for every context switch, but it is too hard to 52037e52b59SBruce Evans * access foreign pcb's. 5215b81b6b3SRodney W. Grimes */ 5225b81b6b3SRodney W. Grimes int 5235b81b6b3SRodney W. Grimes npxdna() 5245b81b6b3SRodney W. Grimes { 5255b81b6b3SRodney W. Grimes if (!npx_exists) 5265b81b6b3SRodney W. Grimes return (0); 5275b81b6b3SRodney W. Grimes if (npxproc != NULL) { 52837e52b59SBruce Evans printf("npxdna: npxproc = %p, curproc = %p\n", 52937e52b59SBruce Evans npxproc, curproc); 5305b81b6b3SRodney W. Grimes panic("npxdna"); 5315b81b6b3SRodney W. Grimes } 5325b81b6b3SRodney W. Grimes stop_emulating(); 5335b81b6b3SRodney W. Grimes /* 5345b81b6b3SRodney W. Grimes * Record new context early in case frstor causes an IRQ13. 5355b81b6b3SRodney W. Grimes */ 5365b81b6b3SRodney W. Grimes npxproc = curproc; 53737e52b59SBruce Evans curpcb->pcb_savefpu.sv_ex_sw = 0; 5385b81b6b3SRodney W. Grimes /* 5395b81b6b3SRodney W. Grimes * The following frstor may cause an IRQ13 when the state being 5405b81b6b3SRodney W. Grimes * restored has a pending error. The error will appear to have been 5415b81b6b3SRodney W. Grimes * triggered by the current (npx) user instruction even when that 5425b81b6b3SRodney W. Grimes * instruction is a no-wait instruction that should not trigger an 5435b81b6b3SRodney W. Grimes * error (e.g., fnclex). On at least one 486 system all of the 5445b81b6b3SRodney W. Grimes * no-wait instructions are broken the same as frstor, so our 5455b81b6b3SRodney W. Grimes * treatment does not amplify the breakage. On at least one 5465b81b6b3SRodney W. Grimes * 386/Cyrix 387 system, fnclex works correctly while frstor and 5475b81b6b3SRodney W. Grimes * fnsave are broken, so our treatment breaks fnclex if it is the 5485b81b6b3SRodney W. Grimes * first FPU instruction after a context switch. 5495b81b6b3SRodney W. Grimes */ 5505b81b6b3SRodney W. Grimes frstor(&curpcb->pcb_savefpu); 5515b81b6b3SRodney W. Grimes 5525b81b6b3SRodney W. Grimes return (1); 5535b81b6b3SRodney W. Grimes } 5545b81b6b3SRodney W. Grimes 5555b81b6b3SRodney W. Grimes /* 5565b81b6b3SRodney W. Grimes * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 5575b81b6b3SRodney W. Grimes * pending, then fnsave generates a bogus IRQ13 on some systems. Force 5585b81b6b3SRodney W. Grimes * any IRQ13 to be handled immediately, and then ignore it. This routine is 5595b81b6b3SRodney W. Grimes * often called at splhigh so it must not use many system services. In 5605b81b6b3SRodney W. Grimes * particular, it's much easier to install a special handler than to 5615b81b6b3SRodney W. Grimes * guarantee that it's safe to use npxintr() and its supporting code. 5625b81b6b3SRodney W. Grimes */ 5635b81b6b3SRodney W. Grimes void 5645b81b6b3SRodney W. Grimes npxsave(addr) 5655b81b6b3SRodney W. Grimes struct save87 *addr; 5665b81b6b3SRodney W. Grimes { 5675b81b6b3SRodney W. Grimes u_char icu1_mask; 5685b81b6b3SRodney W. Grimes u_char icu2_mask; 5695b81b6b3SRodney W. Grimes u_char old_icu1_mask; 5705b81b6b3SRodney W. Grimes u_char old_icu2_mask; 5715b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 5725b81b6b3SRodney W. Grimes 5735b81b6b3SRodney W. Grimes disable_intr(); 5745b81b6b3SRodney W. Grimes old_icu1_mask = inb(IO_ICU1 + 1); 5755b81b6b3SRodney W. Grimes old_icu2_mask = inb(IO_ICU2 + 1); 5765b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 577d2306226SDavid Greenman outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 578d2306226SDavid Greenman outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 5795b81b6b3SRodney W. Grimes idt[npx_intrno] = npx_idt_probeintr; 5805b81b6b3SRodney W. Grimes enable_intr(); 5815b81b6b3SRodney W. Grimes stop_emulating(); 5825b81b6b3SRodney W. Grimes fnsave(addr); 58337e52b59SBruce Evans fnop(); 5845b81b6b3SRodney W. Grimes start_emulating(); 5855b81b6b3SRodney W. Grimes npxproc = NULL; 5865b81b6b3SRodney W. Grimes disable_intr(); 5875b81b6b3SRodney W. Grimes icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 5885b81b6b3SRodney W. Grimes icu2_mask = inb(IO_ICU2 + 1); 5895b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, 590d2306226SDavid Greenman (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 5915b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, 592d2306226SDavid Greenman (icu2_mask & ~(npx0_imask >> 8)) 593d2306226SDavid Greenman | (old_icu2_mask & (npx0_imask >> 8))); 5945b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 5955b81b6b3SRodney W. Grimes enable_intr(); /* back to usual state */ 5965b81b6b3SRodney W. Grimes } 5975b81b6b3SRodney W. Grimes 5985b81b6b3SRodney W. Grimes #endif /* NNPX > 0 */ 599