npx.c revision 28359
1251018Sgonzo/*- 2251018Sgonzo * Copyright (c) 1990 William Jolitz. 3251018Sgonzo * Copyright (c) 1991 The Regents of the University of California. 4251018Sgonzo * All rights reserved. 5251018Sgonzo * 6251018Sgonzo * Redistribution and use in source and binary forms, with or without 7251018Sgonzo * modification, are permitted provided that the following conditions 8251018Sgonzo * are met: 9251018Sgonzo * 1. Redistributions of source code must retain the above copyright 10251018Sgonzo * notice, this list of conditions and the following disclaimer. 11251018Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12251018Sgonzo * notice, this list of conditions and the following disclaimer in the 13251018Sgonzo * documentation and/or other materials provided with the distribution. 14251018Sgonzo * 3. All advertising materials mentioning features or use of this software 15251018Sgonzo * must display the following acknowledgement: 16251018Sgonzo * This product includes software developed by the University of 17251018Sgonzo * California, Berkeley and its contributors. 18251018Sgonzo * 4. Neither the name of the University nor the names of its contributors 19251018Sgonzo * may be used to endorse or promote products derived from this software 20251018Sgonzo * without specific prior written permission. 21251018Sgonzo * 22251018Sgonzo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23251018Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24251018Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25251018Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26251018Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27251018Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28251018Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29251018Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30251018Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31251018Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32251018Sgonzo * SUCH DAMAGE. 33251018Sgonzo * 34251018Sgonzo * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 35251018Sgonzo * $Id: npx.c,v 1.50 1997/08/09 00:03:39 dyson Exp $ 36251018Sgonzo */ 37251018Sgonzo 38251018Sgonzo#include "npx.h" 39251018Sgonzo#if NNPX > 0 40251018Sgonzo 41251018Sgonzo#include "opt_cpu.h" 42251018Sgonzo#include "opt_math_emulate.h" 43251018Sgonzo 44251018Sgonzo#include <sys/param.h> 45252282Sgonzo#include <sys/systm.h> 46252282Sgonzo#include <sys/kernel.h> 47252282Sgonzo#include <sys/sysctl.h> 48252282Sgonzo#include <sys/conf.h> 49251018Sgonzo#include <sys/proc.h> 50251018Sgonzo#ifdef NPX_DEBUG 51251018Sgonzo#include <sys/syslog.h> 52251018Sgonzo#endif 53251018Sgonzo#include <sys/signalvar.h> 54251018Sgonzo 55251018Sgonzo#include <machine/asmacros.h> 56252282Sgonzo#include <machine/cpu.h> 57252282Sgonzo#include <machine/ipl.h> 58252282Sgonzo#include <machine/md_var.h> 59251018Sgonzo#include <machine/pcb.h> 60251018Sgonzo#include <machine/clock.h> 61251018Sgonzo#include <machine/specialreg.h> 62251018Sgonzo 63251018Sgonzo#include <i386/isa/icu.h> 64251018Sgonzo#include <i386/isa/isa_device.h> 65251018Sgonzo#include <i386/isa/intr_machdep.h> 66251018Sgonzo#include <i386/isa/isa.h> 67251018Sgonzo 68251018Sgonzo/* 69251018Sgonzo * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. 70251018Sgonzo */ 71251018Sgonzo 72251018Sgonzo/* Configuration flags. */ 73251018Sgonzo#define NPX_DISABLE_I586_OPTIMIZED_BCOPY (1 << 0) 74251018Sgonzo#define NPX_DISABLE_I586_OPTIMIZED_BZERO (1 << 1) 75251018Sgonzo#define NPX_DISABLE_I586_OPTIMIZED_COPYIO (1 << 2) 76251018Sgonzo 77251018Sgonzo/* XXX - should be in header file. */ 78251018Sgonzoextern void (*bcopy_vector) __P((const void *from, void *to, size_t len)); 79251018Sgonzoextern void (*ovbcopy_vector) __P((const void *from, void *to, size_t len)); 80251018Sgonzoextern int (*copyin_vector) __P((const void *udaddr, void *kaddr, size_t len)); 81251018Sgonzoextern int (*copyout_vector) __P((const void *kaddr, void *udaddr, size_t len)); 82251018Sgonzo 83251018Sgonzovoid i586_bcopy __P((const void *from, void *to, size_t len)); 84251018Sgonzovoid i586_bzero __P((void *buf, size_t len)); 85251018Sgonzoint i586_copyin __P((const void *udaddr, void *kaddr, size_t len)); 86251018Sgonzoint i586_copyout __P((const void *kaddr, void *udaddr, size_t len)); 87251018Sgonzo 88251018Sgonzo#ifdef __GNUC__ 89251018Sgonzo 90251018Sgonzo#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) 91251018Sgonzo#define fnclex() __asm("fnclex") 92251018Sgonzo#define fninit() __asm("fninit") 93251018Sgonzo#define fnop() __asm("fnop") 94251018Sgonzo#define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr))) 95251018Sgonzo#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr))) 96251018Sgonzo#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr))) 97251018Sgonzo#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop") 98251018Sgonzo#define frstor(addr) __asm("frstor %0" : : "m" (*(addr))) 99251018Sgonzo#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ 100251018Sgonzo : : "n" (CR0_TS) : "ax") 101251018Sgonzo#define stop_emulating() __asm("clts") 102251018Sgonzo 103251018Sgonzo#else /* not __GNUC__ */ 104251018Sgonzo 105251018Sgonzovoid fldcw __P((caddr_t addr)); 106251018Sgonzovoid fnclex __P((void)); 107251018Sgonzovoid fninit __P((void)); 108251018Sgonzovoid fnop __P((void)); 109251018Sgonzovoid fnsave __P((caddr_t addr)); 110251018Sgonzovoid fnstcw __P((caddr_t addr)); 111251018Sgonzovoid fnstsw __P((caddr_t addr)); 112251018Sgonzovoid fp_divide_by_0 __P((void)); 113251018Sgonzovoid frstor __P((caddr_t addr)); 114251018Sgonzovoid start_emulating __P((void)); 115251018Sgonzovoid stop_emulating __P((void)); 116251018Sgonzo 117251018Sgonzo#endif /* __GNUC__ */ 118251018Sgonzo 119251018Sgonzotypedef u_char bool_t; 120251018Sgonzo 121251018Sgonzostatic int npxattach __P((struct isa_device *dvp)); 122251018Sgonzostatic int npxprobe __P((struct isa_device *dvp)); 123251018Sgonzostatic int npxprobe1 __P((struct isa_device *dvp)); 124251018Sgonzo 125251018Sgonzostruct isa_driver npxdriver = { 126251018Sgonzo npxprobe, npxattach, "npx", 127251018Sgonzo}; 128251018Sgonzo 129251018Sgonzoint hw_float; /* XXX currently just alias for npx_exists */ 130251018Sgonzo 131251018SgonzoSYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, 132251018Sgonzo CTLFLAG_RD, &hw_float, 0, 133251018Sgonzo "Floatingpoint instructions executed in hardware"); 134251018Sgonzo 135251018Sgonzostatic u_int npx0_imask = SWI_CLOCK_MASK; 136251018Sgonzo 137251018Sgonzo#ifndef SMP /* XXX per-cpu on smp */ 138251018Sgonzostruct proc *npxproc; 139251018Sgonzo#endif 140251018Sgonzo 141251018Sgonzostatic bool_t npx_ex16; 142251018Sgonzostatic bool_t npx_exists; 143251018Sgonzostatic struct gate_descriptor npx_idt_probeintr; 144251018Sgonzostatic int npx_intrno; 145251018Sgonzostatic volatile u_int npx_intrs_while_probing; 146251018Sgonzostatic bool_t npx_irq13; 147251018Sgonzostatic volatile u_int npx_traps_while_probing; 148251018Sgonzo 149251018Sgonzo#ifndef SMP 150251018Sgonzo/* 151251018Sgonzo * Special interrupt handlers. Someday intr0-intr15 will be used to count 152251018Sgonzo * interrupts. We'll still need a special exception 16 handler. The busy 153277405Sgonzo * latch stuff in probeintr() can be moved to npxprobe(). 154251018Sgonzo */ 155251018Sgonzointhand_t probeintr; 156251018Sgonzo 157251018Sgonzoasm 158251018Sgonzo(" 159251018Sgonzo .text 160251018Sgonzo .p2align 2,0x90 161251018Sgonzo" __XSTRING(CNAME(probeintr)) ": 162251018Sgonzo ss 163251018Sgonzo incl " __XSTRING(CNAME(npx_intrs_while_probing)) " 164251018Sgonzo pushl %eax 165251018Sgonzo movb $0x20,%al # EOI (asm in strings loses cpp features) 166251018Sgonzo outb %al,$0xa0 # IO_ICU2 167251018Sgonzo outb %al,$0x20 # IO_ICU1 168251018Sgonzo movb $0,%al 169251018Sgonzo outb %al,$0xf0 # clear BUSY# latch 170251018Sgonzo popl %eax 171251018Sgonzo iret 172251018Sgonzo"); 173251018Sgonzo 174251018Sgonzointhand_t probetrap; 175251018Sgonzoasm 176251018Sgonzo(" 177251018Sgonzo .text 178251018Sgonzo .p2align 2,0x90 179251018Sgonzo" __XSTRING(CNAME(probetrap)) ": 180251018Sgonzo ss 181251018Sgonzo incl " __XSTRING(CNAME(npx_traps_while_probing)) " 182251018Sgonzo fnclex 183251018Sgonzo iret 184251018Sgonzo"); 185251018Sgonzo#endif /* SMP */ 186251018Sgonzo 187251018Sgonzo 188251018Sgonzo/* 189251018Sgonzo * Probe routine. Initialize cr0 to give correct behaviour for [f]wait 190251018Sgonzo * whether the device exists or not (XXX should be elsewhere). Set flags 191251018Sgonzo * to tell npxattach() what to do. Modify device struct if npx doesn't 192251018Sgonzo * need to use interrupts. Return 1 if device exists. 193251018Sgonzo */ 194251018Sgonzostatic int 195251018Sgonzonpxprobe(dvp) 196251018Sgonzo struct isa_device *dvp; 197251018Sgonzo{ 198251018Sgonzo#ifdef SMP 199251018Sgonzo 200251018Sgonzo return npxprobe1(dvp); 201251018Sgonzo 202251018Sgonzo#else /* SMP */ 203251018Sgonzo 204251018Sgonzo int result; 205251018Sgonzo u_long save_eflags; 206251018Sgonzo u_char save_icu1_mask; 207251018Sgonzo u_char save_icu2_mask; 208251018Sgonzo struct gate_descriptor save_idt_npxintr; 209251018Sgonzo struct gate_descriptor save_idt_npxtrap; 210251018Sgonzo /* 211251018Sgonzo * This routine is now just a wrapper for npxprobe1(), to install 212251018Sgonzo * special npx interrupt and trap handlers, to enable npx interrupts 213251018Sgonzo * and to disable other interrupts. Someday isa_configure() will 214251018Sgonzo * install suitable handlers and run with interrupts enabled so we 215251018Sgonzo * won't need to do so much here. 216251018Sgonzo */ 217251018Sgonzo npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; 218251018Sgonzo save_eflags = read_eflags(); 219251018Sgonzo disable_intr(); 220251018Sgonzo save_icu1_mask = inb(IO_ICU1 + 1); 221251018Sgonzo save_icu2_mask = inb(IO_ICU2 + 1); 222251018Sgonzo save_idt_npxintr = idt[npx_intrno]; 223251018Sgonzo save_idt_npxtrap = idt[16]; 224251018Sgonzo outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); 225251018Sgonzo outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); 226251018Sgonzo setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 227267171Skevlo setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 228251018Sgonzo npx_idt_probeintr = idt[npx_intrno]; 229251018Sgonzo enable_intr(); 230251018Sgonzo result = npxprobe1(dvp); 231251018Sgonzo disable_intr(); 232251018Sgonzo outb(IO_ICU1 + 1, save_icu1_mask); 233251018Sgonzo outb(IO_ICU2 + 1, save_icu2_mask); 234251018Sgonzo idt[npx_intrno] = save_idt_npxintr; 235251018Sgonzo idt[16] = save_idt_npxtrap; 236251018Sgonzo write_eflags(save_eflags); 237251018Sgonzo return (result); 238251018Sgonzo 239251018Sgonzo#endif /* SMP */ 240251018Sgonzo} 241251018Sgonzo 242251018Sgonzostatic int 243251018Sgonzonpxprobe1(dvp) 244251018Sgonzo struct isa_device *dvp; 245251018Sgonzo{ 246251018Sgonzo u_short control; 247251018Sgonzo u_short status; 248251018Sgonzo 249251018Sgonzo /* 250251018Sgonzo * Partially reset the coprocessor, if any. Some BIOS's don't reset 251251018Sgonzo * it after a warm boot. 252251018Sgonzo */ 253251018Sgonzo outb(0xf1, 0); /* full reset on some systems, NOP on others */ 254251018Sgonzo outb(0xf0, 0); /* clear BUSY# latch */ 255251018Sgonzo /* 256251018Sgonzo * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT 257251018Sgonzo * instructions. We must set the CR0_MP bit and use the CR0_TS 258251018Sgonzo * bit to control the trap, because setting the CR0_EM bit does 259251018Sgonzo * not cause WAIT instructions to trap. It's important to trap 260251018Sgonzo * WAIT instructions - otherwise the "wait" variants of no-wait 261251018Sgonzo * control instructions would degenerate to the "no-wait" variants 262251018Sgonzo * after FP context switches but work correctly otherwise. It's 263251018Sgonzo * particularly important to trap WAITs when there is no NPX - 264251018Sgonzo * otherwise the "wait" variants would always degenerate. 265251018Sgonzo * 266251018Sgonzo * Try setting CR0_NE to get correct error reporting on 486DX's. 267251018Sgonzo * Setting it should fail or do nothing on lesser processors. 268251018Sgonzo */ 269251018Sgonzo load_cr0(rcr0() | CR0_MP | CR0_NE); 270251018Sgonzo /* 271251018Sgonzo * But don't trap while we're probing. 272251018Sgonzo */ 273251018Sgonzo stop_emulating(); 274251018Sgonzo /* 275251018Sgonzo * Finish resetting the coprocessor, if any. If there is an error 276251018Sgonzo * pending, then we may get a bogus IRQ13, but probeintr() will handle 277251018Sgonzo * it OK. Bogus halts have never been observed, but we enabled 278251018Sgonzo * IRQ13 and cleared the BUSY# latch early to handle them anyway. 279251018Sgonzo */ 280251018Sgonzo fninit(); 281251018Sgonzo 282251018Sgonzo#ifdef SMP 283251018Sgonzo 284251018Sgonzo /* 285251018Sgonzo * Exception 16 MUST work for SMP. 286251018Sgonzo */ 287251018Sgonzo npx_irq13 = 0; 288251018Sgonzo npx_ex16 = hw_float = npx_exists = 1; 289251018Sgonzo dvp->id_irq = 0; /* zap the interrupt */ 290251018Sgonzo /* 291251018Sgonzo * special return value to flag that we do not 292251018Sgonzo * actually use any I/O registers 293251018Sgonzo */ 294251018Sgonzo return (-1); 295251018Sgonzo 296251018Sgonzo#else /* SMP */ 297251018Sgonzo 298251018Sgonzo /* 299251018Sgonzo * Don't use fwait here because it might hang. 300251018Sgonzo * Don't use fnop here because it usually hangs if there is no FPU. 301251018Sgonzo */ 302251018Sgonzo DELAY(1000); /* wait for any IRQ13 */ 303251018Sgonzo#ifdef DIAGNOSTIC 304251018Sgonzo if (npx_intrs_while_probing != 0) 305251018Sgonzo printf("fninit caused %u bogus npx interrupt(s)\n", 306251018Sgonzo npx_intrs_while_probing); 307251018Sgonzo if (npx_traps_while_probing != 0) 308251018Sgonzo printf("fninit caused %u bogus npx trap(s)\n", 309251018Sgonzo npx_traps_while_probing); 310251018Sgonzo#endif 311251018Sgonzo /* 312251018Sgonzo * Check for a status of mostly zero. 313251018Sgonzo */ 314251018Sgonzo status = 0x5a5a; 315251018Sgonzo fnstsw(&status); 316251018Sgonzo if ((status & 0xb8ff) == 0) { 317251018Sgonzo /* 318251018Sgonzo * Good, now check for a proper control word. 319251018Sgonzo */ 320251018Sgonzo control = 0x5a5a; 321251018Sgonzo fnstcw(&control); 322251018Sgonzo if ((control & 0x1f3f) == 0x033f) { 323251018Sgonzo hw_float = npx_exists = 1; 324251018Sgonzo /* 325251018Sgonzo * We have an npx, now divide by 0 to see if exception 326251018Sgonzo * 16 works. 327251018Sgonzo */ 328251018Sgonzo control &= ~(1 << 2); /* enable divide by 0 trap */ 329251018Sgonzo fldcw(&control); 330251018Sgonzo npx_traps_while_probing = npx_intrs_while_probing = 0; 331251018Sgonzo fp_divide_by_0(); 332251018Sgonzo if (npx_traps_while_probing != 0) { 333251018Sgonzo /* 334251018Sgonzo * Good, exception 16 works. 335251018Sgonzo */ 336251018Sgonzo npx_ex16 = 1; 337251018Sgonzo dvp->id_irq = 0; /* zap the interrupt */ 338251018Sgonzo /* 339251018Sgonzo * special return value to flag that we do not 340251018Sgonzo * actually use any I/O registers 341251018Sgonzo */ 342251018Sgonzo return (-1); 343251018Sgonzo } 344251018Sgonzo if (npx_intrs_while_probing != 0) { 345251018Sgonzo /* 346251018Sgonzo * Bad, we are stuck with IRQ13. 347251018Sgonzo */ 348251018Sgonzo npx_irq13 = 1; 349251018Sgonzo /* 350251018Sgonzo * npxattach would be too late to set npx0_imask. 351251018Sgonzo */ 352251018Sgonzo npx0_imask |= dvp->id_irq; 353251018Sgonzo return (IO_NPXSIZE); 354251018Sgonzo } 355251018Sgonzo /* 356251018Sgonzo * Worse, even IRQ13 is broken. Use emulator. 357251018Sgonzo */ 358251018Sgonzo } 359251018Sgonzo } 360251018Sgonzo /* 361251018Sgonzo * Probe failed, but we want to get to npxattach to initialize the 362251018Sgonzo * emulator and say that it has been installed. XXX handle devices 363251018Sgonzo * that aren't really devices better. 364251018Sgonzo */ 365251018Sgonzo dvp->id_irq = 0; 366251018Sgonzo /* 367251018Sgonzo * special return value to flag that we do not 368251018Sgonzo * actually use any I/O registers 369251018Sgonzo */ 370251018Sgonzo return (-1); 371251018Sgonzo 372251018Sgonzo#endif /* SMP */ 373251018Sgonzo} 374251018Sgonzo 375251018Sgonzo/* 376251018Sgonzo * Attach routine - announce which it is, and wire into system 377251018Sgonzo */ 378251018Sgonzoint 379251018Sgonzonpxattach(dvp) 380251018Sgonzo struct isa_device *dvp; 381251018Sgonzo{ 382251018Sgonzo /* The caller has printed "irq 13" for the npx_irq13 case. */ 383251018Sgonzo if (!npx_irq13) { 384251018Sgonzo printf("npx%d: ", dvp->id_unit); 385251018Sgonzo if (npx_ex16) 386251018Sgonzo printf("INT 16 interface\n"); 387251018Sgonzo#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE) 388251018Sgonzo else if (npx_exists) { 389251018Sgonzo printf("error reporting broken; using 387 emulator\n"); 390251018Sgonzo hw_float = npx_exists = 0; 391251018Sgonzo } else 392251018Sgonzo printf("387 emulator\n"); 393251018Sgonzo#else 394251018Sgonzo else 395251018Sgonzo printf("no 387 emulator in kernel!\n"); 396251018Sgonzo#endif 397251018Sgonzo } 398251018Sgonzo npxinit(__INITIAL_NPXCW__); 399251018Sgonzo 400251018Sgonzo#if defined(I586_CPU) 401277405Sgonzo if (cpu_class == CPUCLASS_586 && npx_ex16) { 402277405Sgonzo if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) { 403251018Sgonzo bcopy_vector = i586_bcopy; 404251018Sgonzo ovbcopy_vector = i586_bcopy; 405251018Sgonzo } 406251018Sgonzo if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_BZERO)) 407251018Sgonzo bzero = i586_bzero; 408252282Sgonzo if (!(dvp->id_flags & NPX_DISABLE_I586_OPTIMIZED_COPYIO)) { 409252282Sgonzo copyin_vector = i586_copyin; 410261410Sian copyout_vector = i586_copyout; 411261410Sian } 412261410Sian } 413251018Sgonzo#endif 414251018Sgonzo 415251018Sgonzo return (1); /* XXX unused */ 416251018Sgonzo} 417251018Sgonzo 418252282Sgonzo/* 419252282Sgonzo * Initialize floating point unit. 420252282Sgonzo */ 421252282Sgonzovoid 422252282Sgonzonpxinit(control) 423252282Sgonzo u_short control; 424251018Sgonzo{ 425251018Sgonzo struct save87 dummy; 426251018Sgonzo 427251018Sgonzo if (!npx_exists) 428251018Sgonzo return; 429251018Sgonzo /* 430251018Sgonzo * fninit has the same h/w bugs as fnsave. Use the detoxified 431251018Sgonzo * fnsave to throw away any junk in the fpu. npxsave() initializes 432251018Sgonzo * the fpu and sets npxproc = NULL as important side effects. 433251018Sgonzo */ 434251018Sgonzo npxsave(&dummy); 435251018Sgonzo stop_emulating(); 436251018Sgonzo fldcw(&control); 437251018Sgonzo if (curpcb != NULL) 438251018Sgonzo fnsave(&curpcb->pcb_savefpu); 439277313Sgonzo start_emulating(); 440277313Sgonzo} 441277313Sgonzo 442251018Sgonzo/* 443251018Sgonzo * Free coprocessor (if we have it). 444251018Sgonzo */ 445251018Sgonzovoid 446251018Sgonzonpxexit(p) 447251018Sgonzo struct proc *p; 448251018Sgonzo{ 449251018Sgonzo 450251018Sgonzo if (p == npxproc) 451251018Sgonzo npxsave(&curpcb->pcb_savefpu); 452251018Sgonzo#ifdef NPX_DEBUG 453251018Sgonzo if (npx_exists) { 454251018Sgonzo u_int masked_exceptions; 455251018Sgonzo 456251018Sgonzo masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw 457251018Sgonzo & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f; 458251018Sgonzo /* 459251018Sgonzo * Log exceptions that would have trapped with the old 460251018Sgonzo * control word (overflow, divide by 0, and invalid operand). 461251018Sgonzo */ 462251018Sgonzo if (masked_exceptions & 0x0d) 463251018Sgonzo log(LOG_ERR, 464251018Sgonzo "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", 465251018Sgonzo p->p_pid, p->p_comm, masked_exceptions); 466251018Sgonzo } 467251018Sgonzo#endif 468251018Sgonzo} 469251018Sgonzo 470251018Sgonzo/* 471251018Sgonzo * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE. 472251018Sgonzo * 473251018Sgonzo * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now 474251018Sgonzo * depend on longjmp() restoring a usable state. Restoring the state 475251018Sgonzo * or examining it might fail if we didn't clear exceptions. 476251018Sgonzo * 477251018Sgonzo * XXX there is no standard way to tell SIGFPE handlers about the error 478251018Sgonzo * state. The old interface: 479251018Sgonzo * 480251018Sgonzo * void handler(int sig, int code, struct sigcontext *scp); 481251018Sgonzo * 482251018Sgonzo * is broken because it is non-ANSI and because the FP state is not in 483251018Sgonzo * struct sigcontext. 484251018Sgonzo * 485251018Sgonzo * XXX the FP state is not preserved across signal handlers. So signal 486251018Sgonzo * handlers cannot afford to do FP unless they preserve the state or 487251018Sgonzo * longjmp() out. Both preserving the state and longjmp()ing may be 488251018Sgonzo * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable 489251018Sgonzo * solution for signals other than SIGFPE. 490251018Sgonzo */ 491251018Sgonzovoid 492251018Sgonzonpxintr(unit) 493251018Sgonzo int unit; 494251018Sgonzo{ 495251018Sgonzo int code; 496251018Sgonzo struct intrframe *frame; 497251018Sgonzo 498251018Sgonzo if (npxproc == NULL || !npx_exists) { 499251018Sgonzo printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 500251018Sgonzo npxproc, curproc, npx_exists); 501251018Sgonzo panic("npxintr from nowhere"); 502251018Sgonzo } 503251018Sgonzo if (npxproc != curproc) { 504251018Sgonzo printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n", 505251018Sgonzo npxproc, curproc, npx_exists); 506252282Sgonzo panic("npxintr from non-current process"); 507251018Sgonzo } 508251018Sgonzo 509251018Sgonzo outb(0xf0, 0); 510251018Sgonzo fnstsw(&curpcb->pcb_savefpu.sv_ex_sw); 511251018Sgonzo fnclex(); 512251018Sgonzo fnop(); 513251018Sgonzo 514251018Sgonzo /* 515251018Sgonzo * Pass exception to process. 516251018Sgonzo */ 517251018Sgonzo frame = (struct intrframe *)&unit; /* XXX */ 518251018Sgonzo if (CS_SECURE(frame->if_cs) || (frame->if_eflags & PSL_VM)) { 519251018Sgonzo /* 520251018Sgonzo * Interrupt is essentially a trap, so we can afford to call 521251018Sgonzo * the SIGFPE handler (if any) as soon as the interrupt 522251018Sgonzo * returns. 523251018Sgonzo * 524251018Sgonzo * XXX little or nothing is gained from this, and plenty is 525251018Sgonzo * lost - the interrupt frame has to contain the trap frame 526251018Sgonzo * (this is otherwise only necessary for the rescheduling trap 527251018Sgonzo * in doreti, and the frame for that could easily be set up 528251018Sgonzo * just before it is used). 529251018Sgonzo */ 530251018Sgonzo curproc->p_md.md_regs = (struct trapframe *)&frame->if_es; 531251018Sgonzo#ifdef notyet 532251018Sgonzo /* 533251018Sgonzo * Encode the appropriate code for detailed information on 534251018Sgonzo * this exception. 535251018Sgonzo */ 536277313Sgonzo code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); 537277313Sgonzo#else 538277313Sgonzo code = 0; /* XXX */ 539277313Sgonzo#endif 540277313Sgonzo trapsignal(curproc, SIGFPE, code); 541277313Sgonzo } else { 542277313Sgonzo /* 543277313Sgonzo * Nested interrupt. These losers occur when: 544277313Sgonzo * o an IRQ13 is bogusly generated at a bogus time, e.g.: 545277313Sgonzo * o immediately after an fnsave or frstor of an 546277313Sgonzo * error state. 547251018Sgonzo * o a couple of 386 instructions after 548277313Sgonzo * "fstpl _memvar" causes a stack overflow. 549277313Sgonzo * These are especially nasty when combined with a 550251018Sgonzo * trace trap. 551277313Sgonzo * o an IRQ13 occurs at the same time as another higher- 552277313Sgonzo * priority interrupt. 553251018Sgonzo * 554277313Sgonzo * Treat them like a true async interrupt. 555277313Sgonzo */ 556251018Sgonzo psignal(curproc, SIGFPE); 557251018Sgonzo } 558277313Sgonzo} 559277313Sgonzo 560277313Sgonzo/* 561251018Sgonzo * Implement device not available (DNA) exception 562251018Sgonzo * 563277313Sgonzo * It would be better to switch FP context here (if curproc != npxproc) 564251018Sgonzo * and not necessarily for every context switch, but it is too hard to 565277313Sgonzo * access foreign pcb's. 566251018Sgonzo */ 567251018Sgonzoint 568251018Sgonzonpxdna() 569277313Sgonzo{ 570251018Sgonzo if (!npx_exists) 571277313Sgonzo return (0); 572251018Sgonzo if (npxproc != NULL) { 573251018Sgonzo printf("npxdna: npxproc = %p, curproc = %p\n", 574251018Sgonzo npxproc, curproc); 575251018Sgonzo panic("npxdna"); 576251018Sgonzo } 577251018Sgonzo stop_emulating(); 578251018Sgonzo /* 579251018Sgonzo * Record new context early in case frstor causes an IRQ13. 580251018Sgonzo */ 581251018Sgonzo npxproc = curproc; 582251018Sgonzo curpcb->pcb_savefpu.sv_ex_sw = 0; 583251018Sgonzo /* 584251018Sgonzo * The following frstor may cause an IRQ13 when the state being 585251018Sgonzo * restored has a pending error. The error will appear to have been 586251018Sgonzo * triggered by the current (npx) user instruction even when that 587251018Sgonzo * instruction is a no-wait instruction that should not trigger an 588251018Sgonzo * error (e.g., fnclex). On at least one 486 system all of the 589251018Sgonzo * no-wait instructions are broken the same as frstor, so our 590251018Sgonzo * treatment does not amplify the breakage. On at least one 591251018Sgonzo * 386/Cyrix 387 system, fnclex works correctly while frstor and 592251018Sgonzo * fnsave are broken, so our treatment breaks fnclex if it is the 593251018Sgonzo * first FPU instruction after a context switch. 594251018Sgonzo */ 595251018Sgonzo frstor(&curpcb->pcb_savefpu); 596251018Sgonzo 597251018Sgonzo return (1); 598251018Sgonzo} 599251018Sgonzo 600251018Sgonzo/* 601251018Sgonzo * Wrapper for fnsave instruction to handle h/w bugs. If there is an error 602251018Sgonzo * pending, then fnsave generates a bogus IRQ13 on some systems. Force 603251018Sgonzo * any IRQ13 to be handled immediately, and then ignore it. This routine is 604251018Sgonzo * often called at splhigh so it must not use many system services. In 605251018Sgonzo * particular, it's much easier to install a special handler than to 606251018Sgonzo * guarantee that it's safe to use npxintr() and its supporting code. 607251018Sgonzo */ 608251018Sgonzovoid 609251018Sgonzonpxsave(addr) 610251018Sgonzo struct save87 *addr; 611251018Sgonzo{ 612251018Sgonzo#ifdef SMP 613251018Sgonzo 614251018Sgonzo stop_emulating(); 615251018Sgonzo fnsave(addr); 616251018Sgonzo /* fnop(); */ 617251018Sgonzo start_emulating(); 618251018Sgonzo npxproc = NULL; 619251018Sgonzo 620251018Sgonzo#else /* SMP */ 621251018Sgonzo 622251018Sgonzo u_char icu1_mask; 623251018Sgonzo u_char icu2_mask; 624251018Sgonzo u_char old_icu1_mask; 625251018Sgonzo u_char old_icu2_mask; 626251018Sgonzo struct gate_descriptor save_idt_npxintr; 627251018Sgonzo 628251018Sgonzo disable_intr(); 629251018Sgonzo old_icu1_mask = inb(IO_ICU1 + 1); 630251018Sgonzo old_icu2_mask = inb(IO_ICU2 + 1); 631251018Sgonzo save_idt_npxintr = idt[npx_intrno]; 632251018Sgonzo outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); 633251018Sgonzo outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); 634251018Sgonzo idt[npx_intrno] = npx_idt_probeintr; 635251018Sgonzo enable_intr(); 636251018Sgonzo stop_emulating(); 637251018Sgonzo fnsave(addr); 638251018Sgonzo fnop(); 639251018Sgonzo start_emulating(); 640251018Sgonzo npxproc = NULL; 641251018Sgonzo disable_intr(); 642251018Sgonzo icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ 643251018Sgonzo icu2_mask = inb(IO_ICU2 + 1); 644251018Sgonzo outb(IO_ICU1 + 1, 645251018Sgonzo (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); 646251018Sgonzo outb(IO_ICU2 + 1, 647251018Sgonzo (icu2_mask & ~(npx0_imask >> 8)) 648251018Sgonzo | (old_icu2_mask & (npx0_imask >> 8))); 649251018Sgonzo idt[npx_intrno] = save_idt_npxintr; 650251018Sgonzo enable_intr(); /* back to usual state */ 651251018Sgonzo 652251018Sgonzo#endif /* SMP */ 653251018Sgonzo} 654251018Sgonzo 655251018Sgonzo#endif /* NNPX > 0 */ 656251018Sgonzo