fpu.c revision 12929
160484Sobrien/*- 2218822Sdim * Copyright (c) 1990 William Jolitz. 3130561Sobrien * Copyright (c) 1991 The Regents of the University of California. 460484Sobrien * All rights reserved. 560484Sobrien * 660484Sobrien * Redistribution and use in source and binary forms, with or without 760484Sobrien * modification, are permitted provided that the following conditions 860484Sobrien * are met: 960484Sobrien * 1. Redistributions of source code must retain the above copyright 1060484Sobrien * notice, this list of conditions and the following disclaimer. 1160484Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1260484Sobrien * notice, this list of conditions and the following disclaimer in the 1360484Sobrien * documentation and/or other materials provided with the distribution. 1460484Sobrien * 3. All advertising materials mentioning features or use of this software 1560484Sobrien * must display the following acknowledgement: 1660484Sobrien * This product includes software developed by the University of 1760484Sobrien * California, Berkeley and its contributors. 1860484Sobrien * 4. Neither the name of the University nor the names of its contributors 1960484Sobrien * may be used to endorse or promote products derived from this software 20218822Sdim * without specific prior written permission. 2160484Sobrien * 22218822Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2360484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2460484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25130561Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2660484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2760484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2860484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2960484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3060484Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3160484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3260484Sobrien * SUCH DAMAGE. 33130561Sobrien * 34218822Sdim * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 3560484Sobrien * $Id: npx.c,v 1.26 1995/12/10 13:39:02 phk Exp $ 3660484Sobrien */ 37218822Sdim 3860484Sobrien#include "npx.h" 3960484Sobrien#if NNPX > 0 4060484Sobrien 4160484Sobrien#include <sys/param.h> 4260484Sobrien#include <sys/systm.h> 4360484Sobrien#include <sys/kernel.h> 4460484Sobrien#include <sys/sysctl.h> 4560484Sobrien#include <sys/conf.h> 46130561Sobrien#include <sys/file.h> 47130561Sobrien#include <sys/proc.h> 4860484Sobrien#include <sys/devconf.h> 4960484Sobrien#include <sys/ioctl.h> 5060484Sobrien#include <sys/syslog.h> 5160484Sobrien#include <sys/signalvar.h> 52218822Sdim 5360484Sobrien#include <machine/cpu.h> 5460484Sobrien#include <machine/pcb.h> 5560484Sobrien#include <machine/trap.h> 56218822Sdim#include <machine/clock.h> 5760484Sobrien#include <machine/specialreg.h> 5860484Sobrien 5960484Sobrien#include <i386/isa/icu.h> 60218822Sdim#include <i386/isa/isa_device.h> 6160484Sobrien#include <i386/isa/isa.h> 6260484Sobrien 6360484Sobrien/* 6460484Sobrien * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 6560484Sobrien */ 66218822Sdim 6760484Sobrien#ifdef __GNUC__ 6860484Sobrien 69218822Sdim#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 7060484Sobrien#define fnclex() __asm("fnclex") 7160484Sobrien#define fninit() __asm("fninit") 7260484Sobrien#define fnop() __asm("fnop") 7360484Sobrien#define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 7460484Sobrien#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 7560484Sobrien#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 7660484Sobrien#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 7760484Sobrien#define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 7860484Sobrien#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 7960484Sobrien : : "n" (CR0_TS) : "ax") 8060484Sobrien#define stop_emulating() __asm("clts") 8160484Sobrien 8260484Sobrien#else /* not __GNUC__ */ 8360484Sobrien 8489857Sobrienvoid fldcw __P((caddr_t addr)); 8560484Sobrienvoid fnclex __P((void)); 8689857Sobrienvoid fninit __P((void)); 8760484Sobrienvoid fnop __P((void)); 8860484Sobrienvoid fnsave __P((caddr_t addr)); 8960484Sobrienvoid fnstcw __P((caddr_t addr)); 9060484Sobrienvoid fnstsw __P((caddr_t addr)); 9160484Sobrienvoid fp_divide_by_0 __P((void)); 9289857Sobrienvoid frstor __P((caddr_t addr)); 9389857Sobrienvoid start_emulating __P((void)); 9489857Sobrienvoid stop_emulating __P((void)); 9560484Sobrien 9660484Sobrien#endif /* __GNUC__ */ 9760484Sobrien 9860484Sobrientypedef u_char bool_t; 9960484Sobrien 10060484Sobrienstatic int npxattach __P((struct isa_device *dvp)); 10177298Sobrienstatic int npxprobe __P((struct isa_device *dvp)); 10260484Sobrienstatic int npxprobe1 __P((struct isa_device *dvp)); 10360484Sobrien 10460484Sobrienstruct isa_driver npxdriver = { 10560484Sobrien npxprobe, npxattach, "npx", 10660484Sobrien}; 10760484Sobrien 10860484Sobrienint hw_float; /* XXX currently just alias for npx_exists */ 10960484Sobrien 11060484SobrienSYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 11160484Sobrien CTLFLAG_RD, &hw_float, 0, 11260484Sobrien "Floatingpoint instructions executed in hardware"); 11360484Sobrien 11460484Sobrienstatic u_int npx0_imask = SWI_CLOCK_MASK; 11560484Sobrienstruct proc *npxproc; 11660484Sobrien 11760484Sobrienstatic bool_t npx_ex16; 11860484Sobrienstatic bool_t npx_exists; 11960484Sobrienstatic struct gate_descriptor npx_idt_probeintr; 12060484Sobrienstatic int npx_intrno; 12160484Sobrienstatic volatile u_int npx_intrs_while_probing; 12260484Sobrienstatic bool_t npx_irq13; 12360484Sobrienstatic volatile u_int npx_traps_while_probing; 12460484Sobrien 12560484Sobrien/* 126218822Sdim * Special interrupt handlers. Someday intr0-intr15 will be used to count 12760484Sobrien * interrupts. We'll still need a special exception 16 handler. The busy 12860484Sobrien * latch stuff in probeintr() can be moved to npxprobe(). 12960484Sobrien */ 130218822Sdiminthand_t probeintr; 131218822Sdimasm 13260484Sobrien(" 133218822Sdim .text 13460484Sobrien_probeintr: 13560484Sobrien ss 13660484Sobrien incl _npx_intrs_while_probing 13789857Sobrien pushl %eax 13889857Sobrien movb $0x20,%al # EOI (asm in strings loses cpp features) 13989857Sobrien outb %al,$0xa0 # IO_ICU2 14089857Sobrien outb %al,$0x20 # IO_ICU1 14160484Sobrien movb $0,%al 14277298Sobrien outb %al,$0xf0 # clear BUSY# latch 14360484Sobrien popl %eax 14460484Sobrien iret 14560484Sobrien"); 14660484Sobrien 14760484Sobrieninthand_t probetrap; 14889857Sobrienasm 14989857Sobrien(" 15060484Sobrien .text 15160484Sobrien_probetrap: 15260484Sobrien ss 15377298Sobrien incl _npx_traps_while_probing 15460484Sobrien fnclex 15560484Sobrien iret 15660484Sobrien"); 15777298Sobrien 15877298Sobrienstatic struct kern_devconf kdc_npx[NNPX] = { { 15960484Sobrien 0, 0, 0, /* filled in by dev_attach */ 16060484Sobrien "npx", 0, { MDDT_ISA, 0 }, 16189857Sobrien isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, 16260484Sobrien &kdc_isa0, /* parent */ 16389857Sobrien 0, /* parentdata */ 16460484Sobrien DC_UNCONFIGURED, /* state */ 16577298Sobrien "Floating-point unit", 166130561Sobrien DC_CLS_MISC /* class */ 167130561Sobrien} }; 168130561Sobrien 16989857Sobrienstatic inline void 17077298Sobriennpx_registerdev(struct isa_device *id) 17177298Sobrien{ 17260484Sobrien int unit; 173130561Sobrien 17460484Sobrien unit = id->id_unit; 175130561Sobrien if (unit != 0) 176130561Sobrien kdc_npx[unit] = kdc_npx[0]; 17789857Sobrien kdc_npx[unit].kdc_unit = unit; 17877298Sobrien kdc_npx[unit].kdc_isa = id; 17977298Sobrien dev_attach(&kdc_npx[unit]); 18077298Sobrien} 181130561Sobrien 182130561Sobrien/* 183130561Sobrien * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 18489857Sobrien * whether the device exists or not (XXX should be elsewhere). Set flags 18560484Sobrien * to tell npxattach() what to do. Modify device struct if npx doesn't 18660484Sobrien * need to use interrupts. Return 1 if device exists. 18777298Sobrien */ 18860484Sobrienstatic int 18960484Sobriennpxprobe(dvp) 19060484Sobrien struct isa_device *dvp; 19160484Sobrien{ 19260484Sobrien int result; 19377298Sobrien u_long save_eflags; 19477298Sobrien u_char save_icu1_mask; 19589857Sobrien u_char save_icu2_mask; 19660484Sobrien struct gate_descriptor save_idt_npxintr; 19789857Sobrien struct gate_descriptor save_idt_npxtrap; 19889857Sobrien /* 19989857Sobrien * This routine is now just a wrapper for npxprobe1(), to install 20089857Sobrien * special npx interrupt and trap handlers, to enable npx interrupts 20189857Sobrien * and to disable other interrupts. Someday isa_configure() will 20289857Sobrien * install suitable handlers and run with interrupts enabled so we 20389857Sobrien * won't need to do so much here. 20460484Sobrien */ 20560484Sobrien npx_registerdev(dvp); 20660484Sobrien npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 20760484Sobrien save_eflags = read_eflags(); 20889857Sobrien disable_intr(); 20960484Sobrien save_icu1_mask = inb(IO_ICU1 + 1); 21060484Sobrien save_icu2_mask = inb(IO_ICU2 + 1); 21160484Sobrien save_idt_npxintr = idt[npx_intrno]; 21260484Sobrien save_idt_npxtrap = idt[16]; 21360484Sobrien outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 21460484Sobrien outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 21577298Sobrien setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 21660484Sobrien setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 21789857Sobrien npx_idt_probeintr = idt[npx_intrno]; 21889857Sobrien enable_intr(); 21989857Sobrien result = npxprobe1(dvp); 220130561Sobrien disable_intr(); 221130561Sobrien outb(IO_ICU1 + 1, save_icu1_mask); 22289857Sobrien outb(IO_ICU2 + 1, save_icu2_mask); 223130561Sobrien idt[npx_intrno] = save_idt_npxintr; 22460484Sobrien idt[16] = save_idt_npxtrap; 22577298Sobrien write_eflags(save_eflags); 22660484Sobrien return (result); 22760484Sobrien} 22860484Sobrien 22960484Sobrienstatic int 23060484Sobriennpxprobe1(dvp) 23189857Sobrien struct isa_device *dvp; 23260484Sobrien{ 23360484Sobrien u_short control; 23460484Sobrien u_short status; 23560484Sobrien 23660484Sobrien /* 23789857Sobrien * Partially reset the coprocessor, if any. Some BIOS's don't reset 23860484Sobrien * it after a warm boot. 23989857Sobrien */ 24060484Sobrien outb(0xf1, 0); /* full reset on some systems, NOP on others */ 241130561Sobrien outb(0xf0, 0); /* clear BUSY# latch */ 24260484Sobrien /* 24360484Sobrien * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 24460484Sobrien * instructions. We must set the CR0_MP bit and use the CR0_TS 24560484Sobrien * bit to control the trap, because setting the CR0_EM bit does 24660484Sobrien * not cause WAIT instructions to trap. It's important to trap 24760484Sobrien * WAIT instructions - otherwise the "wait" variants of no-wait 24860484Sobrien * control instructions would degenerate to the "no-wait" variants 24977298Sobrien * after FP context switches but work correctly otherwise. It's 25060484Sobrien * particularly important to trap WAITs when there is no NPX - 25160484Sobrien * otherwise the "wait" variants would always degenerate. 25260484Sobrien * 25360484Sobrien * Try setting CR0_NE to get correct error reporting on 486DX's. 25460484Sobrien * Setting it should fail or do nothing on lesser processors. 25560484Sobrien */ 25660484Sobrien load_cr0(rcr0() | CR0_MP | CR0_NE); 25777298Sobrien /* 25860484Sobrien * But don't trap while we're probing. 25960484Sobrien */ 26060484Sobrien stop_emulating(); 261130561Sobrien /* 262130561Sobrien * Finish resetting the coprocessor, if any. If there is an error 26360484Sobrien * pending, then we may get a bogus IRQ13, but probeintr() will handle 264130561Sobrien * it OK. Bogus halts have never been observed, but we enabled 26560484Sobrien * IRQ13 and cleared the BUSY# latch early to handle them anyway. 26660484Sobrien */ 26760484Sobrien fninit(); 26860484Sobrien /* 269130561Sobrien * Don't use fwait here because it might hang. 27060484Sobrien * Don't use fnop here because it usually hangs if there is no FPU. 27160484Sobrien */ 272130561Sobrien DELAY(1000); /* wait for any IRQ13 */ 27360484Sobrien#ifdef DIAGNOSTIC 27460484Sobrien if (npx_intrs_while_probing != 0) 275130561Sobrien printf("fninit caused %u bogus npx interrupt(s)\n", 27660484Sobrien npx_intrs_while_probing); 27760484Sobrien if (npx_traps_while_probing != 0) 278130561Sobrien printf("fninit caused %u bogus npx trap(s)\n", 27960484Sobrien npx_traps_while_probing); 28060484Sobrien#endif 281130561Sobrien /* 28260484Sobrien * Check for a status of mostly zero. 28360484Sobrien */ 284130561Sobrien status = 0x5a5a; 28560484Sobrien fnstsw(&status); 28660484Sobrien if ((status & 0xb8ff) == 0) { 287130561Sobrien /* 28860484Sobrien * Good, now check for a proper control word. 28960484Sobrien */ 29060484Sobrien control = 0x5a5a; 29160484Sobrien fnstcw(&control); 29260484Sobrien if ((control & 0x1f3f) == 0x033f) { 29360484Sobrien hw_float = npx_exists = 1; 29460484Sobrien /* 29560484Sobrien * We have an npx, now divide by 0 to see if exception 29660484Sobrien * 16 works. 29760484Sobrien */ 29860484Sobrien control &= ~(1 << 2); /* enable divide by 0 trap */ 299130561Sobrien fldcw(&control); 300130561Sobrien npx_traps_while_probing = npx_intrs_while_probing = 0; 301130561Sobrien fp_divide_by_0(); 302130561Sobrien if (npx_traps_while_probing != 0) { 303130561Sobrien /* 30460484Sobrien * Good, exception 16 works. 30560484Sobrien */ 30660484Sobrien npx_ex16 = 1; 30760484Sobrien dvp->id_irq = 0; /* zap the interrupt */ 30860484Sobrien /* 30960484Sobrien * special return value to flag that we do not 31089857Sobrien * actually use any I/O registers 31160484Sobrien */ 31260484Sobrien return (-1); 31360484Sobrien } 31460484Sobrien if (npx_intrs_while_probing != 0) { 31560484Sobrien /* 31689857Sobrien * Bad, we are stuck with IRQ13. 31789857Sobrien */ 31889857Sobrien npx_irq13 = 1; 31989857Sobrien /* 32089857Sobrien * npxattach would be too late to set npx0_imask. 32189857Sobrien */ 32289857Sobrien npx0_imask |= dvp->id_irq; 32389857Sobrien return (IO_NPXSIZE); 32460484Sobrien } 32560484Sobrien /* 32660484Sobrien * Worse, even IRQ13 is broken. Use emulator. 32760484Sobrien */ 32860484Sobrien } 32960484Sobrien } 33060484Sobrien /* 33160484Sobrien * Probe failed, but we want to get to npxattach to initialize the 33260484Sobrien * emulator and say that it has been installed. XXX handle devices 33360484Sobrien * that aren't really devices better. 33460484Sobrien */ 33560484Sobrien dvp->id_irq = 0; 33660484Sobrien /* 33760484Sobrien * special return value to flag that we do not 33860484Sobrien * actually use any I/O registers 33960484Sobrien */ 34060484Sobrien return (-1); 34177298Sobrien} 34277298Sobrien 34360484Sobrien/* 34460484Sobrien * Attach routine - announce which it is, and wire into system 34560484Sobrien */ 34660484Sobrienint 34760484Sobriennpxattach(dvp) 34877298Sobrien struct isa_device *dvp; 34960484Sobrien{ 35077298Sobrien if (npx_ex16) 35160484Sobrien printf("npx%d: INT 16 interface\n", dvp->id_unit); 35260484Sobrien else if (npx_irq13) 353130561Sobrien ; /* higher level has printed "irq 13" */ 354130561Sobrien#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 355130561Sobrien else if (npx_exists) { 356130561Sobrien printf("npx%d: error reporting broken; using 387 emulator\n", 357130561Sobrien dvp->id_unit); 358130561Sobrien npx_exists = 0; 359130561Sobrien } else 36060484Sobrien printf("npx%d: 387 emulator\n",dvp->id_unit); 36160484Sobrien#else 36260484Sobrien else 36360484Sobrien printf("npx%d: no 387 emulator in kernel!\n", dvp->id_unit); 364130561Sobrien#endif 365130561Sobrien npxinit(__INITIAL_NPXCW__); 366130561Sobrien if (npx_exists) { 36760484Sobrien kdc_npx[dvp->id_unit].kdc_state = DC_BUSY; 36860484Sobrien } 36960484Sobrien return (1); /* XXX unused */ 370130561Sobrien} 371130561Sobrien 372130561Sobrien/* 373130561Sobrien * Initialize floating point unit. 374130561Sobrien */ 375130561Sobrienvoid 376130561Sobriennpxinit(control) 377130561Sobrien u_short control; 378130561Sobrien{ 37960484Sobrien struct save87 dummy; 380130561Sobrien 381130561Sobrien if (!npx_exists) 382130561Sobrien return; 383130561Sobrien /* 384130561Sobrien * fninit has the same h/w bugs as fnsave. Use the detoxified 38560484Sobrien * fnsave to throw away any junk in the fpu. npxsave() initializes 386130561Sobrien * the fpu and sets npxproc = NULL as important side effects. 387130561Sobrien */ 388130561Sobrien npxsave(&dummy); 38960484Sobrien stop_emulating(); 390130561Sobrien fldcw(&control); 391130561Sobrien if (curpcb != NULL) 392130561Sobrien fnsave(&curpcb->pcb_savefpu); 39360484Sobrien start_emulating(); 39460484Sobrien} 39589857Sobrien 39689857Sobrien/* 39789857Sobrien * Free coprocessor (if we have it). 39889857Sobrien */ 39989857Sobrienvoid 40089857Sobriennpxexit(p) 40189857Sobrien struct proc *p; 40289857Sobrien{ 40389857Sobrien 40460484Sobrien if (p == npxproc) 40560484Sobrien npxsave(&curpcb->pcb_savefpu); 40677298Sobrien if (npx_exists) { 407130561Sobrien u_int masked_exceptions; 408130561Sobrien 409 masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 410 & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 411 /* 412 * Overflow, divde by 0, and invalid operand would have 413 * caused a trap in 1.1.5. 414 */ 415 if (masked_exceptions & 0x0d) 416 log(LOG_ERR, 417 "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 418 p->p_pid, p->p_comm, masked_exceptions); 419 } 420} 421 422/* 423 * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 424 * 425 * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 426 * depend on longjmp() restoring a usable state. Restoring the state 427 * or examining it might fail if we didn't clear exceptions. 428 * 429 * XXX there is no standard way to tell SIGFPE handlers about the error 430 * state. The old interface: 431 * 432 * void handler(int sig, int code, struct sigcontext *scp); 433 * 434 * is broken because it is non-ANSI and because the FP state is not in 435 * struct sigcontext. 436 * 437 * XXX the FP state is not preserved across signal handlers. So signal 438 * handlers cannot afford to do FP unless they preserve the state or 439 * longjmp() out. Both preserving the state and longjmp()ing may be 440 * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 441 * solution for signals other than SIGFPE. 442 */ 443void 444npxintr(unit) 445 int unit; 446{ 447 int code; 448 struct intrframe *frame; 449 450 if (npxproc == NULL || !npx_exists) { 451 printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 452 npxproc, curproc, npx_exists); 453 panic("npxintr from nowhere"); 454 } 455 if (npxproc != curproc) { 456 printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 457 npxproc, curproc, npx_exists); 458 panic("npxintr from non-current process"); 459 } 460 461 outb(0xf0, 0); 462 fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 463 fnclex(); 464 fnop(); 465 466 /* 467 * Pass exception to process. 468 */ 469 frame = (struct intrframe *)&unit; /* XXX */ 470 if (ISPL(frame->if_cs) == SEL_UPL) { 471 /* 472 * Interrupt is essentially a trap, so we can afford to call 473 * the SIGFPE handler (if any) as soon as the interrupt 474 * returns. 475 * 476 * XXX little or nothing is gained from this, and plenty is 477 * lost - the interrupt frame has to contain the trap frame 478 * (this is otherwise only necessary for the rescheduling trap 479 * in doreti, and the frame for that could easily be set up 480 * just before it is used). 481 */ 482 curproc->p_md.md_regs = &frame->if_es; 483#ifdef notyet 484 /* 485 * Encode the appropriate code for detailed information on 486 * this exception. 487 */ 488 code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 489#else 490 code = 0; /* XXX */ 491#endif 492 trapsignal(curproc, SIGFPE, code); 493 } else { 494 /* 495 * Nested interrupt. These losers occur when: 496 * o an IRQ13 is bogusly generated at a bogus time, e.g.: 497 * o immediately after an fnsave or frstor of an 498 * error state. 499 * o a couple of 386 instructions after 500 * "fstpl _memvar" causes a stack overflow. 501 * These are especially nasty when combined with a 502 * trace trap. 503 * o an IRQ13 occurs at the same time as another higher- 504 * priority interrupt. 505 * 506 * Treat them like a true async interrupt. 507 */ 508 psignal(curproc, SIGFPE); 509 } 510} 511 512/* 513 * Implement device not available (DNA) exception 514 * 515 * It would be better to switch FP context here (if curproc != npxproc) 516 * and not necessarily for every context switch, but it is too hard to 517 * access foreign pcb's. 518 */ 519int 520npxdna() 521{ 522 if (!npx_exists) 523 return (0); 524 if (npxproc != NULL) { 525 printf("npxdna: npxproc = %p, curproc = %p\n", 526 npxproc, curproc); 527 panic("npxdna"); 528 } 529 stop_emulating(); 530 /* 531 * Record new context early in case frstor causes an IRQ13. 532 */ 533 npxproc = curproc; 534 curpcb->pcb_savefpu.sv_ex_sw = 0; 535 /* 536 * The following frstor may cause an IRQ13 when the state being 537 * restored has a pending error. The error will appear to have been 538 * triggered by the current (npx) user instruction even when that 539 * instruction is a no-wait instruction that should not trigger an 540 * error (e.g., fnclex). On at least one 486 system all of the 541 * no-wait instructions are broken the same as frstor, so our 542 * treatment does not amplify the breakage. On at least one 543 * 386/Cyrix 387 system, fnclex works correctly while frstor and 544 * fnsave are broken, so our treatment breaks fnclex if it is the 545 * first FPU instruction after a context switch. 546 */ 547 frstor(&curpcb->pcb_savefpu); 548 549 return (1); 550} 551 552/* 553 * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 554 * pending, then fnsave generates a bogus IRQ13 on some systems. Force 555 * any IRQ13 to be handled immediately, and then ignore it. This routine is 556 * often called at splhigh so it must not use many system services. In 557 * particular, it's much easier to install a special handler than to 558 * guarantee that it's safe to use npxintr() and its supporting code. 559 */ 560void 561npxsave(addr) 562 struct save87 *addr; 563{ 564 u_char icu1_mask; 565 u_char icu2_mask; 566 u_char old_icu1_mask; 567 u_char old_icu2_mask; 568 struct gate_descriptor save_idt_npxintr; 569 570 disable_intr(); 571 old_icu1_mask = inb(IO_ICU1 + 1); 572 old_icu2_mask = inb(IO_ICU2 + 1); 573 save_idt_npxintr = idt[npx_intrno]; 574 outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 575 outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 576 idt[npx_intrno] = npx_idt_probeintr; 577 enable_intr(); 578 stop_emulating(); 579 fnsave(addr); 580 fnop(); 581 start_emulating(); 582 npxproc = NULL; 583 disable_intr(); 584 icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 585 icu2_mask = inb(IO_ICU2 + 1); 586 outb(IO_ICU1 + 1, 587 (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 588 outb(IO_ICU2 + 1, 589 (icu2_mask & ~(npx0_imask >> 8)) 590 | (old_icu2_mask & (npx0_imask >> 8))); 591 idt[npx_intrno] = save_idt_npxintr; 592 enable_intr(); /* back to usual state */ 593} 594 595#endif /* NNPX > 0 */ 596