[BACK]Return to npx.c CVS log [TXT][DIR] Up to [local] / sys / arch / i386 / isa

Annotation of sys/arch/i386/isa/npx.c, Revision 1.1.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