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 35663f1485SBruce Evans * $Id: npx.c,v 1.16 1994/11/06 00:58:06 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> 43f540b106SGarrett Wollman #include <sys/conf.h> 44f540b106SGarrett Wollman #include <sys/file.h> 45f540b106SGarrett Wollman #include <sys/proc.h> 462f86936aSGarrett Wollman #include <sys/devconf.h> 472f86936aSGarrett Wollman #include <sys/ioctl.h> 48663f1485SBruce Evans #include <sys/syslog.h> 49663f1485SBruce Evans #include <sys/signalvar.h> 502f86936aSGarrett Wollman 51f540b106SGarrett Wollman #include <machine/cpu.h> 52f540b106SGarrett Wollman #include <machine/pcb.h> 53f540b106SGarrett Wollman #include <machine/trap.h> 54663f1485SBruce Evans #include <machine/clock.h> 55f540b106SGarrett Wollman #include <machine/specialreg.h> 562f86936aSGarrett Wollman 57f540b106SGarrett Wollman #include <i386/isa/icu.h> 58f540b106SGarrett Wollman #include <i386/isa/isa_device.h> 59f540b106SGarrett Wollman #include <i386/isa/isa.h> 605b81b6b3SRodney W. Grimes 615b81b6b3SRodney W. Grimes /* 625b81b6b3SRodney W. Grimes * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 635b81b6b3SRodney W. Grimes */ 645b81b6b3SRodney W. Grimes 655b81b6b3SRodney W. Grimes #ifdef __GNUC__ 665b81b6b3SRodney W. Grimes 675b81b6b3SRodney W. Grimes #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) 685b81b6b3SRodney W. Grimes #define fnclex() __asm("fnclex") 695b81b6b3SRodney W. Grimes #define fninit() __asm("fninit") 705b81b6b3SRodney W. Grimes #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr)) 715b81b6b3SRodney W. Grimes #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr)) 725b81b6b3SRodney W. Grimes #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr)) 735b81b6b3SRodney W. Grimes #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait") 745b81b6b3SRodney W. Grimes #define frstor(addr) __asm("frstor %0" : : "m" (*addr)) 755b81b6b3SRodney W. Grimes #define fwait() __asm("fwait") 765b81b6b3SRodney W. Grimes #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 775b81b6b3SRodney W. Grimes : : "n" (CR0_TS) : "ax") 785b81b6b3SRodney W. Grimes #define stop_emulating() __asm("clts") 795b81b6b3SRodney W. Grimes 805b81b6b3SRodney W. Grimes #else /* not __GNUC__ */ 815b81b6b3SRodney W. Grimes 825b81b6b3SRodney W. Grimes void fldcw __P((caddr_t addr)); 835b81b6b3SRodney W. Grimes void fnclex __P((void)); 845b81b6b3SRodney W. Grimes void fninit __P((void)); 855b81b6b3SRodney W. Grimes void fnsave __P((caddr_t addr)); 865b81b6b3SRodney W. Grimes void fnstcw __P((caddr_t addr)); 875b81b6b3SRodney W. Grimes void fnstsw __P((caddr_t addr)); 885b81b6b3SRodney W. Grimes void fp_divide_by_0 __P((void)); 895b81b6b3SRodney W. Grimes void frstor __P((caddr_t addr)); 905b81b6b3SRodney W. Grimes void fwait __P((void)); 915b81b6b3SRodney W. Grimes void start_emulating __P((void)); 925b81b6b3SRodney W. Grimes void stop_emulating __P((void)); 935b81b6b3SRodney W. Grimes 945b81b6b3SRodney W. Grimes #endif /* __GNUC__ */ 955b81b6b3SRodney W. Grimes 965b81b6b3SRodney W. Grimes typedef u_char bool_t; 975b81b6b3SRodney W. Grimes 985b81b6b3SRodney W. Grimes static int npxattach __P((struct isa_device *dvp)); 995b81b6b3SRodney W. Grimes static int npxprobe __P((struct isa_device *dvp)); 1005b81b6b3SRodney W. Grimes static int npxprobe1 __P((struct isa_device *dvp)); 1015b81b6b3SRodney W. Grimes 1025b81b6b3SRodney W. Grimes struct isa_driver npxdriver = { 1035b81b6b3SRodney W. Grimes npxprobe, npxattach, "npx", 1045b81b6b3SRodney W. Grimes }; 1055b81b6b3SRodney W. Grimes 1066ce81dd1SDavid Greenman u_int npx0_imask = SWI_CLOCK_MASK; 1075b81b6b3SRodney W. Grimes struct proc *npxproc; 1085b81b6b3SRodney W. Grimes 1095b81b6b3SRodney W. Grimes static bool_t npx_ex16; 1105b81b6b3SRodney W. Grimes static bool_t npx_exists; 111501c2393SGarrett Wollman int hw_float; 1125b81b6b3SRodney W. Grimes static struct gate_descriptor npx_idt_probeintr; 1135b81b6b3SRodney W. Grimes static int npx_intrno; 1145b81b6b3SRodney W. Grimes static volatile u_int npx_intrs_while_probing; 1155b81b6b3SRodney W. Grimes static bool_t npx_irq13; 1165b81b6b3SRodney W. Grimes static volatile u_int npx_traps_while_probing; 1175b81b6b3SRodney W. Grimes 1185b81b6b3SRodney W. Grimes /* 1195b81b6b3SRodney W. Grimes * Special interrupt handlers. Someday intr0-intr15 will be used to count 1205b81b6b3SRodney W. Grimes * interrupts. We'll still need a special exception 16 handler. The busy 1215b81b6b3SRodney W. Grimes * latch stuff in probintr() can be moved to npxprobe(). 1225b81b6b3SRodney W. Grimes */ 123663f1485SBruce Evans inthand_t probeintr; 1245b81b6b3SRodney W. Grimes asm 1255b81b6b3SRodney W. Grimes (" 1265b81b6b3SRodney W. Grimes .text 1275b81b6b3SRodney W. Grimes _probeintr: 1285b81b6b3SRodney W. Grimes ss 1295b81b6b3SRodney W. Grimes incl _npx_intrs_while_probing 1305b81b6b3SRodney W. Grimes pushl %eax 13119bcb5e4SPaul Richards movb $0x20,%al # EOI (asm in strings loses cpp features) 13219bcb5e4SPaul Richards outb %al,$0xa0 # IO_ICU2 13319bcb5e4SPaul Richards outb %al,$0x20 #IO_ICU1 1345b81b6b3SRodney W. Grimes movb $0,%al 13519bcb5e4SPaul Richards outb %al,$0xf0 # clear BUSY# latch 1365b81b6b3SRodney W. Grimes popl %eax 1375b81b6b3SRodney W. Grimes iret 1385b81b6b3SRodney W. Grimes "); 1395b81b6b3SRodney W. Grimes 140663f1485SBruce Evans inthand_t probetrap; 1415b81b6b3SRodney W. Grimes asm 1425b81b6b3SRodney W. Grimes (" 1435b81b6b3SRodney W. Grimes .text 1445b81b6b3SRodney W. Grimes _probetrap: 1455b81b6b3SRodney W. Grimes ss 1465b81b6b3SRodney W. Grimes incl _npx_traps_while_probing 1475b81b6b3SRodney W. Grimes fnclex 1485b81b6b3SRodney W. Grimes iret 1495b81b6b3SRodney W. Grimes "); 1505b81b6b3SRodney W. Grimes 1515b81b6b3SRodney W. Grimes /* 1525b81b6b3SRodney W. Grimes * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 1535b81b6b3SRodney W. Grimes * whether the device exists or not (XXX should be elsewhere). Set flags 1545b81b6b3SRodney W. Grimes * to tell npxattach() what to do. Modify device struct if npx doesn't 1555b81b6b3SRodney W. Grimes * need to use interrupts. Return 1 if device exists. 1565b81b6b3SRodney W. Grimes */ 1575b81b6b3SRodney W. Grimes static int 1585b81b6b3SRodney W. Grimes npxprobe(dvp) 1595b81b6b3SRodney W. Grimes struct isa_device *dvp; 1605b81b6b3SRodney W. Grimes { 1615b81b6b3SRodney W. Grimes int result; 1625b81b6b3SRodney W. Grimes u_long save_eflags; 1635b81b6b3SRodney W. Grimes u_char save_icu1_mask; 1645b81b6b3SRodney W. Grimes u_char save_icu2_mask; 1655b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 1665b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxtrap; 1675b81b6b3SRodney W. Grimes /* 1685b81b6b3SRodney W. Grimes * This routine is now just a wrapper for npxprobe1(), to install 1695b81b6b3SRodney W. Grimes * special npx interrupt and trap handlers, to enable npx interrupts 1705b81b6b3SRodney W. Grimes * and to disable other interrupts. Someday isa_configure() will 1715b81b6b3SRodney W. Grimes * install suitable handlers and run with interrupts enabled so we 1725b81b6b3SRodney W. Grimes * won't need to do so much here. 1735b81b6b3SRodney W. Grimes */ 1745b81b6b3SRodney W. Grimes npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 1755b81b6b3SRodney W. Grimes save_eflags = read_eflags(); 1765b81b6b3SRodney W. Grimes disable_intr(); 1775b81b6b3SRodney W. Grimes save_icu1_mask = inb(IO_ICU1 + 1); 1785b81b6b3SRodney W. Grimes save_icu2_mask = inb(IO_ICU2 + 1); 1795b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 1805b81b6b3SRodney W. Grimes save_idt_npxtrap = idt[16]; 1815b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 1825b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 1835b81b6b3SRodney W. Grimes setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL); 1845b81b6b3SRodney W. Grimes setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL); 1855b81b6b3SRodney W. Grimes npx_idt_probeintr = idt[npx_intrno]; 1865b81b6b3SRodney W. Grimes enable_intr(); 1875b81b6b3SRodney W. Grimes result = npxprobe1(dvp); 1885b81b6b3SRodney W. Grimes disable_intr(); 1895b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, save_icu1_mask); 1905b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, save_icu2_mask); 1915b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 1925b81b6b3SRodney W. Grimes idt[16] = save_idt_npxtrap; 1935b81b6b3SRodney W. Grimes write_eflags(save_eflags); 1945b81b6b3SRodney W. Grimes return (result); 1955b81b6b3SRodney W. Grimes } 1965b81b6b3SRodney W. Grimes 1975b81b6b3SRodney W. Grimes static int 1985b81b6b3SRodney W. Grimes npxprobe1(dvp) 1995b81b6b3SRodney W. Grimes struct isa_device *dvp; 2005b81b6b3SRodney W. Grimes { 2015b81b6b3SRodney W. Grimes int control; 2025b81b6b3SRodney W. Grimes int status; 2035b81b6b3SRodney W. Grimes #ifdef lint 2045b81b6b3SRodney W. Grimes npxintr(); 2055b81b6b3SRodney W. Grimes #endif 2065b81b6b3SRodney W. Grimes /* 2075b81b6b3SRodney W. Grimes * Partially reset the coprocessor, if any. Some BIOS's don't reset 2085b81b6b3SRodney W. Grimes * it after a warm boot. 2095b81b6b3SRodney W. Grimes */ 2105b81b6b3SRodney W. Grimes outb(0xf1, 0); /* full reset on some systems, NOP on others */ 2115b81b6b3SRodney W. Grimes outb(0xf0, 0); /* clear BUSY# latch */ 2125b81b6b3SRodney W. Grimes /* 2135b81b6b3SRodney W. Grimes * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 2145b81b6b3SRodney W. Grimes * instructions. We must set the CR0_MP bit and use the CR0_TS 2155b81b6b3SRodney W. Grimes * bit to control the trap, because setting the CR0_EM bit does 2165b81b6b3SRodney W. Grimes * not cause WAIT instructions to trap. It's important to trap 2175b81b6b3SRodney W. Grimes * WAIT instructions - otherwise the "wait" variants of no-wait 2185b81b6b3SRodney W. Grimes * control instructions would degenerate to the "no-wait" variants 2195b81b6b3SRodney W. Grimes * after FP context switches but work correctly otherwise. It's 2205b81b6b3SRodney W. Grimes * particularly important to trap WAITs when there is no NPX - 2215b81b6b3SRodney W. Grimes * otherwise the "wait" variants would always degenerate. 2225b81b6b3SRodney W. Grimes * 2235b81b6b3SRodney W. Grimes * Try setting CR0_NE to get correct error reporting on 486DX's. 2245b81b6b3SRodney W. Grimes * Setting it should fail or do nothing on lesser processors. 2255b81b6b3SRodney W. Grimes */ 2265b81b6b3SRodney W. Grimes load_cr0(rcr0() | CR0_MP | CR0_NE); 2275b81b6b3SRodney W. Grimes /* 2285b81b6b3SRodney W. Grimes * But don't trap while we're probing. 2295b81b6b3SRodney W. Grimes */ 2305b81b6b3SRodney W. Grimes stop_emulating(); 2315b81b6b3SRodney W. Grimes /* 2325b81b6b3SRodney W. Grimes * Finish resetting the coprocessor, if any. If there is an error 2335b81b6b3SRodney W. Grimes * pending, then we may get a bogus IRQ13, but probeintr() will handle 2345b81b6b3SRodney W. Grimes * it OK. Bogus halts have never been observed, but we enabled 2355b81b6b3SRodney W. Grimes * IRQ13 and cleared the BUSY# latch early to handle them anyway. 2365b81b6b3SRodney W. Grimes */ 2375b81b6b3SRodney W. Grimes fninit(); 2385b81b6b3SRodney W. Grimes DELAY(1000); /* wait for any IRQ13 (fwait might hang) */ 2395b81b6b3SRodney W. Grimes #ifdef DIAGNOSTIC 2405b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) 2415b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx interrupt(s)\n", 2425b81b6b3SRodney W. Grimes npx_intrs_while_probing); 2435b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) 2445b81b6b3SRodney W. Grimes printf("fninit caused %u bogus npx trap(s)\n", 2455b81b6b3SRodney W. Grimes npx_traps_while_probing); 2465b81b6b3SRodney W. Grimes #endif 2475b81b6b3SRodney W. Grimes /* 2485b81b6b3SRodney W. Grimes * Check for a status of mostly zero. 2495b81b6b3SRodney W. Grimes */ 2505b81b6b3SRodney W. Grimes status = 0x5a5a; 2515b81b6b3SRodney W. Grimes fnstsw(&status); 2525b81b6b3SRodney W. Grimes if ((status & 0xb8ff) == 0) { 2535b81b6b3SRodney W. Grimes /* 2545b81b6b3SRodney W. Grimes * Good, now check for a proper control word. 2555b81b6b3SRodney W. Grimes */ 2565b81b6b3SRodney W. Grimes control = 0x5a5a; 2575b81b6b3SRodney W. Grimes fnstcw(&control); 2585b81b6b3SRodney W. Grimes if ((control & 0x1f3f) == 0x033f) { 259501c2393SGarrett Wollman hw_float = npx_exists = 1; 2605b81b6b3SRodney W. Grimes /* 2615b81b6b3SRodney W. Grimes * We have an npx, now divide by 0 to see if exception 2625b81b6b3SRodney W. Grimes * 16 works. 2635b81b6b3SRodney W. Grimes */ 2645b81b6b3SRodney W. Grimes control &= ~(1 << 2); /* enable divide by 0 trap */ 2655b81b6b3SRodney W. Grimes fldcw(&control); 2665b81b6b3SRodney W. Grimes npx_traps_while_probing = npx_intrs_while_probing = 0; 2675b81b6b3SRodney W. Grimes fp_divide_by_0(); 2685b81b6b3SRodney W. Grimes if (npx_traps_while_probing != 0) { 2695b81b6b3SRodney W. Grimes /* 2705b81b6b3SRodney W. Grimes * Good, exception 16 works. 2715b81b6b3SRodney W. Grimes */ 2725b81b6b3SRodney W. Grimes npx_ex16 = 1; 2735b81b6b3SRodney W. Grimes dvp->id_irq = 0; /* zap the interrupt */ 2745b81b6b3SRodney W. Grimes /* 2755b81b6b3SRodney W. Grimes * special return value to flag that we do not 2765b81b6b3SRodney W. Grimes * actually use any I/O registers 2775b81b6b3SRodney W. Grimes */ 2785b81b6b3SRodney W. Grimes return (-1); 2795b81b6b3SRodney W. Grimes } 2805b81b6b3SRodney W. Grimes if (npx_intrs_while_probing != 0) { 2815b81b6b3SRodney W. Grimes /* 2825b81b6b3SRodney W. Grimes * Bad, we are stuck with IRQ13. 2835b81b6b3SRodney W. Grimes */ 2845b81b6b3SRodney W. Grimes npx_irq13 = 1; 285d2306226SDavid Greenman npx0_imask = dvp->id_irq; /* npxattach too late */ 2865b81b6b3SRodney W. Grimes return (IO_NPXSIZE); 2875b81b6b3SRodney W. Grimes } 2885b81b6b3SRodney W. Grimes /* 2895b81b6b3SRodney W. Grimes * Worse, even IRQ13 is broken. Use emulator. 2905b81b6b3SRodney W. Grimes */ 2915b81b6b3SRodney W. Grimes } 2925b81b6b3SRodney W. Grimes } 2935b81b6b3SRodney W. Grimes /* 2945b81b6b3SRodney W. Grimes * Probe failed, but we want to get to npxattach to initialize the 2955b81b6b3SRodney W. Grimes * emulator and say that it has been installed. XXX handle devices 2965b81b6b3SRodney W. Grimes * that aren't really devices better. 2975b81b6b3SRodney W. Grimes */ 2985b81b6b3SRodney W. Grimes dvp->id_irq = 0; 2995c644711SRodney W. Grimes /* 3005c644711SRodney W. Grimes * special return value to flag that we do not 3015c644711SRodney W. Grimes * actually use any I/O registers 3025c644711SRodney W. Grimes */ 3035c644711SRodney W. Grimes return (-1); 3045b81b6b3SRodney W. Grimes } 3055b81b6b3SRodney W. Grimes 3062f86936aSGarrett Wollman static struct kern_devconf kdc_npx[NNPX] = { { 3072f86936aSGarrett Wollman 0, 0, 0, /* filled in by dev_attach */ 3082f86936aSGarrett Wollman "npx", 0, { MDDT_ISA, 0 }, 3092f86936aSGarrett Wollman isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 3102f86936aSGarrett Wollman &kdc_isa0, /* parent */ 3112f86936aSGarrett Wollman 0, /* parentdata */ 3122f86936aSGarrett Wollman DC_UNKNOWN, /* not supported */ 3132f86936aSGarrett Wollman "Floating-point unit" 3142f86936aSGarrett Wollman } }; 3152f86936aSGarrett Wollman 3162f86936aSGarrett Wollman static inline void 3172f86936aSGarrett Wollman npx_registerdev(struct isa_device *id) 3182f86936aSGarrett Wollman { 3192f86936aSGarrett Wollman if(id->id_unit) 3202f86936aSGarrett Wollman kdc_npx[id->id_unit] = kdc_npx[0]; 3212f86936aSGarrett Wollman kdc_npx[id->id_unit].kdc_unit = id->id_unit; 3222f86936aSGarrett Wollman kdc_npx[id->id_unit].kdc_isa = id; 3232f86936aSGarrett Wollman dev_attach(&kdc_npx[id->id_unit]); 3242f86936aSGarrett Wollman } 3252f86936aSGarrett Wollman 3265b81b6b3SRodney W. Grimes /* 3275b81b6b3SRodney W. Grimes * Attach routine - announce which it is, and wire into system 3285b81b6b3SRodney W. Grimes */ 3295b81b6b3SRodney W. Grimes int 3305b81b6b3SRodney W. Grimes npxattach(dvp) 3315b81b6b3SRodney W. Grimes struct isa_device *dvp; 3325b81b6b3SRodney W. Grimes { 3335c644711SRodney W. Grimes if (!npx_ex16 && !npx_irq13) { 334600f527fSDavid Greenman if (npx_exists) { 3352a6c8980SDavid Greenman printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); 336501c2393SGarrett Wollman hw_float = npx_exists = 0; 337600f527fSDavid Greenman } else { 3382a6c8980SDavid Greenman printf("npx%d: 387 Emulator\n",dvp->id_unit); 3395c644711SRodney W. Grimes } 340600f527fSDavid Greenman } 3415b81b6b3SRodney W. Grimes npxinit(__INITIAL_NPXCW__); 3422f86936aSGarrett Wollman npx_registerdev(dvp); 3435b81b6b3SRodney W. Grimes return (1); /* XXX unused */ 3445b81b6b3SRodney W. Grimes } 3455b81b6b3SRodney W. Grimes 3465b81b6b3SRodney W. Grimes /* 3475b81b6b3SRodney W. Grimes * Initialize floating point unit. 3485b81b6b3SRodney W. Grimes */ 3495b81b6b3SRodney W. Grimes void 3505b81b6b3SRodney W. Grimes npxinit(control) 3515b81b6b3SRodney W. Grimes u_int control; 3525b81b6b3SRodney W. Grimes { 3535b81b6b3SRodney W. Grimes struct save87 dummy; 3545b81b6b3SRodney W. Grimes 3555b81b6b3SRodney W. Grimes if (!npx_exists) 3565b81b6b3SRodney W. Grimes return; 3575b81b6b3SRodney W. Grimes /* 3585b81b6b3SRodney W. Grimes * fninit has the same h/w bugs as fnsave. Use the detoxified 3595b81b6b3SRodney W. Grimes * fnsave to throw away any junk in the fpu. fnsave initializes 3605b81b6b3SRodney W. Grimes * the fpu and sets npxproc = NULL as important side effects. 3615b81b6b3SRodney W. Grimes */ 3625b81b6b3SRodney W. Grimes npxsave(&dummy); 3635b81b6b3SRodney W. Grimes stop_emulating(); 3645b81b6b3SRodney W. Grimes fldcw(&control); 3655b81b6b3SRodney W. Grimes if (curpcb != NULL) 3665b81b6b3SRodney W. Grimes fnsave(&curpcb->pcb_savefpu); 3675b81b6b3SRodney W. Grimes start_emulating(); 3685b81b6b3SRodney W. Grimes } 3695b81b6b3SRodney W. Grimes 3705b81b6b3SRodney W. Grimes /* 3715b81b6b3SRodney W. Grimes * Free coprocessor (if we have it). 3725b81b6b3SRodney W. Grimes */ 3735b81b6b3SRodney W. Grimes void 3745b81b6b3SRodney W. Grimes npxexit(p) 3755b81b6b3SRodney W. Grimes struct proc *p; 3765b81b6b3SRodney W. Grimes { 3775b81b6b3SRodney W. Grimes 378663f1485SBruce Evans if (p == npxproc) 379663f1485SBruce Evans npxsave(&curpcb->pcb_savefpu); 380663f1485SBruce Evans if (npx_exists) { 381663f1485SBruce Evans u_int masked_exceptions; 382663f1485SBruce Evans 383663f1485SBruce Evans masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 384663f1485SBruce Evans & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 385663f1485SBruce Evans /* 386663f1485SBruce Evans * Overflow, divde by 0, and invalid operand would have 387663f1485SBruce Evans * caused a trap in 1.1.5. 388663f1485SBruce Evans */ 389663f1485SBruce Evans if (masked_exceptions & 0x0d) 390663f1485SBruce Evans log(LOG_ERR, 391663f1485SBruce Evans "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 392663f1485SBruce Evans p->p_pid, p->p_comm, masked_exceptions); 3935b81b6b3SRodney W. Grimes } 3945b81b6b3SRodney W. Grimes } 3955b81b6b3SRodney W. Grimes 3965b81b6b3SRodney W. Grimes /* 3975b81b6b3SRodney W. Grimes * Record the FPU state and reinitialize it all except for the control word. 3985b81b6b3SRodney W. Grimes * Then generate a SIGFPE. 3995b81b6b3SRodney W. Grimes * 4005b81b6b3SRodney W. Grimes * Reinitializing the state allows naive SIGFPE handlers to longjmp without 4015b81b6b3SRodney W. Grimes * doing any fixups. 4025b81b6b3SRodney W. Grimes * 4035b81b6b3SRodney W. Grimes * XXX there is currently no way to pass the full error state to signal 4045b81b6b3SRodney W. Grimes * handlers, and if this is a nested interrupt there is no way to pass even 4055b81b6b3SRodney W. Grimes * a status code! So there is no way to have a non-naive SIGFPE handler. At 4065b81b6b3SRodney W. Grimes * best a handler could do an fninit followed by an fldcw of a static value. 4075b81b6b3SRodney W. Grimes * fnclex would be of little use because it would leave junk on the FPU stack. 4085b81b6b3SRodney W. Grimes * Returning from the handler would be even less safe than usual because 4095b81b6b3SRodney W. Grimes * IRQ13 exception handling makes exceptions even less precise than usual. 4105b81b6b3SRodney W. Grimes */ 4115b81b6b3SRodney W. Grimes void 4125b81b6b3SRodney W. Grimes npxintr(frame) 4135b81b6b3SRodney W. Grimes struct intrframe frame; 4145b81b6b3SRodney W. Grimes { 4155b81b6b3SRodney W. Grimes int code; 4165b81b6b3SRodney W. Grimes 4175b81b6b3SRodney W. Grimes if (npxproc == NULL || !npx_exists) { 4185b81b6b3SRodney W. Grimes /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */ 4195b81b6b3SRodney W. Grimes printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", 4205b81b6b3SRodney W. Grimes (u_long) npxproc, (u_long) curproc, npx_exists); 4215b81b6b3SRodney W. Grimes panic("npxintr from nowhere"); 4225b81b6b3SRodney W. Grimes } 4235b81b6b3SRodney W. Grimes if (npxproc != curproc) { 4245b81b6b3SRodney W. Grimes printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", 4255b81b6b3SRodney W. Grimes (u_long) npxproc, (u_long) curproc, npx_exists); 4265b81b6b3SRodney W. Grimes panic("npxintr from non-current process"); 4275b81b6b3SRodney W. Grimes } 4285b81b6b3SRodney W. Grimes /* 4295b81b6b3SRodney W. Grimes * Save state. This does an implied fninit. It had better not halt 4305b81b6b3SRodney W. Grimes * the cpu or we'll hang. 4315b81b6b3SRodney W. Grimes */ 4325b81b6b3SRodney W. Grimes outb(0xf0, 0); 4335b81b6b3SRodney W. Grimes fnsave(&curpcb->pcb_savefpu); 4345b81b6b3SRodney W. Grimes fwait(); 4355b81b6b3SRodney W. Grimes /* 4365b81b6b3SRodney W. Grimes * Restore control word (was clobbered by fnsave). 4375b81b6b3SRodney W. Grimes */ 4385b81b6b3SRodney W. Grimes fldcw(&curpcb->pcb_savefpu.sv_env.en_cw); 4395b81b6b3SRodney W. Grimes fwait(); 4405b81b6b3SRodney W. Grimes /* 4415b81b6b3SRodney W. Grimes * Remember the exception status word and tag word. The current 4425b81b6b3SRodney W. Grimes * (almost fninit'ed) fpu state is in the fpu and the exception 4435b81b6b3SRodney W. Grimes * state just saved will soon be junk. However, the implied fninit 4445b81b6b3SRodney W. Grimes * doesn't change the error pointers or register contents, and we 4455b81b6b3SRodney W. Grimes * preserved the control word and will copy the status and tag 4465b81b6b3SRodney W. Grimes * words, so the complete exception state can be recovered. 4475b81b6b3SRodney W. Grimes */ 4485b81b6b3SRodney W. Grimes curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw; 4495b81b6b3SRodney W. Grimes curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw; 4505b81b6b3SRodney W. Grimes 4515b81b6b3SRodney W. Grimes /* 4525b81b6b3SRodney W. Grimes * Pass exception to process. 4535b81b6b3SRodney W. Grimes */ 4545b81b6b3SRodney W. Grimes if (ISPL(frame.if_cs) == SEL_UPL) { 4555b81b6b3SRodney W. Grimes /* 4565b81b6b3SRodney W. Grimes * Interrupt is essentially a trap, so we can afford to call 4575b81b6b3SRodney W. Grimes * the SIGFPE handler (if any) as soon as the interrupt 4585b81b6b3SRodney W. Grimes * returns. 4595b81b6b3SRodney W. Grimes * 4605b81b6b3SRodney W. Grimes * XXX little or nothing is gained from this, and plenty is 4615b81b6b3SRodney W. Grimes * lost - the interrupt frame has to contain the trap frame 4625b81b6b3SRodney W. Grimes * (this is otherwise only necessary for the rescheduling trap 4635b81b6b3SRodney W. Grimes * in doreti, and the frame for that could easily be set up 4645b81b6b3SRodney W. Grimes * just before it is used). 4655b81b6b3SRodney W. Grimes */ 46626f9a767SRodney W. Grimes curproc->p_md.md_regs = (int *)&frame.if_es; 4675b81b6b3SRodney W. Grimes #ifdef notyet 4685b81b6b3SRodney W. Grimes /* 4695b81b6b3SRodney W. Grimes * Encode the appropriate code for detailed information on 4705b81b6b3SRodney W. Grimes * this exception. 4715b81b6b3SRodney W. Grimes */ 4725b81b6b3SRodney W. Grimes code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 4735b81b6b3SRodney W. Grimes #else 4745b81b6b3SRodney W. Grimes code = 0; /* XXX */ 4755b81b6b3SRodney W. Grimes #endif 4765b81b6b3SRodney W. Grimes trapsignal(curproc, SIGFPE, code); 4775b81b6b3SRodney W. Grimes } else { 4785b81b6b3SRodney W. Grimes /* 4795b81b6b3SRodney W. Grimes * Nested interrupt. These losers occur when: 4805b81b6b3SRodney W. Grimes * o an IRQ13 is bogusly generated at a bogus time, e.g.: 4815b81b6b3SRodney W. Grimes * o immediately after an fnsave or frstor of an 4825b81b6b3SRodney W. Grimes * error state. 4835b81b6b3SRodney W. Grimes * o a couple of 386 instructions after 4845b81b6b3SRodney W. Grimes * "fstpl _memvar" causes a stack overflow. 4855b81b6b3SRodney W. Grimes * These are especially nasty when combined with a 4865b81b6b3SRodney W. Grimes * trace trap. 4875b81b6b3SRodney W. Grimes * o an IRQ13 occurs at the same time as another higher- 4885b81b6b3SRodney W. Grimes * priority interrupt. 4895b81b6b3SRodney W. Grimes * 4905b81b6b3SRodney W. Grimes * Treat them like a true async interrupt. 4915b81b6b3SRodney W. Grimes */ 4925b81b6b3SRodney W. Grimes psignal(npxproc, SIGFPE); 4935b81b6b3SRodney W. Grimes } 4945b81b6b3SRodney W. Grimes } 4955b81b6b3SRodney W. Grimes 4965b81b6b3SRodney W. Grimes /* 4975b81b6b3SRodney W. Grimes * Implement device not available (DNA) exception 4985b81b6b3SRodney W. Grimes * 4995b81b6b3SRodney W. Grimes * It would be better to switch FP context here (only). This would require 5005b81b6b3SRodney W. Grimes * saving the state in the proc table instead of in the pcb. 5015b81b6b3SRodney W. Grimes */ 5025b81b6b3SRodney W. Grimes int 5035b81b6b3SRodney W. Grimes npxdna() 5045b81b6b3SRodney W. Grimes { 5055b81b6b3SRodney W. Grimes if (!npx_exists) 5065b81b6b3SRodney W. Grimes return (0); 5075b81b6b3SRodney W. Grimes if (npxproc != NULL) { 5085b81b6b3SRodney W. Grimes printf("npxdna: npxproc = %lx, curproc = %lx\n", 5095b81b6b3SRodney W. Grimes (u_long) npxproc, (u_long) curproc); 5105b81b6b3SRodney W. Grimes panic("npxdna"); 5115b81b6b3SRodney W. Grimes } 5125b81b6b3SRodney W. Grimes stop_emulating(); 5135b81b6b3SRodney W. Grimes /* 5145b81b6b3SRodney W. Grimes * Record new context early in case frstor causes an IRQ13. 5155b81b6b3SRodney W. Grimes */ 5165b81b6b3SRodney W. Grimes npxproc = curproc; 5175b81b6b3SRodney W. Grimes /* 5185b81b6b3SRodney W. Grimes * The following frstor may cause an IRQ13 when the state being 5195b81b6b3SRodney W. Grimes * restored has a pending error. The error will appear to have been 5205b81b6b3SRodney W. Grimes * triggered by the current (npx) user instruction even when that 5215b81b6b3SRodney W. Grimes * instruction is a no-wait instruction that should not trigger an 5225b81b6b3SRodney W. Grimes * error (e.g., fnclex). On at least one 486 system all of the 5235b81b6b3SRodney W. Grimes * no-wait instructions are broken the same as frstor, so our 5245b81b6b3SRodney W. Grimes * treatment does not amplify the breakage. On at least one 5255b81b6b3SRodney W. Grimes * 386/Cyrix 387 system, fnclex works correctly while frstor and 5265b81b6b3SRodney W. Grimes * fnsave are broken, so our treatment breaks fnclex if it is the 5275b81b6b3SRodney W. Grimes * first FPU instruction after a context switch. 5285b81b6b3SRodney W. Grimes */ 5295b81b6b3SRodney W. Grimes frstor(&curpcb->pcb_savefpu); 5305b81b6b3SRodney W. Grimes 5315b81b6b3SRodney W. Grimes return (1); 5325b81b6b3SRodney W. Grimes } 5335b81b6b3SRodney W. Grimes 5345b81b6b3SRodney W. Grimes /* 5355b81b6b3SRodney W. Grimes * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 5365b81b6b3SRodney W. Grimes * pending, then fnsave generates a bogus IRQ13 on some systems. Force 5375b81b6b3SRodney W. Grimes * any IRQ13 to be handled immediately, and then ignore it. This routine is 5385b81b6b3SRodney W. Grimes * often called at splhigh so it must not use many system services. In 5395b81b6b3SRodney W. Grimes * particular, it's much easier to install a special handler than to 5405b81b6b3SRodney W. Grimes * guarantee that it's safe to use npxintr() and its supporting code. 5415b81b6b3SRodney W. Grimes */ 5425b81b6b3SRodney W. Grimes void 5435b81b6b3SRodney W. Grimes npxsave(addr) 5445b81b6b3SRodney W. Grimes struct save87 *addr; 5455b81b6b3SRodney W. Grimes { 5465b81b6b3SRodney W. Grimes u_char icu1_mask; 5475b81b6b3SRodney W. Grimes u_char icu2_mask; 5485b81b6b3SRodney W. Grimes u_char old_icu1_mask; 5495b81b6b3SRodney W. Grimes u_char old_icu2_mask; 5505b81b6b3SRodney W. Grimes struct gate_descriptor save_idt_npxintr; 5515b81b6b3SRodney W. Grimes 5525b81b6b3SRodney W. Grimes disable_intr(); 5535b81b6b3SRodney W. Grimes old_icu1_mask = inb(IO_ICU1 + 1); 5545b81b6b3SRodney W. Grimes old_icu2_mask = inb(IO_ICU2 + 1); 5555b81b6b3SRodney W. Grimes save_idt_npxintr = idt[npx_intrno]; 556d2306226SDavid Greenman outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 557d2306226SDavid Greenman outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 5585b81b6b3SRodney W. Grimes idt[npx_intrno] = npx_idt_probeintr; 5595b81b6b3SRodney W. Grimes enable_intr(); 5605b81b6b3SRodney W. Grimes stop_emulating(); 5615b81b6b3SRodney W. Grimes fnsave(addr); 5625b81b6b3SRodney W. Grimes fwait(); 5635b81b6b3SRodney W. Grimes start_emulating(); 5645b81b6b3SRodney W. Grimes npxproc = NULL; 5655b81b6b3SRodney W. Grimes disable_intr(); 5665b81b6b3SRodney W. Grimes icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 5675b81b6b3SRodney W. Grimes icu2_mask = inb(IO_ICU2 + 1); 5685b81b6b3SRodney W. Grimes outb(IO_ICU1 + 1, 569d2306226SDavid Greenman (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 5705b81b6b3SRodney W. Grimes outb(IO_ICU2 + 1, 571d2306226SDavid Greenman (icu2_mask & ~(npx0_imask >> 8)) 572d2306226SDavid Greenman | (old_icu2_mask & (npx0_imask >> 8))); 5735b81b6b3SRodney W. Grimes idt[npx_intrno] = save_idt_npxintr; 5745b81b6b3SRodney W. Grimes enable_intr(); /* back to usual state */ 5755b81b6b3SRodney W. Grimes } 5765b81b6b3SRodney W. Grimes 5775b81b6b3SRodney W. Grimes #endif /* NNPX > 0 */ 578