Annotation of sys/arch/i386/isa/npx.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: npx.c,v 1.42 2006/10/18 19:48:32 tom Exp $ */
! 2: /* $NetBSD: npx.c,v 1.57 1996/05/12 23:12:24 mycroft Exp $ */
! 3:
! 4: #if 0
! 5: #define IPRINTF(x) printf x
! 6: #else
! 7: #define IPRINTF(x)
! 8: #endif
! 9:
! 10: /*-
! 11: * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
! 12: * Copyright (c) 1990 William Jolitz.
! 13: * Copyright (c) 1991 The Regents of the University of California.
! 14: * All rights reserved.
! 15: *
! 16: * Redistribution and use in source and binary forms, with or without
! 17: * modification, are permitted provided that the following conditions
! 18: * are met:
! 19: * 1. Redistributions of source code must retain the above copyright
! 20: * notice, this list of conditions and the following disclaimer.
! 21: * 2. Redistributions in binary form must reproduce the above copyright
! 22: * notice, this list of conditions and the following disclaimer in the
! 23: * documentation and/or other materials provided with the distribution.
! 24: * 3. Neither the name of the University nor the names of its contributors
! 25: * may be used to endorse or promote products derived from this software
! 26: * without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 29: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 30: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 31: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 32: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 33: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 34: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 35: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 36: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 37: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 38: * SUCH DAMAGE.
! 39: *
! 40: * @(#)npx.c 7.2 (Berkeley) 5/12/91
! 41: */
! 42:
! 43: #include <sys/param.h>
! 44: #include <sys/systm.h>
! 45: #include <sys/conf.h>
! 46: #include <sys/file.h>
! 47: #include <sys/proc.h>
! 48: #include <sys/signalvar.h>
! 49: #include <sys/user.h>
! 50: #include <sys/ioctl.h>
! 51: #include <sys/device.h>
! 52:
! 53: #include <uvm/uvm_extern.h>
! 54:
! 55: #include <machine/cpu.h>
! 56: #include <machine/intr.h>
! 57: #include <machine/npx.h>
! 58: #include <machine/pio.h>
! 59: #include <machine/cpufunc.h>
! 60: #include <machine/pcb.h>
! 61: #include <machine/trap.h>
! 62: #include <machine/specialreg.h>
! 63: #include <machine/i8259.h>
! 64:
! 65: #include <dev/isa/isareg.h>
! 66: #include <dev/isa/isavar.h>
! 67:
! 68: /*
! 69: * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
! 70: *
! 71: * We do lazy initialization and switching using the TS bit in cr0 and the
! 72: * MDP_USEDFPU bit in mdproc.
! 73: *
! 74: * DNA exceptions are handled like this:
! 75: *
! 76: * 1) If there is no NPX, return and go to the emulator.
! 77: * 2) If someone else has used the NPX, save its state into that process's PCB.
! 78: * 3a) If MDP_USEDFPU is not set, set it and initialize the NPX.
! 79: * 3b) Otherwise, reload the process's previous NPX state.
! 80: *
! 81: * When a process is created or exec()s, its saved cr0 image has the TS bit
! 82: * set and the MDP_USEDFPU bit clear. The MDP_USEDFPU bit is set when the
! 83: * process first gets a DNA and the NPX is initialized. The TS bit is turned
! 84: * off when the NPX is used, and turned on again later when the process's NPX
! 85: * state is saved.
! 86: */
! 87:
! 88: #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
! 89: #define fnclex() __asm("fnclex")
! 90: #define fninit() __asm("fninit")
! 91: #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr))
! 92: #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr))
! 93: #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr))
! 94: #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
! 95: #define frstor(addr) __asm("frstor %0" : : "m" (*addr))
! 96: #define fwait() __asm("fwait")
! 97: #define clts() __asm("clts")
! 98: #define stts() lcr0(rcr0() | CR0_TS)
! 99:
! 100: int npxintr(void *);
! 101: static int npxprobe1(struct isa_attach_args *);
! 102: static int x86fpflags_to_siginfo(u_int32_t);
! 103:
! 104:
! 105: struct npx_softc {
! 106: struct device sc_dev;
! 107: void *sc_ih;
! 108: };
! 109:
! 110: int npxprobe(struct device *, void *, void *);
! 111: void npxattach(struct device *, struct device *, void *);
! 112:
! 113: struct cfattach npx_ca = {
! 114: sizeof(struct npx_softc), npxprobe, npxattach
! 115: };
! 116:
! 117: struct cfdriver npx_cd = {
! 118: NULL, "npx", DV_DULL
! 119: };
! 120:
! 121: enum npx_type {
! 122: NPX_NONE = 0,
! 123: NPX_INTERRUPT,
! 124: NPX_EXCEPTION,
! 125: NPX_BROKEN,
! 126: NPX_CPUID,
! 127: };
! 128:
! 129: static enum npx_type npx_type;
! 130: static volatile u_int npx_intrs_while_probing;
! 131: static volatile u_int npx_traps_while_probing;
! 132:
! 133: extern int i386_fpu_present;
! 134: extern int i386_fpu_exception;
! 135: extern int i386_fpu_fdivbug;
! 136:
! 137: #ifdef I686_CPU
! 138: #define fxsave(addr) __asm("fxsave %0" : "=m" (*addr))
! 139: #define fxrstor(addr) __asm("fxrstor %0" : : "m" (*addr))
! 140: #endif /* I686_CPU */
! 141:
! 142: static __inline void
! 143: fpu_save(union savefpu *addr)
! 144: {
! 145:
! 146: #ifdef I686_CPU
! 147: if (i386_use_fxsave) {
! 148: fxsave(&addr->sv_xmm);
! 149: /* FXSAVE doesn't FNINIT like FNSAVE does -- so do it here. */
! 150: fninit();
! 151: } else
! 152: #endif /* I686_CPU */
! 153: fnsave(&addr->sv_87);
! 154: }
! 155:
! 156: static int
! 157: npxdna_notset(struct cpu_info *ci)
! 158: {
! 159: panic("npxdna vector not initialized");
! 160: }
! 161:
! 162: int (*npxdna_func)(struct cpu_info *) = npxdna_notset;
! 163: int npxdna_s87(struct cpu_info *);
! 164: #ifdef I686_CPU
! 165: int npxdna_xmm(struct cpu_info *);
! 166: #endif /* I686_CPU */
! 167: void npxexit(void);
! 168:
! 169: /*
! 170: * Special interrupt handlers. Someday intr0-intr15 will be used to count
! 171: * interrupts. We'll still need a special exception 16 handler. The busy
! 172: * latch stuff in probintr() can be moved to npxprobe().
! 173: */
! 174: void probeintr(void);
! 175: asm (".text\n\t"
! 176: "probeintr:\n\t"
! 177: "ss\n\t"
! 178: "incl npx_intrs_while_probing\n\t"
! 179: "pushl %eax\n\t"
! 180: "movb $0x20,%al # EOI (asm in strings loses cpp features)\n\t"
! 181: "outb %al,$0xa0 # IO_ICU2\n\t"
! 182: "outb %al,$0x20 # IO_ICU1\n\t"
! 183: "movb $0,%al\n\t"
! 184: "outb %al,$0xf0 # clear BUSY# latch\n\t"
! 185: "popl %eax\n\t"
! 186: "iret\n\t");
! 187:
! 188: void probetrap(void);
! 189: asm (".text\n\t"
! 190: "probetrap:\n\t"
! 191: "ss\n\t"
! 192: "incl npx_traps_while_probing\n\t"
! 193: "fnclex\n\t"
! 194: "iret\n\t");
! 195:
! 196: static inline int
! 197: npxprobe1(struct isa_attach_args *ia)
! 198: {
! 199: int control;
! 200: int status;
! 201:
! 202: ia->ia_iosize = 16;
! 203: ia->ia_msize = 0;
! 204:
! 205: /*
! 206: * Finish resetting the coprocessor, if any. If there is an error
! 207: * pending, then we may get a bogus IRQ13, but probeintr() will handle
! 208: * it OK. Bogus halts have never been observed, but we enabled
! 209: * IRQ13 and cleared the BUSY# latch early to handle them anyway.
! 210: */
! 211: fninit();
! 212: delay(1000); /* wait for any IRQ13 (fwait might hang) */
! 213:
! 214: /*
! 215: * Check for a status of mostly zero.
! 216: */
! 217: status = 0x5a5a;
! 218: fnstsw(&status);
! 219: if ((status & 0xb8ff) == 0) {
! 220: /*
! 221: * Good, now check for a proper control word.
! 222: */
! 223: control = 0x5a5a;
! 224: fnstcw(&control);
! 225: if ((control & 0x1f3f) == 0x033f) {
! 226: /*
! 227: * We have an npx, now divide by 0 to see if exception
! 228: * 16 works.
! 229: */
! 230: control &= ~(1 << 2); /* enable divide by 0 trap */
! 231: fldcw(&control);
! 232: npx_traps_while_probing = npx_intrs_while_probing = 0;
! 233: fp_divide_by_0();
! 234: delay(1);
! 235: if (npx_traps_while_probing != 0) {
! 236: /*
! 237: * Good, exception 16 works.
! 238: */
! 239: npx_type = NPX_EXCEPTION;
! 240: ia->ia_irq = IRQUNK; /* zap the interrupt */
! 241: i386_fpu_exception = 1;
! 242: } else if (npx_intrs_while_probing != 0) {
! 243: /*
! 244: * Bad, we are stuck with IRQ13.
! 245: */
! 246: npx_type = NPX_INTERRUPT;
! 247: } else {
! 248: /*
! 249: * Worse, even IRQ13 is broken.
! 250: */
! 251: npx_type = NPX_BROKEN;
! 252: ia->ia_irq = IRQUNK;
! 253: }
! 254: return 1;
! 255: }
! 256: }
! 257:
! 258: /*
! 259: * Probe failed. There is no usable FPU.
! 260: */
! 261: npx_type = NPX_NONE;
! 262: return 0;
! 263: }
! 264:
! 265: /*
! 266: * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
! 267: * whether the device exists or not (XXX should be elsewhere). Set flags
! 268: * to tell npxattach() what to do. Modify device struct if npx doesn't
! 269: * need to use interrupts. Return 1 if device exists.
! 270: */
! 271: int
! 272: npxprobe(struct device *parent, void *match, void *aux)
! 273: {
! 274: struct isa_attach_args *ia = aux;
! 275: int irq;
! 276: int result;
! 277: u_long save_eflags;
! 278: unsigned save_imen;
! 279: struct gate_descriptor save_idt_npxintr;
! 280: struct gate_descriptor save_idt_npxtrap;
! 281:
! 282: if (cpu_feature & CPUID_FPU) {
! 283: npx_type = NPX_CPUID;
! 284: i386_fpu_exception = 1;
! 285: ia->ia_irq = IRQUNK; /* Don't want the interrupt vector */
! 286: ia->ia_iosize = 16;
! 287: ia->ia_msize = 0;
! 288: return 1;
! 289: }
! 290:
! 291: /*
! 292: * This routine is now just a wrapper for npxprobe1(), to install
! 293: * special npx interrupt and trap handlers, to enable npx interrupts
! 294: * and to disable other interrupts. Someday isa_configure() will
! 295: * install suitable handlers and run with interrupts enabled so we
! 296: * won't need to do so much here.
! 297: */
! 298: irq = NRSVIDT + ia->ia_irq;
! 299: save_eflags = read_eflags();
! 300: disable_intr();
! 301: save_idt_npxintr = idt[irq];
! 302: save_idt_npxtrap = idt[16];
! 303: setgate(&idt[irq], probeintr, 0, SDT_SYS386IGT, SEL_KPL, GICODE_SEL);
! 304: setgate(&idt[16], probetrap, 0, SDT_SYS386TGT, SEL_KPL, GCODE_SEL);
! 305: save_imen = imen;
! 306: imen = ~((1 << IRQ_SLAVE) | (1 << ia->ia_irq));
! 307: SET_ICUS();
! 308:
! 309: /*
! 310: * Partially reset the coprocessor, if any. Some BIOS's don't reset
! 311: * it after a warm boot.
! 312: */
! 313: outb(0xf1, 0); /* full reset on some systems, NOP on others */
! 314: delay(1000);
! 315: outb(0xf0, 0); /* clear BUSY# latch */
! 316:
! 317: /*
! 318: * We set CR0 in locore to trap all ESC and WAIT instructions.
! 319: * We have to turn off the CR0_EM bit temporarily while probing.
! 320: */
! 321: lcr0(rcr0() & ~(CR0_EM|CR0_TS));
! 322: enable_intr();
! 323: result = npxprobe1(ia);
! 324: disable_intr();
! 325: lcr0(rcr0() | (CR0_EM|CR0_TS));
! 326:
! 327: imen = save_imen;
! 328: SET_ICUS();
! 329: idt[irq] = save_idt_npxintr;
! 330: idt[16] = save_idt_npxtrap;
! 331: write_eflags(save_eflags);
! 332: return (result);
! 333: }
! 334:
! 335: int npx586bug1(int, int);
! 336: asm (".text\n\t"
! 337: "npx586bug1:\n\t"
! 338: "fildl 4(%esp) # x\n\t"
! 339: "fildl 8(%esp) # y\n\t"
! 340: "fld %st(1)\n\t"
! 341: "fdiv %st(1),%st # x/y\n\t"
! 342: "fmulp %st,%st(1) # (x/y)*y\n\t"
! 343: "fsubrp %st,%st(1) # x-(x/y)*y\n\t"
! 344: "pushl $0\n\t"
! 345: "fistpl (%esp)\n\t"
! 346: "popl %eax\n\t"
! 347: "ret\n\t");
! 348:
! 349: void
! 350: npxinit(struct cpu_info *ci)
! 351: {
! 352: lcr0(rcr0() & ~(CR0_EM|CR0_TS));
! 353: fninit();
! 354: if (npx586bug1(4195835, 3145727) != 0) {
! 355: i386_fpu_fdivbug = 1;
! 356: printf("%s: WARNING: Pentium FDIV bug detected!\n",
! 357: ci->ci_dev.dv_xname);
! 358: }
! 359: lcr0(rcr0() | (CR0_TS));
! 360: }
! 361:
! 362: /*
! 363: * Attach routine - announce which it is, and wire into system
! 364: */
! 365: void
! 366: npxattach(struct device *parent, struct device *self, void *aux)
! 367: {
! 368: struct npx_softc *sc = (void *)self;
! 369: struct isa_attach_args *ia = aux;
! 370:
! 371: switch (npx_type) {
! 372: case NPX_INTERRUPT:
! 373: printf("\n");
! 374: lcr0(rcr0() & ~CR0_NE);
! 375: sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq,
! 376: IST_EDGE, IPL_NONE, npxintr, 0, sc->sc_dev.dv_xname);
! 377: break;
! 378: case NPX_EXCEPTION:
! 379: printf(": using exception 16\n");
! 380: break;
! 381: case NPX_CPUID:
! 382: printf(": reported by CPUID; using exception 16\n");
! 383: npx_type = NPX_EXCEPTION;
! 384: break;
! 385: case NPX_BROKEN:
! 386: printf(": error reporting broken; not using\n");
! 387: npx_type = NPX_NONE;
! 388: return;
! 389: case NPX_NONE:
! 390: return;
! 391: }
! 392:
! 393: npxinit(&cpu_info_primary);
! 394: i386_fpu_present = 1;
! 395:
! 396: #ifdef I686_CPU
! 397: if (i386_use_fxsave)
! 398: npxdna_func = npxdna_xmm;
! 399: else
! 400: #endif /* I686_CPU */
! 401: npxdna_func = npxdna_s87;
! 402: }
! 403:
! 404: /*
! 405: * Record the FPU state and reinitialize it all except for the control word.
! 406: * Then generate a SIGFPE.
! 407: *
! 408: * Reinitializing the state allows naive SIGFPE handlers to longjmp without
! 409: * doing any fixups.
! 410: *
! 411: * XXX there is currently no way to pass the full error state to signal
! 412: * handlers, and if this is a nested interrupt there is no way to pass even
! 413: * a status code! So there is no way to have a non-naive SIGFPE handler. At
! 414: * best a handler could do an fninit followed by an fldcw of a static value.
! 415: * fnclex would be of little use because it would leave junk on the FPU stack.
! 416: * Returning from the handler would be even less safe than usual because
! 417: * IRQ13 exception handling makes exceptions even less precise than usual.
! 418: */
! 419: int
! 420: npxintr(void *arg)
! 421: {
! 422: struct cpu_info *ci = curcpu();
! 423: struct proc *p = ci->ci_fpcurproc;
! 424: union savefpu *addr;
! 425: struct intrframe *frame = arg;
! 426: int code;
! 427: union sigval sv;
! 428:
! 429: uvmexp.traps++;
! 430: IPRINTF(("%s: fp intr\n", ci->ci_dev.dv_xname));
! 431:
! 432: if (p == NULL || npx_type == NPX_NONE) {
! 433: /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
! 434: printf("npxintr: p = %lx, curproc = %lx, npx_type = %d\n",
! 435: (u_long) p, (u_long) curproc, npx_type);
! 436: panic("npxintr from nowhere");
! 437: }
! 438: /*
! 439: * Clear the interrupt latch.
! 440: */
! 441: outb(0xf0, 0);
! 442: /*
! 443: * If we're saving, ignore the interrupt. The FPU will happily
! 444: * generate another one when we restore the state later.
! 445: */
! 446: if (ci->ci_fpsaving)
! 447: return (1);
! 448:
! 449: #ifdef DIAGNOSTIC
! 450: /*
! 451: * At this point, fpcurproc should be curproc. If it wasn't, the TS
! 452: * bit should be set, and we should have gotten a DNA exception.
! 453: */
! 454: if (p != curproc)
! 455: panic("npxintr: wrong process");
! 456: #endif
! 457:
! 458: /*
! 459: * Find the address of fpcurproc's saved FPU state. (Given the
! 460: * invariant above, this is always the one in curpcb.)
! 461: */
! 462: addr = &p->p_addr->u_pcb.pcb_savefpu;
! 463: /*
! 464: * Save state. This does an implied fninit. It had better not halt
! 465: * the cpu or we'll hang.
! 466: */
! 467: fpu_save(addr);
! 468: fwait();
! 469: /*
! 470: * Restore control word (was clobbered by fpu_save).
! 471: */
! 472: if (i386_use_fxsave) {
! 473: fldcw(&addr->sv_xmm.sv_env.en_cw);
! 474: /*
! 475: * FNINIT doesn't affect MXCSR or the XMM registers;
! 476: * no need to re-load MXCSR here.
! 477: */
! 478: } else
! 479: fldcw(&addr->sv_87.sv_env.en_cw);
! 480: fwait();
! 481: /*
! 482: * Remember the exception status word and tag word. The current
! 483: * (almost fninit'ed) fpu state is in the fpu and the exception
! 484: * state just saved will soon be junk. However, the implied fninit
! 485: * doesn't change the error pointers or register contents, and we
! 486: * preserved the control word and will copy the status and tag
! 487: * words, so the complete exception state can be recovered.
! 488: */
! 489: if (i386_use_fxsave) {
! 490: addr->sv_xmm.sv_ex_sw = addr->sv_xmm.sv_env.en_sw;
! 491: addr->sv_xmm.sv_ex_tw = addr->sv_xmm.sv_env.en_tw;
! 492: } else {
! 493: addr->sv_87.sv_ex_sw = addr->sv_87.sv_env.en_sw;
! 494: addr->sv_87.sv_ex_tw = addr->sv_87.sv_env.en_tw;
! 495: }
! 496:
! 497: /*
! 498: * Pass exception to process. If it's the current process, try to do
! 499: * it immediately.
! 500: */
! 501: if (p == curproc && USERMODE(frame->if_cs, frame->if_eflags)) {
! 502: /*
! 503: * Interrupt is essentially a trap, so we can afford to call
! 504: * the SIGFPE handler (if any) as soon as the interrupt
! 505: * returns.
! 506: *
! 507: * XXX little or nothing is gained from this, and plenty is
! 508: * lost - the interrupt frame has to contain the trap frame
! 509: * (this is otherwise only necessary for the rescheduling trap
! 510: * in doreti, and the frame for that could easily be set up
! 511: * just before it is used).
! 512: */
! 513: p->p_md.md_regs = (struct trapframe *)&frame->if_fs;
! 514:
! 515: /*
! 516: * Encode the appropriate code for detailed information on
! 517: * this exception.
! 518: */
! 519: if (i386_use_fxsave)
! 520: code = x86fpflags_to_siginfo(addr->sv_xmm.sv_ex_sw);
! 521: else
! 522: code = x86fpflags_to_siginfo(addr->sv_87.sv_ex_sw);
! 523: sv.sival_int = frame->if_eip;
! 524: trapsignal(p, SIGFPE, T_ARITHTRAP, code, sv);
! 525: } else {
! 526: /*
! 527: * Nested interrupt. These losers occur when:
! 528: * o an IRQ13 is bogusly generated at a bogus time, e.g.:
! 529: * o immediately after an fnsave or frstor of an
! 530: * error state.
! 531: * o a couple of 386 instructions after
! 532: * "fstpl _memvar" causes a stack overflow.
! 533: * These are especially nasty when combined with a
! 534: * trace trap.
! 535: * o an IRQ13 occurs at the same time as another higher-
! 536: * priority interrupt.
! 537: *
! 538: * Treat them like a true async interrupt.
! 539: */
! 540: psignal(p, SIGFPE);
! 541: }
! 542:
! 543: return (1);
! 544: }
! 545:
! 546: static int
! 547: x86fpflags_to_siginfo(u_int32_t flags)
! 548: {
! 549: int i;
! 550: static int x86fp_siginfo_table[] = {
! 551: FPE_FLTINV, /* bit 0 - invalid operation */
! 552: FPE_FLTRES, /* bit 1 - denormal operand */
! 553: FPE_FLTDIV, /* bit 2 - divide by zero */
! 554: FPE_FLTOVF, /* bit 3 - fp overflow */
! 555: FPE_FLTUND, /* bit 4 - fp underflow */
! 556: FPE_FLTRES, /* bit 5 - fp precision */
! 557: FPE_FLTINV, /* bit 6 - stack fault */
! 558: };
! 559:
! 560: for (i=0;i < sizeof(x86fp_siginfo_table)/sizeof(int); i++) {
! 561: if (flags & (1 << i))
! 562: return (x86fp_siginfo_table[i]);
! 563: }
! 564: /* punt if flags not set */
! 565: return (FPE_FLTINV);
! 566: }
! 567:
! 568: /*
! 569: * Implement device not available (DNA) exception
! 570: *
! 571: * If we were the last process to use the FPU, we can simply return.
! 572: * Otherwise, we save the previous state, if necessary, and restore our last
! 573: * saved state.
! 574: */
! 575:
! 576: /*
! 577: * XXX It is unclear if the code below is correct in the multiprocessor
! 578: * XXX case. Check the NetBSD sources once again to be sure.
! 579: */
! 580: #ifdef I686_CPU
! 581: int
! 582: npxdna_xmm(struct cpu_info *ci)
! 583: {
! 584: struct proc *p;
! 585: int s;
! 586:
! 587: if (ci->ci_fpsaving) {
! 588: printf("recursive npx trap; cr0=%x\n", rcr0());
! 589: return (0);
! 590: }
! 591:
! 592: s = splipi(); /* lock out IPI's while we clean house.. */
! 593:
! 594: #ifdef MULTIPROCESSOR
! 595: p = ci->ci_curproc;
! 596: #else
! 597: p = curproc;
! 598: #endif
! 599:
! 600: IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p,
! 601: (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : ""));
! 602:
! 603: /*
! 604: * XXX should have a fast-path here when no save/restore is necessary
! 605: */
! 606: /*
! 607: * Initialize the FPU state to clear any exceptions. If someone else
! 608: * was using the FPU, save their state (which does an implicit
! 609: * initialization).
! 610: */
! 611: if (ci->ci_fpcurproc != NULL) {
! 612: IPRINTF(("%s: fp save %lx\n", ci->ci_dev.dv_xname,
! 613: (u_long)ci->ci_fpcurproc));
! 614: npxsave_cpu(ci, 1);
! 615: } else {
! 616: clts();
! 617: IPRINTF(("%s: fp init\n", ci->ci_dev.dv_xname));
! 618: fninit();
! 619: fwait();
! 620: stts();
! 621: }
! 622: splx(s);
! 623:
! 624: IPRINTF(("%s: done saving\n", ci->ci_dev.dv_xname));
! 625: KDASSERT(ci->ci_fpcurproc == NULL);
! 626: #ifndef MULTIPROCESSOR
! 627: KDASSERT(p->p_addr->u_pcb.pcb_fpcpu == NULL);
! 628: #else
! 629: if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
! 630: npxsave_proc(p, 1);
! 631: #endif
! 632: p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
! 633: clts();
! 634: s = splipi();
! 635: ci->ci_fpcurproc = p;
! 636: p->p_addr->u_pcb.pcb_fpcpu = ci;
! 637: splx(s);
! 638: uvmexp.fpswtch++;
! 639:
! 640: if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
! 641: fldcw(&p->p_addr->u_pcb.pcb_savefpu.sv_xmm.sv_env.en_cw);
! 642: p->p_md.md_flags |= MDP_USEDFPU;
! 643: } else {
! 644: static double zero = 0.0;
! 645:
! 646: /*
! 647: * amd fpu does not restore fip, fdp, fop on fxrstor
! 648: * thus leaking other process's execution history.
! 649: */
! 650: fnclex();
! 651: __asm __volatile("ffree %%st(7)\n\tfld %0" : : "m" (zero));
! 652: fxrstor(&p->p_addr->u_pcb.pcb_savefpu.sv_xmm);
! 653: }
! 654:
! 655: return (1);
! 656: }
! 657: #endif /* I686_CPU */
! 658:
! 659: int
! 660: npxdna_s87(struct cpu_info *ci)
! 661: {
! 662: struct proc *p;
! 663: int s;
! 664:
! 665: KDASSERT(i386_use_fxsave == 0);
! 666:
! 667: if (ci->ci_fpsaving) {
! 668: printf("recursive npx trap; cr0=%x\n", rcr0());
! 669: return (0);
! 670: }
! 671:
! 672: s = splipi(); /* lock out IPI's while we clean house.. */
! 673: #ifdef MULTIPROCESSOR
! 674: p = ci->ci_curproc;
! 675: #else
! 676: p = curproc;
! 677: #endif
! 678:
! 679: IPRINTF(("%s: dna for %lx%s\n", ci->ci_dev.dv_xname, (u_long)p,
! 680: (p->p_md.md_flags & MDP_USEDFPU) ? " (used fpu)" : ""));
! 681:
! 682: /*
! 683: * If someone else was using our FPU, save their state (which does an
! 684: * implicit initialization); otherwise, initialize the FPU state to
! 685: * clear any exceptions.
! 686: */
! 687: if (ci->ci_fpcurproc != NULL) {
! 688: IPRINTF(("%s: fp save %lx\n", ci->ci_dev.dv_xname,
! 689: (u_long)ci->ci_fpcurproc));
! 690: npxsave_cpu(ci, 1);
! 691: } else {
! 692: clts();
! 693: IPRINTF(("%s: fp init\n", ci->ci_dev.dv_xname));
! 694: fninit();
! 695: fwait();
! 696: stts();
! 697: }
! 698: splx(s);
! 699:
! 700: IPRINTF(("%s: done saving\n", ci->ci_dev.dv_xname));
! 701: KDASSERT(ci->ci_fpcurproc == NULL);
! 702: #ifndef MULTIPROCESSOR
! 703: KDASSERT(p->p_addr->u_pcb.pcb_fpcpu == NULL);
! 704: #else
! 705: if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
! 706: npxsave_proc(p, 1);
! 707: #endif
! 708: p->p_addr->u_pcb.pcb_cr0 &= ~CR0_TS;
! 709: clts();
! 710: s = splipi();
! 711: ci->ci_fpcurproc = p;
! 712: p->p_addr->u_pcb.pcb_fpcpu = ci;
! 713: splx(s);
! 714: uvmexp.fpswtch++;
! 715:
! 716: if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
! 717: fldcw(&p->p_addr->u_pcb.pcb_savefpu.sv_87.sv_env.en_cw);
! 718: p->p_md.md_flags |= MDP_USEDFPU;
! 719: } else {
! 720: /*
! 721: * The following frstor may cause an IRQ13 when the state being
! 722: * restored has a pending error. The error will appear to have
! 723: * been triggered by the current (npx) user instruction even
! 724: * when that instruction is a no-wait instruction that should
! 725: * not trigger an error (e.g., fnclex). On at least one 486
! 726: * system all of the no-wait instructions are broken the same
! 727: * as frstor, so our treatment does not amplify the breakage.
! 728: * On at least one 386/Cyrix 387 system, fnclex works correctly
! 729: * while frstor and fnsave are broken, so our treatment breaks
! 730: * fnclex if it is the first FPU instruction after a context
! 731: * switch.
! 732: */
! 733: frstor(&p->p_addr->u_pcb.pcb_savefpu.sv_87);
! 734: }
! 735:
! 736: return (1);
! 737: }
! 738:
! 739: /*
! 740: * The FNSAVE instruction clears the FPU state. Rather than reloading the FPU
! 741: * immediately, we clear fpcurproc and turn on CR0_TS to force a DNA and a
! 742: * reload of the FPU state the next time we try to use it. This routine
! 743: * is only called when forking, core dumping, or debugging, or swapping,
! 744: * so the lazy reload at worst forces us to trap once per fork(), and at best
! 745: * saves us a reload once per fork().
! 746: */
! 747: void
! 748: npxsave_cpu(struct cpu_info *ci, int save)
! 749: {
! 750: struct proc *p;
! 751: int s;
! 752:
! 753: KDASSERT(ci == curcpu());
! 754:
! 755: p = ci->ci_fpcurproc;
! 756: if (p == NULL)
! 757: return;
! 758:
! 759: IPRINTF(("%s: fp cpu %s %lx\n", ci->ci_dev.dv_xname,
! 760: save ? "save" : "flush", (u_long)p));
! 761:
! 762: if (save) {
! 763: #ifdef DIAGNOSTIC
! 764: if (ci->ci_fpsaving != 0)
! 765: panic("npxsave_cpu: recursive save!");
! 766: #endif
! 767: /*
! 768: * Set ci->ci_fpsaving, so that any pending exception will be
! 769: * thrown away. (It will be caught again if/when the FPU
! 770: * state is restored.)
! 771: *
! 772: * XXX on i386 and earlier, this routine should always be
! 773: * called at spl0; if it might called with the NPX interrupt
! 774: * masked, it would be necessary to forcibly unmask the NPX
! 775: * interrupt so that it could succeed.
! 776: * XXX this is irrelevant on 486 and above (systems
! 777: * which report FP failures via traps rather than irq13).
! 778: * XXX punting for now..
! 779: */
! 780: clts();
! 781: ci->ci_fpsaving = 1;
! 782: fpu_save(&p->p_addr->u_pcb.pcb_savefpu);
! 783: ci->ci_fpsaving = 0;
! 784: /* It is unclear if this is needed. */
! 785: fwait();
! 786: }
! 787:
! 788: /*
! 789: * We set the TS bit in the saved CR0 for this process, so that it
! 790: * will get a DNA exception on any FPU instruction and force a reload.
! 791: */
! 792: stts();
! 793: p->p_addr->u_pcb.pcb_cr0 |= CR0_TS;
! 794:
! 795: s = splipi();
! 796: p->p_addr->u_pcb.pcb_fpcpu = NULL;
! 797: ci->ci_fpcurproc = NULL;
! 798: splx(s);
! 799: }
! 800:
! 801: /*
! 802: * Save p's FPU state, which may be on this processor or another processor.
! 803: */
! 804: void
! 805: npxsave_proc(struct proc *p, int save)
! 806: {
! 807: struct cpu_info *ci = curcpu();
! 808: struct cpu_info *oci;
! 809:
! 810: KDASSERT(p->p_addr != NULL);
! 811:
! 812: oci = p->p_addr->u_pcb.pcb_fpcpu;
! 813: if (oci == NULL)
! 814: return;
! 815:
! 816: IPRINTF(("%s: fp proc %s %lx\n", ci->ci_dev.dv_xname,
! 817: save ? "save" : "flush", (u_long)p));
! 818:
! 819: #if defined(MULTIPROCESSOR)
! 820: if (oci == ci) {
! 821: int s = splipi();
! 822: npxsave_cpu(ci, save);
! 823: splx(s);
! 824: } else {
! 825: #ifdef DIAGNOSTIC
! 826: int spincount;
! 827: #endif
! 828:
! 829: IPRINTF(("%s: fp ipi to %s %s %lx\n", ci->ci_dev.dv_xname,
! 830: oci->ci_dev.dv_xname, save ? "save" : "flush", (u_long)p));
! 831:
! 832: i386_send_ipi(oci,
! 833: save ? I386_IPI_SYNCH_FPU : I386_IPI_FLUSH_FPU);
! 834:
! 835: #ifdef DIAGNOSTIC
! 836: spincount = 0;
! 837: #endif
! 838: while (p->p_addr->u_pcb.pcb_fpcpu != NULL) {
! 839: SPINLOCK_SPIN_HOOK;
! 840: #ifdef DIAGNOSTIC
! 841: if (spincount++ > 100000000)
! 842: panic("%s: fp_save ipi didn't (%s)",
! 843: ci->ci_dev.dv_xname, oci->ci_dev.dv_xname);
! 844: #endif
! 845: }
! 846: }
! 847: #else
! 848: KASSERT(ci->ci_fpcurproc == p);
! 849: npxsave_cpu(ci, save);
! 850: #endif
! 851: }
CVSweb