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 353a34a5c3SPoul-Henning Kamp * $Id: npx.c,v 1.24 1995/09/19 18:55:14 bde Exp $ 365b81b6b3SRodney W. Grimes */ 375b81b6b3SRodney W. Grimes 385b81b6b3SRodney W. Grimes #include "npx.h" 395b81b6b3SRodney W. Grimes #if NNPX > 0 405b81b6b3SRodney W. Grimes 41f540b106SGarrett Wollman #include <sys/param.h> 42f540b106SGarrett Wollman #include <sys/systm.h> 433a34a5c3SPoul-Henning Kamp #include <sys/kernel.h> 443a34a5c3SPoul-Henning Kamp #include <sys/sysctl.h> 45f540b106SGarrett Wollman #include <sys/conf.h> 46f540b106SGarrett Wollman #include <sys/file.h> 47f540b106SGarrett Wollman #include <sys/proc.h> 482f86936aSGarrett Wollman #include <sys/devconf.h> 492f86936aSGarrett Wollman #include <sys/ioctl.h> 50663f1485SBruce Evans #include <sys/syslog.h> 51663f1485SBruce Evans #include <sys/signalvar.h> 522f86936aSGarrett Wollman 53f540b106SGarrett Wollman #include <machine/cpu.h> 54f540b106SGarrett Wollman #include <machine/pcb.h> 55f540b106SGarrett Wollman #include <machine/trap.h> 56663f1485SBruce Evans #include <machine/clock.h> 57f540b106SGarrett Wollman #include <machine/specialreg.h> 582f86936aSGarrett Wollman 59f540b106SGarrett Wollman #include <i386/isa/icu.h> 60f540b106SGarrett Wollman #include <i386/isa/isa_device.h> 61f540b106SGarrett Wollman #include <i386/isa/isa.h> 625b81b6b3SRodney W. Grimes 635b81b6b3SRodney W. Grimes /* 645b81b6b3SRodney W. Grimes * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 655b81b6b3SRodney W. Grimes */ 665b81b6b3SRodney W. Grimes 675b81b6b3SRodney W. Grimes #ifdef __GNUC__ 685b81b6b3SRodney W. Grimes 6937e52b59SBruce Evans #define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 705b81b6b3SRodney W. Grimes #define fnclex() __asm("fnclex") 715b81b6b3SRodney W. Grimes #define fninit() __asm("fninit") 7237e52b59SBruce Evans #define fnop() __asm("fnop") 7337e52b59SBruce Evans #define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 7437e52b59SBruce Evans #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 7537e52b59SBruce Evans #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 7637e52b59SBruce Evans #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 7737e52b59SBruce Evans #define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 785b81b6b3SRodney W. Grimes #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 795b81b6b3SRodney W. Grimes : : "n" (CR0_TS) : "ax") 805b81b6b3SRodney W. Grimes #define stop_emulating() __asm("clts") 815b81b6b3SRodney W. Grimes 825b81b6b3SRodney W. Grimes #else /* not __GNUC__ */ 835b81b6b3SRodney W. Grimes 845b81b6b3SRodney W. Grimes void fldcw __P((caddr_t addr)); 855b81b6b3SRodney W. Grimes void fnclex __P((void)); 865b81b6b3SRodney W. Grimes void fninit __P((void)); 8737e52b59SBruce Evans void fnop __P((void)); 885b81b6b3SRodney W. Grimes void fnsave __P((caddr_t addr)); 895b81b6b3SRodney W. Grimes void fnstcw __P((caddr_t addr)); 905b81b6b3SRodney W. Grimes void fnstsw __P((caddr_t addr)); 915b81b6b3SRodney W. Grimes void fp_divide_by_0 __P((void)); 925b81b6b3SRodney W. Grimes void frstor __P((caddr_t addr)); 935b81b6b3SRodney W. Grimes void start_emulating __P((void)); 945b81b6b3SRodney W. Grimes void stop_emulating __P((void)); 955b81b6b3SRodney W. Grimes 965b81b6b3SRodney W. Grimes #endif /* __GNUC__ */ 975b81b6b3SRodney W. Grimes 985b81b6b3SRodney W. Grimes typedef u_char bool_t; 995b81b6b3SRodney W. Grimes 1005b81b6b3SRodney W. Grimes static int npxattach __P((struct isa_device *dvp)); 1015b81b6b3SRodney W. Grimes static int npxprobe __P((struct isa_device *dvp)); 1025b81b6b3SRodney W. Grimes static int npxprobe1 __P((struct isa_device *dvp)); 1035b81b6b3SRodney W. Grimes 1045b81b6b3SRodney W. Grimes struct isa_driver npxdriver = { 1055b81b6b3SRodney W. Grimes npxprobe, npxattach, "npx", 1065b81b6b3SRodney W. Grimes }; 1075b81b6b3SRodney W. Grimes 10837e52b59SBruce Evans int hw_float; /* XXX currently just alias for npx_exists */ 1093a34a5c3SPoul-Henning Kamp 1103a34a5c3SPoul-Henning Kamp SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 1113a34a5c3SPoul-Henning Kamp CTLFLAG_RD, &hw_float, 0, 1123a34a5c3SPoul-Henning Kamp "Floatingpoint instructions executed in hardware"); 1133a34a5c3SPoul-Henning Kamp 1146ce81dd1SDavid Greenman u_int npx0_imask = SWI_CLOCK_MASK; 1155b81b6b3SRodney W. Grimes struct proc *npxproc; 1165b81b6b3SRodney W. Grimes 1175b81b6b3SRodney W. Grimes static bool_t npx_ex16; 1185b81b6b3SRodney W. Grimes static bool_t npx_exists; 1195b81b6b3SRodney W. Grimes static struct gate_descriptor npx_idt_probeintr; 1205b81b6b3SRodney W. Grimes static int npx_intrno; 1215b81b6b3SRodney W. Grimes static volatile u_int npx_intrs_while_probing; 1225b81b6b3SRodney W. Grimes static bool_t npx_irq13; 1235b81b6b3SRodney W. Grimes static volatile u_int npx_traps_while_probing; 1245b81b6b3SRodney W. Grimes 1255b81b6b3SRodney W. Grimes /* 1265b81b6b3SRodney W. Grimes * Special interrupt handlers. Someday intr0-intr15 will be used to count 1275b81b6b3SRodney W. Grimes * interrupts. We'll still need a special exception 16 handler. The busy 12837e52b59SBruce Evans * latch stuff in probeintr() can be moved to npxprobe(). 1295b81b6b3SRodney W. Grimes */ 130663f1485SBruce Evans inthand_t probeintr; 1315b81b6b3SRodney W. Grimes asm 1325b81b6b3SRodney W. Grimes (" 1335b81b6b3SRodney W. Grimes .text 1345b81b6b3SRodney W. Grimes _probeintr: 1355b81b6b3SRodney W. Grimes ss 1365b81b6b3SRodney W. Grimes incl _npx_intrs_while_probing 1375b81b6b3SRodney W. Grimes pushl %eax 13819bcb5e4SPaul Richards movb $0x20,%al # EOI (asm in strings loses cpp features) 13919bcb5e4SPaul Richards outb %al,$0xa0 # IO_ICU2 14019bcb5e4SPaul Richards outb %al,$0x20 # IO_ICU1 1415b81b6b3SRodney W. Grimes movb $0,%al 14219bcb5e4SPaul Richards outb %al,$0xf0 # clear BUSY# latch 1435b81b6b3SRodney W. Grimes popl %eax 1445b81b6b3SRodney W. Grimes iret 1455b81b6b3SRodney W. Grimes "); 1465b81b6b3SRodney W. Grimes 147663f1485SBruce Evans inthand_t probetrap; 1485b81b6b3SRodney W. Grimes asm 1495b81b6b3SRodney W. Grimes (" 1505b81b6b3SRodney W. Grimes .text 1515b81b6b3SRodney W. Grimes _probetrap: 1525b81b6b3SRodney W. Grimes ss 1535b81b6b3SRodney W. Grimes incl _npx_traps_while_probing 1545b81b6b3SRodney W. Grimes fnclex 1555b81b6b3SRodney W. Grimes iret 1565b81b6b3SRodney W. Grimes "); 1575b81b6b3SRodney W. Grimes 1586c0081e9SGarrett Wollman static struct kern_devconf kdc_npx[NNPX] = { { 1596c0081e9SGarrett Wollman 0, 0, 0, /* filled in by dev_attach */ 1606c0081e9SGarrett Wollman "npx", 0, { MDDT_ISA, 0 }, 1616c0081e9SGarrett Wollman isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 1626c0081e9SGarrett Wollman &kdc_isa0, /* parent */ 1636c0081e9SGarrett Wollman 0, /* parentdata */ 1646c0081e9SGarrett Wollman DC_UNCONFIGURED, /* state */ 1656c0081e9SGarrett Wollman "Floating-point unit", 1666c0081e9SGarrett Wollman DC_CLS_MISC /* class */ 1676c0081e9SGarrett Wollman } }; 1686c0081e9SGarrett Wollman 1696c0081e9SGarrett Wollman static inline void 1706c0081e9SGarrett Wollman npx_registerdev(struct isa_device *id) 1716c0081e9SGarrett Wollman { 1726c0081e9SGarrett Wollman int unit; 1736c0081e9SGarrett Wollman 1746c0081e9SGarrett Wollman unit = id->id_unit; 1756c0081e9SGarrett Wollman if (unit != 0) 1766c0081e9SGarrett Wollman kdc_npx[unit] = kdc_npx[0]; 1776c0081e9SGarrett Wollman kdc_npx[unit].kdc_unit = unit; 1786c0081e9SGarrett Wollman kdc_npx[unit].kdc_isa = id; 1796c0081e9SGarrett Wollman dev_attach(&kdc_npx[unit]); 1806c0081e9SGarrett Wollman } 1816c0081e9SGarrett Wollman 1825b81b6b3SRodney W. Grimes /* 1835b81b6b3SRodney W. Grimes * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 1845b81b6b3SRodney W. Grimes * whether the device exists or not (XXX should be elsewhere). Set flags 1855b81b6b3SRodney W. Grimes * to tell npxattach() what to do. Modify device struct if npx doesn't 1865b81b6b3SRodney W. Grimes * need to use interrupts. Return 1 if device exists. 1875b81b6b3SRodney W. Grimes */ 1885b81b6b3SRodney W. Grimes static int 1895b81b6b3SRodney W. Grimes npxprobe(dvp) 1905b81b6b3SRodney W. Grimes struct isa_device *dvp; 1915b81b6b3SRodney W. Grimes { 1925b81b6b3SRodney W. Grimes int result; 1935b81b6b3SRodney W. Grimes u_long save_eflags; 1945b81b6b3SRodney W. Grimes u_char save_icu1_mask; 1955b81b6b3SRodney W. Grimes u_char save_icu2_mask; 1965b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 1975b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxtrap; 1985b81b6b3SRodney W. Grimes /* 1995b81b6b3SRodney W. Grimes * This routine is now just a wrapper for npxprobe1(), to install 2005b81b6b3SRodney W. Grimes * special npx interrupt and trap handlers, to enable npx interrupts 2015b81b6b3SRodney W. Grimes * and to disable other interrupts. Someday isa_configure() will 2025b81b6b3SRodney W. Grimes * install suitable handlers and run with interrupts enabled so we 2035b81b6b3SRodney W. Grimes * won't need to do so much here. 2045b81b6b3SRodney W. Grimes */ 2056c0081e9SGarrett Wollman npx_registerdev(dvp); 2065b81b6b3SRodney W. Grimes npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 2075b81b6b3SRodney W. Grimes save_eflags = read_eflags(); 2085b81b6b3SRodney W. Grimes disable_intr(); 2095b81b6b3SRodney W. Grimes save_icu1_mask = inb(IO_ICU1 + 1); 2105b81b6b3SRodney W. Grimes save_icu2_mask = inb(IO_ICU2 + 1); 2115b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 2125b81b6b3SRodney W. Grimes save_idt_npxtrap = idt[16]; 2135b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 2145b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 2155b81b6b3SRodney W. Grimes setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL); 2165b81b6b3SRodney W. Grimes setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL); 2175b81b6b3SRodney W. Grimes npx_idt_probeintr = idt[npx_intrno]; 2185b81b6b3SRodney W. Grimes enable_intr(); 2195b81b6b3SRodney W. Grimes result = npxprobe1(dvp); 2205b81b6b3SRodney W. Grimes disable_intr(); 2215b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, save_icu1_mask); 2225b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, save_icu2_mask); 2235b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 2245b81b6b3SRodney W. Grimes idt[16] = save_idt_npxtrap; 2255b81b6b3SRodney W. Grimes write_eflags(save_eflags); 2265b81b6b3SRodney W. Grimes return (result); 2275b81b6b3SRodney W. Grimes } 2285b81b6b3SRodney W. Grimes 2295b81b6b3SRodney W. Grimes static int 2305b81b6b3SRodney W. Grimes npxprobe1(dvp) 2315b81b6b3SRodney W. Grimes struct isa_device *dvp; 2325b81b6b3SRodney W. Grimes { 23337e52b59SBruce Evans u_short control; 23437e52b59SBruce Evans u_short status; 23537e52b59SBruce Evans 2365b81b6b3SRodney W. Grimes /* 2375b81b6b3SRodney W. Grimes * Partially reset the coprocessor, if any. Some BIOS's don't reset 2385b81b6b3SRodney W. Grimes * it after a warm boot. 2395b81b6b3SRodney W. Grimes */ 2405b81b6b3SRodney W. Grimes outb(0xf1, 0); /* full reset on some systems, NOP on others */ 2415b81b6b3SRodney W. Grimes outb(0xf0, 0); /* clear BUSY# latch */ 2425b81b6b3SRodney W. Grimes /* 2435b81b6b3SRodney W. Grimes * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 2445b81b6b3SRodney W. Grimes * instructions. We must set the CR0_MP bit and use the CR0_TS 2455b81b6b3SRodney W. Grimes * bit to control the trap, because setting the CR0_EM bit does 2465b81b6b3SRodney W. Grimes * not cause WAIT instructions to trap. It's important to trap 2475b81b6b3SRodney W. Grimes * WAIT instructions - otherwise the "wait" variants of no-wait 2485b81b6b3SRodney W. Grimes * control instructions would degenerate to the "no-wait" variants 2495b81b6b3SRodney W. Grimes * after FP context switches but work correctly otherwise. It's 2505b81b6b3SRodney W. Grimes * particularly important to trap WAITs when there is no NPX - 2515b81b6b3SRodney W. Grimes * otherwise the "wait" variants would always degenerate. 2525b81b6b3SRodney W. Grimes * 2535b81b6b3SRodney W. Grimes * Try setting CR0_NE to get correct error reporting on 486DX's. 2545b81b6b3SRodney W. Grimes * Setting it should fail or do nothing on lesser processors. 2555b81b6b3SRodney W. Grimes */ 2565b81b6b3SRodney W. Grimes load_cr0(rcr0() | CR0_MP | CR0_NE); 2575b81b6b3SRodney W. Grimes /* 2585b81b6b3SRodney W. Grimes * But don't trap while we're probing. 2595b81b6b3SRodney W. Grimes */ 2605b81b6b3SRodney W. Grimes stop_emulating(); 2615b81b6b3SRodney W. Grimes /* 2625b81b6b3SRodney W. Grimes * Finish resetting the coprocessor, if any. If there is an error 2635b81b6b3SRodney W. Grimes * pending, then we may get a bogus IRQ13, but probeintr() will handle 2645b81b6b3SRodney W. Grimes * it OK. Bogus halts have never been observed, but we enabled 2655b81b6b3SRodney W. Grimes * IRQ13 and cleared the BUSY# latch early to handle them anyway. 2665b81b6b3SRodney W. Grimes */ 2675b81b6b3SRodney W. Grimes fninit(); 2687f3ae831SBruce Evans /* 2697f3ae831SBruce Evans * Don't use fwait here because it might hang. 2707f3ae831SBruce Evans * Don't use fnop here because it usually hangs if there is no FPU. 2717f3ae831SBruce Evans */ 27237e52b59SBruce Evans DELAY(1000); /* wait for any IRQ13 */ 2735b81b6b3SRodney W. Grimes #ifdef DIAGNOSTIC 2745b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) 2755b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx interrupt(s)\n", 2765b81b6b3SRodney W. Grimes npx_intrs_while_probing); 2775b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) 2785b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx trap(s)\n", 2795b81b6b3SRodney W. Grimes npx_traps_while_probing); 2805b81b6b3SRodney W. Grimes #endif 2815b81b6b3SRodney W. Grimes /* 2825b81b6b3SRodney W. Grimes * Check for a status of mostly zero. 2835b81b6b3SRodney W. Grimes */ 2845b81b6b3SRodney W. Grimes status = 0x5a5a; 2855b81b6b3SRodney W. Grimes fnstsw(&status); 2865b81b6b3SRodney W. Grimes if ((status & 0xb8ff) == 0) { 2875b81b6b3SRodney W. Grimes /* 2885b81b6b3SRodney W. Grimes * Good, now check for a proper control word. 2895b81b6b3SRodney W. Grimes */ 2905b81b6b3SRodney W. Grimes control = 0x5a5a; 2915b81b6b3SRodney W. Grimes fnstcw(&control); 2925b81b6b3SRodney W. Grimes if ((control & 0x1f3f) == 0x033f) { 293501c2393SGarrett Wollman hw_float = npx_exists = 1; 2945b81b6b3SRodney W. Grimes /* 2955b81b6b3SRodney W. Grimes * We have an npx, now divide by 0 to see if exception 2965b81b6b3SRodney W. Grimes * 16 works. 2975b81b6b3SRodney W. Grimes */ 2985b81b6b3SRodney W. Grimes control &= ~(1 << 2); /* enable divide by 0 trap */ 2995b81b6b3SRodney W. Grimes fldcw(&control); 3005b81b6b3SRodney W. Grimes npx_traps_while_probing = npx_intrs_while_probing = 0; 3015b81b6b3SRodney W. Grimes fp_divide_by_0(); 3025b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) { 3035b81b6b3SRodney W. Grimes /* 3045b81b6b3SRodney W. Grimes * Good, exception 16 works. 3055b81b6b3SRodney W. Grimes */ 3065b81b6b3SRodney W. Grimes npx_ex16 = 1; 3075b81b6b3SRodney W. Grimes dvp->id_irq = 0; /* zap the interrupt */ 3085b81b6b3SRodney W. Grimes /* 3095b81b6b3SRodney W. Grimes * special return value to flag that we do not 3105b81b6b3SRodney W. Grimes * actually use any I/O registers 3115b81b6b3SRodney W. Grimes */ 3125b81b6b3SRodney W. Grimes return (-1); 3135b81b6b3SRodney W. Grimes } 3145b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) { 3155b81b6b3SRodney W. Grimes /* 3165b81b6b3SRodney W. Grimes * Bad, we are stuck with IRQ13. 3175b81b6b3SRodney W. Grimes */ 3185b81b6b3SRodney W. Grimes npx_irq13 = 1; 31937e52b59SBruce Evans /* 32037e52b59SBruce Evans * npxattach would be too late to set npx0_imask. 32137e52b59SBruce Evans */ 32237e52b59SBruce Evans npx0_imask |= dvp->id_irq; 3235b81b6b3SRodney W. Grimes return (IO_NPXSIZE); 3245b81b6b3SRodney W. Grimes } 3255b81b6b3SRodney W. Grimes /* 3265b81b6b3SRodney W. Grimes * Worse, even IRQ13 is broken. Use emulator. 3275b81b6b3SRodney W. Grimes */ 3285b81b6b3SRodney W. Grimes } 3295b81b6b3SRodney W. Grimes } 3305b81b6b3SRodney W. Grimes /* 3315b81b6b3SRodney W. Grimes * Probe failed, but we want to get to npxattach to initialize the 3325b81b6b3SRodney W. Grimes * emulator and say that it has been installed. XXX handle devices 3335b81b6b3SRodney W. Grimes * that aren't really devices better. 3345b81b6b3SRodney W. Grimes */ 3355b81b6b3SRodney W. Grimes dvp->id_irq = 0; 3365c644711SRodney W. Grimes /* 3375c644711SRodney W. Grimes * special return value to flag that we do not 3385c644711SRodney W. Grimes * actually use any I/O registers 3395c644711SRodney W. Grimes */ 3405c644711SRodney W. Grimes return (-1); 3415b81b6b3SRodney W. Grimes } 3425b81b6b3SRodney W. Grimes 3435b81b6b3SRodney W. Grimes /* 3445b81b6b3SRodney W. Grimes * Attach routine - announce which it is, and wire into system 3455b81b6b3SRodney W. Grimes */ 3465b81b6b3SRodney W. Grimes int 3475b81b6b3SRodney W. Grimes npxattach(dvp) 3485b81b6b3SRodney W. Grimes struct isa_device *dvp; 3495b81b6b3SRodney W. Grimes { 35037e52b59SBruce Evans if (npx_ex16) 351bf3e3428SBill Paul printf("npx%d: INT 16 interface\n", dvp->id_unit); 35237e52b59SBruce Evans else if (npx_irq13) 353390784fbSBruce Evans ; /* higher level has printed "irq 13" */ 35437e52b59SBruce Evans #if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 35537e52b59SBruce Evans else if (npx_exists) { 356bf3e3428SBill Paul printf("npx%d: error reporting broken; using 387 emulator\n", 357bf3e3428SBill Paul dvp->id_unit); 35837e52b59SBruce Evans npx_exists = 0; 35937e52b59SBruce Evans } else 360bf3e3428SBill Paul printf("npx%d: 387 emulator\n",dvp->id_unit); 36137e52b59SBruce Evans #else 36237e52b59SBruce Evans else 363bf3e3428SBill Paul printf("npx%d: no 387 emulator in kernel!\n", dvp->id_unit); 36437e52b59SBruce Evans #endif 3655b81b6b3SRodney W. Grimes npxinit(__INITIAL_NPXCW__); 3666c0081e9SGarrett Wollman if (npx_exists) { 3676c0081e9SGarrett Wollman kdc_npx[dvp->id_unit].kdc_state = DC_BUSY; 3686c0081e9SGarrett Wollman } 3695b81b6b3SRodney W. Grimes return (1); /* XXX unused */ 3705b81b6b3SRodney W. Grimes } 3715b81b6b3SRodney W. Grimes 3725b81b6b3SRodney W. Grimes /* 3735b81b6b3SRodney W. Grimes * Initialize floating point unit. 3745b81b6b3SRodney W. Grimes */ 3755b81b6b3SRodney W. Grimes void 3765b81b6b3SRodney W. Grimes npxinit(control) 37737e52b59SBruce Evans u_short control; 3785b81b6b3SRodney W. Grimes { 3795b81b6b3SRodney W. Grimes struct save87 dummy; 3805b81b6b3SRodney W. Grimes 3815b81b6b3SRodney W. Grimes if (!npx_exists) 3825b81b6b3SRodney W. Grimes return; 3835b81b6b3SRodney W. Grimes /* 3845b81b6b3SRodney W. Grimes * fninit has the same h/w bugs as fnsave. Use the detoxified 38537e52b59SBruce Evans * fnsave to throw away any junk in the fpu. npxsave() initializes 3865b81b6b3SRodney W. Grimes * the fpu and sets npxproc = NULL as important side effects. 3875b81b6b3SRodney W. Grimes */ 3885b81b6b3SRodney W. Grimes npxsave(&dummy); 3895b81b6b3SRodney W. Grimes stop_emulating(); 3905b81b6b3SRodney W. Grimes fldcw(&control); 3915b81b6b3SRodney W. Grimes if (curpcb != NULL) 3925b81b6b3SRodney W. Grimes fnsave(&curpcb->pcb_savefpu); 3935b81b6b3SRodney W. Grimes start_emulating(); 3945b81b6b3SRodney W. Grimes } 3955b81b6b3SRodney W. Grimes 3965b81b6b3SRodney W. Grimes /* 3975b81b6b3SRodney W. Grimes * Free coprocessor (if we have it). 3985b81b6b3SRodney W. Grimes */ 3995b81b6b3SRodney W. Grimes void 4005b81b6b3SRodney W. Grimes npxexit(p) 4015b81b6b3SRodney W. Grimes struct proc *p; 4025b81b6b3SRodney W. Grimes { 4035b81b6b3SRodney W. Grimes 404663f1485SBruce Evans if (p == npxproc) 405663f1485SBruce Evans npxsave(&curpcb->pcb_savefpu); 406663f1485SBruce Evans if (npx_exists) { 407663f1485SBruce Evans u_int masked_exceptions; 408663f1485SBruce Evans 409663f1485SBruce Evans masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 410663f1485SBruce Evans & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 411663f1485SBruce Evans /* 412663f1485SBruce Evans * Overflow, divde by 0, and invalid operand would have 413663f1485SBruce Evans * caused a trap in 1.1.5. 414663f1485SBruce Evans */ 415663f1485SBruce Evans if (masked_exceptions & 0x0d) 416663f1485SBruce Evans log(LOG_ERR, 417663f1485SBruce Evans "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 418663f1485SBruce Evans p->p_pid, p->p_comm, masked_exceptions); 4195b81b6b3SRodney W. Grimes } 4205b81b6b3SRodney W. Grimes } 4215b81b6b3SRodney W. Grimes 4225b81b6b3SRodney W. Grimes /* 42337e52b59SBruce Evans * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 4245b81b6b3SRodney W. Grimes * 42537e52b59SBruce Evans * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 42637e52b59SBruce Evans * depend on longjmp() restoring a usable state. Restoring the state 42737e52b59SBruce Evans * or examining it might fail if we didn't clear exceptions. 4285b81b6b3SRodney W. Grimes * 42937e52b59SBruce Evans * XXX there is no standard way to tell SIGFPE handlers about the error 43037e52b59SBruce Evans * state. The old interface: 43137e52b59SBruce Evans * 43237e52b59SBruce Evans * void handler(int sig, int code, struct sigcontext *scp); 43337e52b59SBruce Evans * 43437e52b59SBruce Evans * is broken because it is non-ANSI and because the FP state is not in 43537e52b59SBruce Evans * struct sigcontext. 43637e52b59SBruce Evans * 43737e52b59SBruce Evans * XXX the FP state is not preserved across signal handlers. So signal 43837e52b59SBruce Evans * handlers cannot afford to do FP unless they preserve the state or 43937e52b59SBruce Evans * longjmp() out. Both preserving the state and longjmp()ing may be 44037e52b59SBruce Evans * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 44137e52b59SBruce Evans * solution for signals other than SIGFPE. 4425b81b6b3SRodney W. Grimes */ 4435b81b6b3SRodney W. Grimes void 4442e69f359SBruce Evans npxintr(unit) 4452e69f359SBruce Evans int unit; 4465b81b6b3SRodney W. Grimes { 4475b81b6b3SRodney W. Grimes int code; 4482e69f359SBruce Evans struct intrframe *frame; 4495b81b6b3SRodney W. Grimes 4505b81b6b3SRodney W. Grimes if (npxproc == NULL || !npx_exists) { 45137e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 45237e52b59SBruce Evans npxproc, curproc, npx_exists); 4535b81b6b3SRodney W. Grimes panic("npxintr from nowhere"); 4545b81b6b3SRodney W. Grimes } 4555b81b6b3SRodney W. Grimes if (npxproc != curproc) { 45637e52b59SBruce Evans printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 45737e52b59SBruce Evans npxproc, curproc, npx_exists); 4585b81b6b3SRodney W. Grimes panic("npxintr from non-current process"); 4595b81b6b3SRodney W. Grimes } 46037e52b59SBruce Evans 461390784fbSBruce Evans outb(0xf0, 0); 46237e52b59SBruce Evans fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 46337e52b59SBruce Evans fnclex(); 464390784fbSBruce Evans fnop(); 4655b81b6b3SRodney W. Grimes 4665b81b6b3SRodney W. Grimes /* 4675b81b6b3SRodney W. Grimes * Pass exception to process. 4685b81b6b3SRodney W. Grimes */ 4692e69f359SBruce Evans frame = (struct intrframe *)&unit; /* XXX */ 4702e69f359SBruce Evans if (ISPL(frame->if_cs) == SEL_UPL) { 4715b81b6b3SRodney W. Grimes /* 4725b81b6b3SRodney W. Grimes * Interrupt is essentially a trap, so we can afford to call 4735b81b6b3SRodney W. Grimes * the SIGFPE handler (if any) as soon as the interrupt 4745b81b6b3SRodney W. Grimes * returns. 4755b81b6b3SRodney W. Grimes * 4765b81b6b3SRodney W. Grimes * XXX little or nothing is gained from this, and plenty is 4775b81b6b3SRodney W. Grimes * lost - the interrupt frame has to contain the trap frame 4785b81b6b3SRodney W. Grimes * (this is otherwise only necessary for the rescheduling trap 4795b81b6b3SRodney W. Grimes * in doreti, and the frame for that could easily be set up 4805b81b6b3SRodney W. Grimes * just before it is used). 4815b81b6b3SRodney W. Grimes */ 4822e69f359SBruce Evans curproc->p_md.md_regs = &frame->if_es; 4835b81b6b3SRodney W. Grimes #ifdef notyet 4845b81b6b3SRodney W. Grimes /* 4855b81b6b3SRodney W. Grimes * Encode the appropriate code for detailed information on 4865b81b6b3SRodney W. Grimes * this exception. 4875b81b6b3SRodney W. Grimes */ 4885b81b6b3SRodney W. Grimes code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 4895b81b6b3SRodney W. Grimes #else 4905b81b6b3SRodney W. Grimes code = 0; /* XXX */ 4915b81b6b3SRodney W. Grimes #endif 4925b81b6b3SRodney W. Grimes trapsignal(curproc, SIGFPE, code); 4935b81b6b3SRodney W. Grimes } else { 4945b81b6b3SRodney W. Grimes /* 4955b81b6b3SRodney W. Grimes * Nested interrupt. These losers occur when: 4965b81b6b3SRodney W. Grimes * o an IRQ13 is bogusly generated at a bogus time, e.g.: 4975b81b6b3SRodney W. Grimes * o immediately after an fnsave or frstor of an 4985b81b6b3SRodney W. Grimes * error state. 4995b81b6b3SRodney W. Grimes * o a couple of 386 instructions after 5005b81b6b3SRodney W. Grimes * "fstpl _memvar" causes a stack overflow. 5015b81b6b3SRodney W. Grimes * These are especially nasty when combined with a 5025b81b6b3SRodney W. Grimes * trace trap. 5035b81b6b3SRodney W. Grimes * o an IRQ13 occurs at the same time as another higher- 5045b81b6b3SRodney W. Grimes * priority interrupt. 5055b81b6b3SRodney W. Grimes * 5065b81b6b3SRodney W. Grimes * Treat them like a true async interrupt. 5075b81b6b3SRodney W. Grimes */ 50837e52b59SBruce Evans psignal(curproc, SIGFPE); 5095b81b6b3SRodney W. Grimes } 5105b81b6b3SRodney W. Grimes } 5115b81b6b3SRodney W. Grimes 5125b81b6b3SRodney W. Grimes /* 5135b81b6b3SRodney W. Grimes * Implement device not available (DNA) exception 5145b81b6b3SRodney W. Grimes * 51537e52b59SBruce Evans * It would be better to switch FP context here (if curproc != npxproc) 51637e52b59SBruce Evans * and not necessarily for every context switch, but it is too hard to 51737e52b59SBruce Evans * access foreign pcb's. 5185b81b6b3SRodney W. Grimes */ 5195b81b6b3SRodney W. Grimes int 5205b81b6b3SRodney W. Grimes npxdna() 5215b81b6b3SRodney W. Grimes { 5225b81b6b3SRodney W. Grimes if (!npx_exists) 5235b81b6b3SRodney W. Grimes return (0); 5245b81b6b3SRodney W. Grimes if (npxproc != NULL) { 52537e52b59SBruce Evans printf("npxdna: npxproc = %p, curproc = %p\n", 52637e52b59SBruce Evans npxproc, curproc); 5275b81b6b3SRodney W. Grimes panic("npxdna"); 5285b81b6b3SRodney W. Grimes } 5295b81b6b3SRodney W. Grimes stop_emulating(); 5305b81b6b3SRodney W. Grimes /* 5315b81b6b3SRodney W. Grimes * Record new context early in case frstor causes an IRQ13. 5325b81b6b3SRodney W. Grimes */ 5335b81b6b3SRodney W. Grimes npxproc = curproc; 53437e52b59SBruce Evans curpcb->pcb_savefpu.sv_ex_sw = 0; 5355b81b6b3SRodney W. Grimes /* 5365b81b6b3SRodney W. Grimes * The following frstor may cause an IRQ13 when the state being 5375b81b6b3SRodney W. Grimes * restored has a pending error. The error will appear to have been 5385b81b6b3SRodney W. Grimes * triggered by the current (npx) user instruction even when that 5395b81b6b3SRodney W. Grimes * instruction is a no-wait instruction that should not trigger an 5405b81b6b3SRodney W. Grimes * error (e.g., fnclex). On at least one 486 system all of the 5415b81b6b3SRodney W. Grimes * no-wait instructions are broken the same as frstor, so our 5425b81b6b3SRodney W. Grimes * treatment does not amplify the breakage. On at least one 5435b81b6b3SRodney W. Grimes * 386/Cyrix 387 system, fnclex works correctly while frstor and 5445b81b6b3SRodney W. Grimes * fnsave are broken, so our treatment breaks fnclex if it is the 5455b81b6b3SRodney W. Grimes * first FPU instruction after a context switch. 5465b81b6b3SRodney W. Grimes */ 5475b81b6b3SRodney W. Grimes frstor(&curpcb->pcb_savefpu); 5485b81b6b3SRodney W. Grimes 5495b81b6b3SRodney W. Grimes return (1); 5505b81b6b3SRodney W. Grimes } 5515b81b6b3SRodney W. Grimes 5525b81b6b3SRodney W. Grimes /* 5535b81b6b3SRodney W. Grimes * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 5545b81b6b3SRodney W. Grimes * pending, then fnsave generates a bogus IRQ13 on some systems. Force 5555b81b6b3SRodney W. Grimes * any IRQ13 to be handled immediately, and then ignore it. This routine is 5565b81b6b3SRodney W. Grimes * often called at splhigh so it must not use many system services. In 5575b81b6b3SRodney W. Grimes * particular, it's much easier to install a special handler than to 5585b81b6b3SRodney W. Grimes * guarantee that it's safe to use npxintr() and its supporting code. 5595b81b6b3SRodney W. Grimes */ 5605b81b6b3SRodney W. Grimes void 5615b81b6b3SRodney W. Grimes npxsave(addr) 5625b81b6b3SRodney W. Grimes struct save87 *addr; 5635b81b6b3SRodney W. Grimes { 5645b81b6b3SRodney W. Grimes u_char icu1_mask; 5655b81b6b3SRodney W. Grimes u_char icu2_mask; 5665b81b6b3SRodney W. Grimes u_char old_icu1_mask; 5675b81b6b3SRodney W. Grimes u_char old_icu2_mask; 5685b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 5695b81b6b3SRodney W. Grimes 5705b81b6b3SRodney W. Grimes disable_intr(); 5715b81b6b3SRodney W. Grimes old_icu1_mask = inb(IO_ICU1 + 1); 5725b81b6b3SRodney W. Grimes old_icu2_mask = inb(IO_ICU2 + 1); 5735b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 574d2306226SDavid Greenman outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 575d2306226SDavid Greenman outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 5765b81b6b3SRodney W. Grimes idt[npx_intrno] = npx_idt_probeintr; 5775b81b6b3SRodney W. Grimes enable_intr(); 5785b81b6b3SRodney W. Grimes stop_emulating(); 5795b81b6b3SRodney W. Grimes fnsave(addr); 58037e52b59SBruce Evans fnop(); 5815b81b6b3SRodney W. Grimes start_emulating(); 5825b81b6b3SRodney W. Grimes npxproc = NULL; 5835b81b6b3SRodney W. Grimes disable_intr(); 5845b81b6b3SRodney W. Grimes icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 5855b81b6b3SRodney W. Grimes icu2_mask = inb(IO_ICU2 + 1); 5865b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, 587d2306226SDavid Greenman (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 5885b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, 589d2306226SDavid Greenman (icu2_mask & ~(npx0_imask >> 8)) 590d2306226SDavid Greenman | (old_icu2_mask & (npx0_imask >> 8))); 5915b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 5925b81b6b3SRodney W. Grimes enable_intr(); /* back to usual state */ 5935b81b6b3SRodney W. Grimes } 5945b81b6b3SRodney W. Grimes 5955b81b6b3SRodney W. Grimes #endif /* NNPX > 0 */ 596