fpu.c revision 13290
1231437Sluigi/*- 2252869Sdelphij * Copyright (c) 1990 William Jolitz. 3231437Sluigi * Copyright (c) 1991 The Regents of the University of California. 4231437Sluigi * All rights reserved. 5231437Sluigi * 6231437Sluigi * Redistribution and use in source and binary forms, with or without 7231437Sluigi * modification, are permitted provided that the following conditions 8231437Sluigi * are met: 9231437Sluigi * 1. Redistributions of source code must retain the above copyright 10231437Sluigi * notice, this list of conditions and the following disclaimer. 11231437Sluigi * 2. Redistributions in binary form must reproduce the above copyright 12231437Sluigi * notice, this list of conditions and the following disclaimer in the 13231437Sluigi * documentation and/or other materials provided with the distribution. 14231437Sluigi * 3. All advertising materials mentioning features or use of this software 15231437Sluigi * must display the following acknowledgement: 16231437Sluigi * This product includes software developed by the University of 17231437Sluigi * California, Berkeley and its contributors. 18231437Sluigi * 4. Neither the name of the University nor the names of its contributors 19231437Sluigi * may be used to endorse or promote products derived from this software 20231437Sluigi * without specific prior written permission. 21231437Sluigi * 22231437Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23231437Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24231437Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25231437Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26231437Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27231437Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28231437Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29231437Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30231437Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31231437Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32231437Sluigi * SUCH DAMAGE. 33231437Sluigi * 34231437Sluigi * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 35231437Sluigi * $Id: npx.c,v 1.28 1996/01/04 19:51:14 wollman Exp $ 36231437Sluigi */ 37231437Sluigi 38231437Sluigi#include "npx.h" 39231437Sluigi#if NNPX > 0 40231437Sluigi 41231437Sluigi#include "opt_math_emulate.h" 42231437Sluigi 43231437Sluigi#include <sys/param.h> 44231437Sluigi#include <sys/systm.h> 45231437Sluigi#include <sys/kernel.h> 46231437Sluigi#include <sys/sysctl.h> 47258941Sdelphij#include <sys/conf.h> 48231437Sluigi#include <sys/file.h> 49257007Sdelphij#include <sys/proc.h> 50231437Sluigi#include <sys/devconf.h> 51231437Sluigi#include <sys/ioctl.h> 52247880Sdelphij#include <sys/syslog.h> 53231437Sluigi#include <sys/signalvar.h> 54231437Sluigi 55231437Sluigi#include <machine/cpu.h> 56231437Sluigi#include <machine/pcb.h> 57231437Sluigi#include <machine/trap.h> 58231437Sluigi#include <machine/clock.h> 59231437Sluigi#include <machine/specialreg.h> 60231437Sluigi 61231437Sluigi#include <i386/isa/icu.h> 62231437Sluigi#include <i386/isa/isa_device.h> 63231437Sluigi#include <i386/isa/isa.h> 64257007Sdelphij 65231437Sluigi/* 66247880Sdelphij * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 67231437Sluigi */ 68257007Sdelphij 69257007Sdelphij#ifdef __GNUC__ 70257007Sdelphij 71257007Sdelphij#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 72257007Sdelphij#define fnclex() __asm("fnclex") 73257007Sdelphij#define fninit() __asm("fninit") 74257007Sdelphij#define fnop() __asm("fnop") 75257007Sdelphij#define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 76231437Sluigi#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 77231437Sluigi#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 78231437Sluigi#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 79231437Sluigi#define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 80231437Sluigi#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 81231437Sluigi : : "n" (CR0_TS) : "ax") 82231437Sluigi#define stop_emulating() __asm("clts") 83231437Sluigi 84231437Sluigi#else /* not __GNUC__ */ 85231437Sluigi 86231437Sluigivoid fldcw __P((caddr_t addr)); 87231437Sluigivoid fnclex __P((void)); 88231437Sluigivoid fninit __P((void)); 89231437Sluigivoid fnop __P((void)); 90231437Sluigivoid fnsave __P((caddr_t addr)); 91231437Sluigivoid fnstcw __P((caddr_t addr)); 92231437Sluigivoid fnstsw __P((caddr_t addr)); 93231437Sluigivoid fp_divide_by_0 __P((void)); 94231437Sluigivoid frstor __P((caddr_t addr)); 95231437Sluigivoid start_emulating __P((void)); 96231437Sluigivoid stop_emulating __P((void)); 97231437Sluigi 98231437Sluigi#endif /* __GNUC__ */ 99231437Sluigi 100231437Sluigitypedef u_char bool_t; 101231437Sluigi 102231437Sluigistatic int npxattach __P((struct isa_device *dvp)); 103231437Sluigistatic int npxprobe __P((struct isa_device *dvp)); 104231437Sluigistatic int npxprobe1 __P((struct isa_device *dvp)); 105231437Sluigi 106247880Sdelphijstruct isa_driver npxdriver = { 107247880Sdelphij npxprobe, npxattach, "npx", 108231437Sluigi}; 109231437Sluigi 110231437Sluigiint hw_float; /* XXX currently just alias for npx_exists */ 111231437Sluigi 112231437SluigiSYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 113231437Sluigi CTLFLAG_RD, &hw_float, 0, 114231437Sluigi "Floatingpoint instructions executed in hardware"); 115231437Sluigi 116231437Sluigistatic u_int npx0_imask = SWI_CLOCK_MASK; 117231437Sluigistruct proc *npxproc; 118231437Sluigi 119231437Sluigistatic bool_t npx_ex16; 120231879Sluigistatic bool_t npx_exists; 121231879Sluigistatic struct gate_descriptor npx_idt_probeintr; 122231879Sluigistatic int npx_intrno; 123231879Sluigistatic volatile u_int npx_intrs_while_probing; 124231879Sluigistatic bool_t npx_irq13; 125231879Sluigistatic volatile u_int npx_traps_while_probing; 126231879Sluigi 127231437Sluigi/* 128231437Sluigi * Special interrupt handlers. Someday intr0-intr15 will be used to count 129231437Sluigi * interrupts. We'll still need a special exception 16 handler. The busy 130231437Sluigi * latch stuff in probeintr() can be moved to npxprobe(). 131231437Sluigi */ 132231437Sluigiinthand_t probeintr; 133231437Sluigiasm 134231437Sluigi(" 135258941Sdelphij .text 136258941Sdelphij_probeintr: 137258941Sdelphij ss 138258941Sdelphij incl _npx_intrs_while_probing 139247880Sdelphij pushl %eax 140247880Sdelphij movb $0x20,%al # EOI (asm in strings loses cpp features) 141247880Sdelphij outb %al,$0xa0 # IO_ICU2 142247880Sdelphij outb %al,$0x20 # IO_ICU1 143247880Sdelphij movb $0,%al 144247880Sdelphij outb %al,$0xf0 # clear BUSY# latch 145247880Sdelphij popl %eax 146247880Sdelphij iret 147247880Sdelphij"); 148247880Sdelphij 149247880Sdelphijinthand_t probetrap; 150247880Sdelphijasm 151247880Sdelphij(" 152231437Sluigi .text 153231437Sluigi_probetrap: 154231437Sluigi ss 155252869Sdelphij incl _npx_traps_while_probing 156231437Sluigi fnclex 157231437Sluigi iret 158231437Sluigi"); 159231437Sluigi 160231437Sluigistatic struct kern_devconf kdc_npx[NNPX] = { { 161231437Sluigi 0, 0, 0, /* filled in by dev_attach */ 162231437Sluigi "npx", 0, { MDDT_ISA, 0 }, 163231437Sluigi isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 164231437Sluigi &kdc_isa0, /* parent */ 165231437Sluigi 0, /* parentdata */ 166231437Sluigi DC_UNCONFIGURED, /* state */ 167231437Sluigi "Floating-point unit", 168231437Sluigi DC_CLS_MISC /* class */ 169257007Sdelphij} }; 170257007Sdelphij 171231437Sluigistatic inline void 172257007Sdelphijnpx_registerdev(struct isa_device *id) 173231437Sluigi{ 174231437Sluigi int unit; 175231437Sluigi 176231437Sluigi unit = id->id_unit; 177231437Sluigi if (unit != 0) 178258941Sdelphij kdc_npx[unit] = kdc_npx[0]; 179258941Sdelphij kdc_npx[unit].kdc_unit = unit; 180258941Sdelphij kdc_npx[unit].kdc_isa = id; 181258941Sdelphij dev_attach(&kdc_npx[unit]); 182258941Sdelphij} 183258941Sdelphij 184258941Sdelphij/* 185258941Sdelphij * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 186258941Sdelphij * whether the device exists or not (XXX should be elsewhere). Set flags 187258941Sdelphij * to tell npxattach() what to do. Modify device struct if npx doesn't 188258941Sdelphij * need to use interrupts. Return 1 if device exists. 189258941Sdelphij */ 190258941Sdelphijstatic int 191258941Sdelphijnpxprobe(dvp) 192258941Sdelphij struct isa_device *dvp; 193258941Sdelphij{ 194258941Sdelphij int result; 195258941Sdelphij u_long save_eflags; 196258941Sdelphij u_char save_icu1_mask; 197258941Sdelphij u_char save_icu2_mask; 198258941Sdelphij struct gate_descriptor save_idt_npxintr; 199258941Sdelphij struct gate_descriptor save_idt_npxtrap; 200258941Sdelphij /* 201258941Sdelphij * This routine is now just a wrapper for npxprobe1(), to install 202258941Sdelphij * special npx interrupt and trap handlers, to enable npx interrupts 203258941Sdelphij * and to disable other interrupts. Someday isa_configure() will 204258941Sdelphij * install suitable handlers and run with interrupts enabled so we 205258941Sdelphij * won't need to do so much here. 206258941Sdelphij */ 207231437Sluigi npx_registerdev(dvp); 208231437Sluigi npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 209231437Sluigi save_eflags = read_eflags(); 210231437Sluigi disable_intr(); 211231437Sluigi save_icu1_mask = inb(IO_ICU1 + 1); 212231437Sluigi save_icu2_mask = inb(IO_ICU2 + 1); 213231437Sluigi save_idt_npxintr = idt[npx_intrno]; 214231437Sluigi save_idt_npxtrap = idt[16]; 215231437Sluigi outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 216231437Sluigi outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 217231437Sluigi setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 218231437Sluigi setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 219231437Sluigi npx_idt_probeintr = idt[npx_intrno]; 220231437Sluigi enable_intr(); 221231437Sluigi result = npxprobe1(dvp); 222231437Sluigi disable_intr(); 223231437Sluigi outb(IO_ICU1 + 1, save_icu1_mask); 224231437Sluigi outb(IO_ICU2 + 1, save_icu2_mask); 225231437Sluigi idt[npx_intrno] = save_idt_npxintr; 226231437Sluigi idt[16] = save_idt_npxtrap; 227231437Sluigi write_eflags(save_eflags); 228231437Sluigi return (result); 229231437Sluigi} 230231437Sluigi 231231437Sluigistatic int 232231437Sluiginpxprobe1(dvp) 233231437Sluigi struct isa_device *dvp; 234231437Sluigi{ 235231437Sluigi u_short control; 236231437Sluigi u_short status; 237231437Sluigi 238231437Sluigi /* 239231437Sluigi * Partially reset the coprocessor, if any. Some BIOS's don't reset 240231437Sluigi * it after a warm boot. 241231437Sluigi */ 242231437Sluigi outb(0xf1, 0); /* full reset on some systems, NOP on others */ 243231437Sluigi outb(0xf0, 0); /* clear BUSY# latch */ 244231437Sluigi /* 245231437Sluigi * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 246231437Sluigi * instructions. We must set the CR0_MP bit and use the CR0_TS 247231437Sluigi * bit to control the trap, because setting the CR0_EM bit does 248231437Sluigi * not cause WAIT instructions to trap. It's important to trap 249231437Sluigi * WAIT instructions - otherwise the "wait" variants of no-wait 250231437Sluigi * control instructions would degenerate to the "no-wait" variants 251231437Sluigi * after FP context switches but work correctly otherwise. It's 252231437Sluigi * particularly important to trap WAITs when there is no NPX - 253231437Sluigi * otherwise the "wait" variants would always degenerate. 254231437Sluigi * 255231437Sluigi * Try setting CR0_NE to get correct error reporting on 486DX's. 256231437Sluigi * Setting it should fail or do nothing on lesser processors. 257231437Sluigi */ 258231437Sluigi load_cr0(rcr0() | CR0_MP | CR0_NE); 259231437Sluigi /* 260231437Sluigi * But don't trap while we're probing. 261231437Sluigi */ 262231437Sluigi stop_emulating(); 263231437Sluigi /* 264231437Sluigi * Finish resetting the coprocessor, if any. If there is an error 265231437Sluigi * pending, then we may get a bogus IRQ13, but probeintr() will handle 266231437Sluigi * it OK. Bogus halts have never been observed, but we enabled 267257007Sdelphij * IRQ13 and cleared the BUSY# latch early to handle them anyway. 268231437Sluigi */ 269231437Sluigi fninit(); 270231437Sluigi /* 271231437Sluigi * Don't use fwait here because it might hang. 272231437Sluigi * Don't use fnop here because it usually hangs if there is no FPU. 273231437Sluigi */ 274231437Sluigi DELAY(1000); /* wait for any IRQ13 */ 275257007Sdelphij#ifdef DIAGNOSTIC 276257007Sdelphij if (npx_intrs_while_probing != 0) 277231437Sluigi printf("fninit caused %u bogus npx interrupt(s)\n", 278231437Sluigi npx_intrs_while_probing); 279231437Sluigi if (npx_traps_while_probing != 0) 280231437Sluigi printf("fninit caused %u bogus npx trap(s)\n", 281231437Sluigi npx_traps_while_probing); 282231437Sluigi#endif 283231437Sluigi /* 284231437Sluigi * Check for a status of mostly zero. 285231437Sluigi */ 286231437Sluigi status = 0x5a5a; 287231437Sluigi fnstsw(&status); 288231437Sluigi if ((status & 0xb8ff) == 0) { 289231437Sluigi /* 290231437Sluigi * Good, now check for a proper control word. 291231437Sluigi */ 292257007Sdelphij control = 0x5a5a; 293257007Sdelphij fnstcw(&control); 294257007Sdelphij if ((control & 0x1f3f) == 0x033f) { 295231437Sluigi hw_float = npx_exists = 1; 296257007Sdelphij /* 297257007Sdelphij * We have an npx, now divide by 0 to see if exception 298257007Sdelphij * 16 works. 299231437Sluigi */ 300231437Sluigi control &= ~(1 << 2); /* enable divide by 0 trap */ 301257007Sdelphij fldcw(&control); 302257007Sdelphij npx_traps_while_probing = npx_intrs_while_probing = 0; 303257007Sdelphij fp_divide_by_0(); 304257007Sdelphij if (npx_traps_while_probing != 0) { 305257007Sdelphij /* 306257007Sdelphij * Good, exception 16 works. 307257007Sdelphij */ 308257007Sdelphij npx_ex16 = 1; 309257007Sdelphij dvp->id_irq = 0; /* zap the interrupt */ 310231437Sluigi /* 311257007Sdelphij * special return value to flag that we do not 312257007Sdelphij * actually use any I/O registers 313257007Sdelphij */ 314257007Sdelphij return (-1); 315257007Sdelphij } 316257007Sdelphij if (npx_intrs_while_probing != 0) { 317257007Sdelphij /* 318257007Sdelphij * Bad, we are stuck with IRQ13. 319257007Sdelphij */ 320257007Sdelphij npx_irq13 = 1; 321257007Sdelphij /* 322257007Sdelphij * npxattach would be too late to set npx0_imask. 323257007Sdelphij */ 324257007Sdelphij npx0_imask |= dvp->id_irq; 325257007Sdelphij return (IO_NPXSIZE); 326257007Sdelphij } 327257007Sdelphij /* 328257007Sdelphij * Worse, even IRQ13 is broken. Use emulator. 329257007Sdelphij */ 330257007Sdelphij } 331257007Sdelphij } 332257007Sdelphij /* 333257007Sdelphij * Probe failed, but we want to get to npxattach to initialize the 334257007Sdelphij * emulator and say that it has been installed. XXX handle devices 335257007Sdelphij * that aren't really devices better. 336257007Sdelphij */ 337257007Sdelphij dvp->id_irq = 0; 338257007Sdelphij /* 339257007Sdelphij * special return value to flag that we do not 340257007Sdelphij * actually use any I/O registers 341257007Sdelphij */ 342257007Sdelphij return (-1); 343257007Sdelphij} 344257007Sdelphij 345257007Sdelphij/* 346257007Sdelphij * Attach routine - announce which it is, and wire into system 347257007Sdelphij */ 348257007Sdelphijint 349257007Sdelphijnpxattach(dvp) 350257007Sdelphij struct isa_device *dvp; 351257007Sdelphij{ 352257007Sdelphij if (npx_ex16) 353257007Sdelphij printf("npx%d: INT 16 interface\n", dvp->id_unit); 354257007Sdelphij else if (npx_irq13) 355257007Sdelphij ; /* higher level has printed "irq 13" */ 356257007Sdelphij#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 357257007Sdelphij else if (npx_exists) { 358257007Sdelphij printf("npx%d: error reporting broken; using 387 emulator\n", 359257007Sdelphij dvp->id_unit); 360257007Sdelphij npx_exists = 0; 361257007Sdelphij } else 362257007Sdelphij printf("npx%d: 387 emulator\n",dvp->id_unit); 363257007Sdelphij#else 364257007Sdelphij else 365257007Sdelphij printf("npx%d: no 387 emulator in kernel!\n", dvp->id_unit); 366257007Sdelphij#endif 367257007Sdelphij npxinit(__INITIAL_NPXCW__); 368257007Sdelphij if (npx_exists) { 369257007Sdelphij kdc_npx[dvp->id_unit].kdc_state = DC_BUSY; 370257007Sdelphij } 371257007Sdelphij return (1); /* XXX unused */ 372257007Sdelphij} 373257007Sdelphij 374257007Sdelphij/* 375257007Sdelphij * Initialize floating point unit. 376257007Sdelphij */ 377257007Sdelphijvoid 378257007Sdelphijnpxinit(control) 379257007Sdelphij u_short control; 380257007Sdelphij{ 381257007Sdelphij struct save87 dummy; 382257007Sdelphij 383257007Sdelphij if (!npx_exists) 384257007Sdelphij return; 385231437Sluigi /* 386231437Sluigi * fninit has the same h/w bugs as fnsave. Use the detoxified 387231437Sluigi * fnsave to throw away any junk in the fpu. npxsave() initializes 388231437Sluigi * the fpu and sets npxproc = NULL as important side effects. 389231437Sluigi */ 390257007Sdelphij npxsave(&dummy); 391231437Sluigi stop_emulating(); 392231437Sluigi fldcw(&control); 393231437Sluigi if (curpcb != NULL) 394231437Sluigi fnsave(&curpcb->pcb_savefpu); 395231437Sluigi start_emulating(); 396257007Sdelphij} 397231437Sluigi 398257007Sdelphij/* 399231437Sluigi * Free coprocessor (if we have it). 400231437Sluigi */ 401231437Sluigivoid 402257007Sdelphijnpxexit(p) 403231437Sluigi struct proc *p; 404231437Sluigi{ 405231437Sluigi 406231437Sluigi if (p == npxproc) 407231437Sluigi npxsave(&curpcb->pcb_savefpu); 408231437Sluigi if (npx_exists) { 409231437Sluigi u_int masked_exceptions; 410231437Sluigi 411231437Sluigi masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 412231437Sluigi & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 413231437Sluigi /* 414257007Sdelphij * Overflow, divde by 0, and invalid operand would have 415231437Sluigi * caused a trap in 1.1.5. 416231437Sluigi */ 417231437Sluigi if (masked_exceptions & 0x0d) 418258140Sdelphij log(LOG_ERR, 419258140Sdelphij "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 420231437Sluigi p->p_pid, p->p_comm, masked_exceptions); 421231437Sluigi } 422257007Sdelphij} 423231437Sluigi 424231437Sluigi/* 425231437Sluigi * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 426231437Sluigi * 427257007Sdelphij * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 428257007Sdelphij * depend on longjmp() restoring a usable state. Restoring the state 429257007Sdelphij * or examining it might fail if we didn't clear exceptions. 430257007Sdelphij * 431231437Sluigi * XXX there is no standard way to tell SIGFPE handlers about the error 432257007Sdelphij * state. The old interface: 433257007Sdelphij * 434257007Sdelphij * void handler(int sig, int code, struct sigcontext *scp); 435257007Sdelphij * 436257007Sdelphij * is broken because it is non-ANSI and because the FP state is not in 437257007Sdelphij * struct sigcontext. 438257007Sdelphij * 439231437Sluigi * XXX the FP state is not preserved across signal handlers. So signal 440231437Sluigi * handlers cannot afford to do FP unless they preserve the state or 441231437Sluigi * longjmp() out. Both preserving the state and longjmp()ing may be 442257007Sdelphij * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 443257007Sdelphij * solution for signals other than SIGFPE. 444257007Sdelphij */ 445231437Sluigivoid 446231437Sluiginpxintr(unit) 447231437Sluigi int unit; 448231437Sluigi{ 449257007Sdelphij int code; 450231437Sluigi struct intrframe *frame; 451257007Sdelphij 452231437Sluigi if (npxproc == NULL || !npx_exists) { 453231437Sluigi printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 454257007Sdelphij npxproc, curproc, npx_exists); 455257007Sdelphij panic("npxintr from nowhere"); 456231437Sluigi } 457257007Sdelphij if (npxproc != curproc) { 458231437Sluigi printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 459231437Sluigi npxproc, curproc, npx_exists); 460231437Sluigi panic("npxintr from non-current process"); 461231437Sluigi } 462231437Sluigi 463231437Sluigi outb(0xf0, 0); 464231437Sluigi fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 465257007Sdelphij fnclex(); 466257007Sdelphij fnop(); 467231437Sluigi 468231437Sluigi /* 469257007Sdelphij * Pass exception to process. 470231437Sluigi */ 471231437Sluigi frame = (struct intrframe *)&unit; /* XXX */ 472231437Sluigi if (ISPL(frame->if_cs) == SEL_UPL) { 473231437Sluigi /* 474231437Sluigi * Interrupt is essentially a trap, so we can afford to call 475231437Sluigi * the SIGFPE handler (if any) as soon as the interrupt 476231437Sluigi * returns. 477257007Sdelphij * 478231437Sluigi * XXX little or nothing is gained from this, and plenty is 479257007Sdelphij * lost - the interrupt frame has to contain the trap frame 480231437Sluigi * (this is otherwise only necessary for the rescheduling trap 481231437Sluigi * in doreti, and the frame for that could easily be set up 482231437Sluigi * just before it is used). 483257007Sdelphij */ 484231437Sluigi curproc->p_md.md_regs = &frame->if_es; 485257007Sdelphij#ifdef notyet 486257007Sdelphij /* 487257007Sdelphij * Encode the appropriate code for detailed information on 488257007Sdelphij * this exception. 489257007Sdelphij */ 490257007Sdelphij code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 491257007Sdelphij#else 492257007Sdelphij code = 0; /* XXX */ 493257007Sdelphij#endif 494257007Sdelphij trapsignal(curproc, SIGFPE, code); 495257007Sdelphij } else { 496257007Sdelphij /* 497257007Sdelphij * Nested interrupt. These losers occur when: 498257007Sdelphij * o an IRQ13 is bogusly generated at a bogus time, e.g.: 499257007Sdelphij * o immediately after an fnsave or frstor of an 500257007Sdelphij * error state. 501257007Sdelphij * o a couple of 386 instructions after 502257007Sdelphij * "fstpl _memvar" causes a stack overflow. 503257007Sdelphij * These are especially nasty when combined with a 504257007Sdelphij * trace trap. 505257007Sdelphij * o an IRQ13 occurs at the same time as another higher- 506257007Sdelphij * priority interrupt. 507257007Sdelphij * 508257007Sdelphij * Treat them like a true async interrupt. 509257007Sdelphij */ 510257007Sdelphij psignal(curproc, SIGFPE); 511257007Sdelphij } 512257007Sdelphij} 513231437Sluigi 514231437Sluigi/* 515231437Sluigi * Implement device not available (DNA) exception 516257007Sdelphij * 517257007Sdelphij * It would be better to switch FP context here (if curproc != npxproc) 518257007Sdelphij * and not necessarily for every context switch, but it is too hard to 519257007Sdelphij * access foreign pcb's. 520257007Sdelphij */ 521257007Sdelphijint 522257007Sdelphijnpxdna() 523257007Sdelphij{ 524257007Sdelphij if (!npx_exists) 525257007Sdelphij return (0); 526257007Sdelphij if (npxproc != NULL) { 527257007Sdelphij printf("npxdna: npxproc = %p, curproc = %p\n", 528257007Sdelphij npxproc, curproc); 529257007Sdelphij panic("npxdna"); 530257007Sdelphij } 531257007Sdelphij stop_emulating(); 532257007Sdelphij /* 533257007Sdelphij * Record new context early in case frstor causes an IRQ13. 534257007Sdelphij */ 535257007Sdelphij npxproc = curproc; 536257007Sdelphij curpcb->pcb_savefpu.sv_ex_sw = 0; 537257007Sdelphij /* 538257007Sdelphij * The following frstor may cause an IRQ13 when the state being 539257007Sdelphij * restored has a pending error. The error will appear to have been 540257007Sdelphij * triggered by the current (npx) user instruction even when that 541257007Sdelphij * instruction is a no-wait instruction that should not trigger an 542257007Sdelphij * error (e.g., fnclex). On at least one 486 system all of the 543257007Sdelphij * no-wait instructions are broken the same as frstor, so our 544257007Sdelphij * treatment does not amplify the breakage. On at least one 545257007Sdelphij * 386/Cyrix 387 system, fnclex works correctly while frstor and 546257007Sdelphij * fnsave are broken, so our treatment breaks fnclex if it is the 547257007Sdelphij * first FPU instruction after a context switch. 548257007Sdelphij */ 549257007Sdelphij frstor(&curpcb->pcb_savefpu); 550257007Sdelphij 551257007Sdelphij return (1); 552257007Sdelphij} 553257007Sdelphij 554257007Sdelphij/* 555257007Sdelphij * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 556257007Sdelphij * pending, then fnsave generates a bogus IRQ13 on some systems. Force 557257007Sdelphij * any IRQ13 to be handled immediately, and then ignore it. This routine is 558257007Sdelphij * often called at splhigh so it must not use many system services. In 559257007Sdelphij * particular, it's much easier to install a special handler than to 560257007Sdelphij * guarantee that it's safe to use npxintr() and its supporting code. 561257007Sdelphij */ 562257007Sdelphijvoid 563257007Sdelphijnpxsave(addr) 564257007Sdelphij struct save87 *addr; 565257007Sdelphij{ 566257007Sdelphij u_char icu1_mask; 567257007Sdelphij u_char icu2_mask; 568257007Sdelphij u_char old_icu1_mask; 569257007Sdelphij u_char old_icu2_mask; 570257007Sdelphij struct gate_descriptor save_idt_npxintr; 571257007Sdelphij 572257007Sdelphij disable_intr(); 573257007Sdelphij old_icu1_mask = inb(IO_ICU1 + 1); 574257007Sdelphij old_icu2_mask = inb(IO_ICU2 + 1); 575257007Sdelphij save_idt_npxintr = idt[npx_intrno]; 576257007Sdelphij outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 577257007Sdelphij outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 578257007Sdelphij idt[npx_intrno] = npx_idt_probeintr; 579257007Sdelphij enable_intr(); 580257007Sdelphij stop_emulating(); 581257007Sdelphij fnsave(addr); 582257007Sdelphij fnop(); 583257007Sdelphij start_emulating(); 584257007Sdelphij npxproc = NULL; 585257007Sdelphij disable_intr(); 586257007Sdelphij icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 587257007Sdelphij icu2_mask = inb(IO_ICU2 + 1); 588257007Sdelphij outb(IO_ICU1 + 1, 589257007Sdelphij (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 590257007Sdelphij outb(IO_ICU2 + 1, 591257007Sdelphij (icu2_mask & ~(npx0_imask >> 8)) 592257007Sdelphij | (old_icu2_mask & (npx0_imask >> 8))); 593257007Sdelphij idt[npx_intrno] = save_idt_npxintr; 594257007Sdelphij enable_intr(); /* back to usual state */ 595231437Sluigi} 596231437Sluigi 597231437Sluigi#endif /* NNPX > 0 */ 598231437Sluigi