summaryrefslogtreecommitdiffstats
path: root/sys/amd64/isa/npx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64/isa/npx.c')
-rw-r--r--sys/amd64/isa/npx.c564
1 files changed, 564 insertions, 0 deletions
diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c
new file mode 100644
index 0000000..73392fa
--- /dev/null
+++ b/sys/amd64/isa/npx.c
@@ -0,0 +1,564 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)npx.c 7.2 (Berkeley) 5/12/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00154
+ * -------------------- ----- ----------------------
+ *
+ * 20 Apr 93 Bruce Evans New npx-0.5 code
+ * 23 May 93 Rodney W. Grimes Return a special value of -1 from
+ * the probe code to keep isa_config from
+ * printing out the I/O address when we
+ * are using trap 16 handling.
+ *
+ */
+static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/npx.c,v 1.2 92/01/21 14:34:27 william Exp $";
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/trap.h"
+#include "ioctl.h"
+#include "machine/specialreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0mask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al /* EOI (asm in strings loses cpp features) */
+ outb %al,$0xa0 /* IO_ICU2 */
+ outb %al,$0x20 /* IO_ICU1 */
+ movb $0,%al
+ outb %al,$0xf0 /* clear BUSY# latch */
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0mask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ return (IO_NPXSIZE);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (npx_ex16)
+ printf(" <Errors reported via Exception 16>");
+ else if (npx_irq13)
+ printf(" <Errors reported via IRQ 13>");
+ else if (npx_exists)
+ printf(" <Error reporting broken, using 387 emulator>");
+ else
+ printf(" <387 Emulator>");
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_regs = (int *)&frame.if_es;
+ curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ curpcb->pcb_flags &= ~FM_TRAP;
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0mask >> 8))
+ | (old_icu2_mask & (npx0mask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
OpenPOWER on IntegriCloud