diff --git a/sys/arch/i386/i386/genassym.c b/sys/arch/i386/i386/genassym.c index cff230d15d01..6d090ca1d4f3 100644 --- a/sys/arch/i386/i386/genassym.c +++ b/sys/arch/i386/i386/genassym.c @@ -35,7 +35,7 @@ * * @(#)genassym.c 5.11 (Berkeley) 5/10/91 */ -static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/Attic/genassym.c,v 1.2 1993/05/06 10:48:11 cgd Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/Attic/genassym.c,v 1.3 1993/05/09 23:02:34 deraadt Exp $"; #ifndef lint static char sccsid[] = "@(#)genassym.c 5.11 (Berkeley) 5/10/91"; @@ -146,9 +146,6 @@ main() printf("#define\tRU_MINFLT %d\n", &rup->ru_minflt); printf("#define\tPCB_FLAGS %d\n", &pcb->pcb_flags); printf("#define\tPCB_SAVEFPU %d\n", &pcb->pcb_savefpu); - printf("#define\tFP_WASUSED %d\n", FP_WASUSED); - printf("#define\tFP_NEEDSSAVE %d\n", FP_NEEDSSAVE); - printf("#define\tFP_NEEDSRESTORE %d\n", FP_NEEDSRESTORE); printf("#define\tFP_USESEMC %d\n", FP_USESEMC); printf("#define\tPCB_SAVEEMC %d\n", &pcb->pcb_saveemc); printf("#define\tPCB_CMAP2 %d\n", &pcb->pcb_cmap2); diff --git a/sys/arch/i386/i386/locore.s b/sys/arch/i386/i386/locore.s index 29a9e44fe359..9f508b1ad4a2 100644 --- a/sys/arch/i386/i386/locore.s +++ b/sys/arch/i386/i386/locore.s @@ -51,6 +51,10 @@ #include "machine/trap.h" +#include "machine/specialreg.h" + +#define KDSEL 0x10 + /* * Note: This version greatly munged to avoid various assembler errors * that may be fixed in newer versions of gas. Perhaps newer versions @@ -1204,20 +1208,16 @@ ENTRY(swtch) movl %edi, PCB_EDI(%ecx) #ifdef NPX - movb PCB_FLAGS(%ecx),%al /* have we used fp, and need a save? */ - andb $ FP_WASUSED|FP_NEEDSSAVE,%al - cmpb $ FP_WASUSED|FP_NEEDSSAVE,%al + mov _curproc,%eax + cmp %eax,_npxproc jne 1f - movl %cr0,%eax /* insure fp is enabled */ - andb $0xfb,%al - movl %eax,%cr0 - fnsave PCB_SAVEFPU(%ecx) - orb $4,%al /* disable it */ - movl %eax,%cr0 - movb PCB_FLAGS(%ecx),%al - xorb $ FP_NEEDSSAVE,%al /* save processed */ - movb %al,PCB_FLAGS(%ecx) + pushl %ecx /* h/w bugs make saving complicated */ + leal PCB_SAVEFPU(%ecx),%eax + pushl %eax + call _npxsave /* do it in a big C function */ + popl %eax + popl %ecx 1: #endif @@ -1288,15 +1288,6 @@ swfnd: movl PCB_EIP(%edx), %eax movl %eax, (%esp) -#ifdef NPX - movb PCB_FLAGS(%edx),%al - /* if fp could be used, a dna trap will do a restore */ - testb $ FP_WASUSED,%al - je 1f - orb $ FP_NEEDSRESTORE,PCB_FLAGS(%edx) -1: -#endif - movl PCB_CMAP2(%edx),%eax # get temporary map movl %eax,_CMAP2 # reload temporary map PTE @@ -1350,18 +1341,45 @@ ENTRY(savectx) movl %ebp, PCB_EBP(%ecx) movl %esi, PCB_ESI(%ecx) movl %edi, PCB_EDI(%ecx) + #ifdef NPX - /* have we ever used fp, and need to save? */ - testb $ FP_WASUSED, PCB_FLAGS(%ecx) - je 1f - movl %cr0, %edx - andb $0xfb, %dl - movl %edx, %cr0 - fnsave PCB_SAVEFPU(%ecx) - orb $4, %edx - movl %edx, %cr0 + /* + * If npxproc == NULL, then the npx h/w state is irrelevant and the + * state had better already be in the pcb. This is true for forks + * but not for dumps (the old book-keeping with FP flags in the pcb + * always lost for dumps because the dump pcb has 0 flags). + * + * If npxproc != NULL, then we have to save the npx h/w state to + * npxproc's pcb and copy it to the requested pcb, or save to the + * requested pcb and reload. Copying is easier because we would + * have to handle h/w bugs for reloading. We used to lose the + * parent's npx state for forks by forgetting to reload. + */ + mov _npxproc,%eax + testl %eax,%eax + je 1f + + pushl %ecx + movl P_ADDR(%eax),%eax + leal PCB_SAVEFPU(%eax),%eax + pushl %eax + pushl %eax + call _npxsave + popl %eax + popl %eax + popl %ecx + + pushl %ecx + pushl $108+8*2 /* XXX h/w state size + padding */ + leal PCB_SAVEFPU(%ecx),%ecx + pushl %ecx + pushl %eax + call _bcopy + addl $12,%esp + popl %ecx 1: #endif + movl _CMAP2, %edx # save temporary map PTE movl %edx, PCB_CMAP2(%ecx) # in our context @@ -1496,7 +1514,31 @@ IDTVEC(page) IDTVEC(rsvd) pushl $0; TRAP(T_RESERVED) IDTVEC(fpu) +#ifdef NPX + /* + * Handle like an interrupt so that we can call npxintr to clear the + * error. It would be better to handle npx interrupts as traps but + * this is difficult for nested interrupts. + */ + pushl $0 /* dummy error code */ + pushl $T_ASTFLT + pushal + nop /* silly, the bug is for popal and it only + * bites when the next instruction has a + * complicated address mode */ + pushl %ds + pushl %es /* now the stack frame is a trap frame */ + movl $KDSEL,%eax + movl %ax,%ds + movl %ax,%es + pushl _cpl + pushl $0 /* dummy unit to finish building intr frame */ + incl _cnt+V_TRAP + call _npxintr + jmp doreti +#else pushl $0; TRAP(T_ARITHTRAP) +#endif /* 17 - 31 reserved for future exp */ IDTVEC(rsvd0) pushl $0; TRAP(17) @@ -1542,13 +1584,14 @@ alltraps: calltrap: incl _cnt+V_TRAP call _trap - call _spl0 - pop %es - pop %ds - popal - nop - addl $8,%esp # pop type, code - iret + /* + * Return through doreti to handle ASTs. Have to change trap frame + * to interrupt frame. + */ + movl $T_ASTFLT,4+4+32(%esp) /* new trap type (err code not used) */ + pushl _cpl + pushl $0 /* dummy unit */ + jmp doreti #ifdef KGDB /* @@ -1581,20 +1624,37 @@ IDTVEC(syscall) pushfl # only for stupid carry bit and more stupid wait3 cc kludge pushal # only need eax,ecx,edx - trap resaves others nop - # movw $KDSEL,%ax - movw $0x10,%ax # switch to kernel segments - movw %ax,%ds - movw %ax,%es + movl $KDSEL,%eax # switch to kernel segments + movl %ax,%ds + movl %ax,%es incl _cnt+V_SYSCALL # kml 3/25/93 call _syscall - call _spl0 - movw __udatasel,%ax # switch back to user segments - movw %ax,%ds - movw %ax,%es + /* + * Return through doreti to handle ASTs. Have to change syscall frame + * to interrupt frame. + * + * XXX - we should have set up the frame earlier to avoid the + * following popal/pushal (not much can be done to avoid shuffling + * the flags). Consistent frames would simplify things all over. + */ + movl 32+0(%esp),%eax /* old flags, shuffle to above cs:eip */ + movl 32+4(%esp),%ebx /* `int' frame should have been ef, eip, cs */ + movl 32+8(%esp),%ecx + movl %ebx,32+0(%esp) + movl %ecx,32+4(%esp) + movl %eax,32+8(%esp) popal nop - popfl - lret + pushl $0 /* dummy error code */ + pushl $T_ASTFLT + pushal + nop + movl __udatasel,%eax /* switch back to user segments */ + push %eax /* XXX - better to preserve originals? */ + push %eax + pushl _cpl + pushl $0 + jmp doreti ALIGN32 ENTRY(set_cpu_type) @@ -1611,7 +1671,7 @@ ENTRY(set_cpu_type) andl $1, %eax push %ecx popfl - + movl $_cpu, %ecx cmpl $0, %eax jne 1f diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index e9e47b953be4..ef2047011867 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -44,7 +44,7 @@ * 15 Aug 92 William Jolitz Large memory bug * 15 Aug 92 Terry Lambert Fixed CMOS RAM size bug */ -static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/machdep.c,v 1.7 1993/05/07 05:22:14 cgd Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/machdep.c,v 1.8 1993/05/09 23:02:38 deraadt Exp $"; #include "param.h" #include "systm.h" @@ -623,7 +623,7 @@ setregs(p, entry) p->p_regs[sEIP] = entry; p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */ - load_cr0(rcr0() | CR0_EM); /* start emulating */ + load_cr0(rcr0() | CR0_TS); /* start emulating */ #ifdef NPX npxinit(__INITIAL_NPXCW__); #endif diff --git a/sys/arch/i386/i386/vm_machdep.c b/sys/arch/i386/i386/vm_machdep.c index 924db96bb550..10bc557d2d47 100644 --- a/sys/arch/i386/i386/vm_machdep.c +++ b/sys/arch/i386/i386/vm_machdep.c @@ -41,7 +41,7 @@ /* * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ */ -static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/Attic/vm_machdep.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/i386/Attic/vm_machdep.c,v 1.2 1993/05/09 23:02:39 deraadt Exp $"; #include "param.h" #include "systm.h" @@ -115,8 +115,6 @@ cpu_fork(p1, p2) return (0); } -extern struct proc *npxproc; - #ifdef notyet /* * cpu_exit is called as the last action during exit. @@ -138,8 +136,9 @@ cpu_exit(p) { static struct pcb nullpcb; /* pcb to overwrite on last swtch */ - /* free cporcessor (if we have it) */ - if( p == npxproc) npxproc =0; +#ifdef NPX + npxexit(p); +#endif /* move to inactive space and stack, passing arg accross */ p = swtch_to_inactive(p); @@ -158,9 +157,9 @@ cpu_exit(p) register struct proc *p; { - /* free coprocessor (if we have it) */ - if( p == npxproc) npxproc =0; - +#ifdef NPX + npxexit(p); +#endif splclock(); swtch(); } diff --git a/sys/arch/i386/include/npx.h b/sys/arch/i386/include/npx.h index ad710fc9cf41..9bd0d482dbe5 100644 --- a/sys/arch/i386/include/npx.h +++ b/sys/arch/i386/include/npx.h @@ -58,16 +58,25 @@ struct env87 { /* Contents of each floating point accumulator */ struct fpacc87 { +#ifdef dontdef /* too unportable */ u_long fp_mantlo; /* mantissa low (31:0) */ u_long fp_manthi; /* mantissa high (63:32) */ int fp_exp:15; /* exponent */ int fp_sgn:1; /* mantissa sign */ +#else + u_char fp_bytes[10]; +#endif }; /* Floating point context */ struct save87 { struct env87 sv_env; /* floating point control/status */ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ +#ifndef dontdef + u_long sv_ex_sw; /* status word for last exception (was pad) */ + u_long sv_ex_tw; /* tag word for last exception (was pad) */ + u_char sv_pad[8 * 2 - 2 * 4]; /* bogus historical padding */ +#endif }; /* Cyrix EMC memory - mapped coprocessor context switch information */ @@ -77,15 +86,53 @@ struct emcsts { long em_dl; /* memory mapped D low register when swtched */ }; -/* Intel prefer's long real (53 bit) precision */ +/* Intel prefers long real (53 bit) precision */ #define __iBCS_NPXCW__ 0x262 -/* wfj prefer's temporary real (64 bit) precision */ +/* wfj prefers temporary real (64 bit) precision */ #define __386BSD_NPXCW__ 0x362 +/* + * bde prefers 53 bit precision and all exceptions masked. + * + * The standard control word from finit is 0x37F, giving: + * + * round to nearest + * 64-bit precision + * all exceptions masked. + * + * Now I want: + * + * affine mode for 287's (if they work at all) (1 in bitfield 1<<12) + * 53-bit precision (2 in bitfield 3<<8) + * overflow exception unmasked (0 in bitfield 1<<3) + * zero divide exception unmasked (0 in bitfield 1<<2) + * invalid-operand exception unmasked (0 in bitfield 1<<0). + * + * 64-bit precision often gives bad results with high level languages + * because it makes the results of calculations depend on whether + * intermediate values are stored in memory or in FPU registers. + * + * The "Intel" and wfj control words have: + * + * underflow exception unmasked (0 in bitfield 1<<4) + * + * but that causes an unexpected exception in the test program 'paranoia' + * and makes denormals useless (DBL_MIN / 2 underflows). It doesn't make + * a lot of sense to trap underflow without trapping denormals. + * + * Later I will want the IEEE default of all exceptions masked. See the + * 0.0 math manpage for why this is better. The 0.1 math manpage is empty. + */ +#define __BDE_NPXCW__ 0x1272 +#define __BETTER_BDE_NPXCW__ 0x127f +#ifdef __BROKEN_NPXCW__ #ifdef __386BSD__ #define __INITIAL_NPXCW__ __386BSD_NPXCW__ #else #define __INITIAL_NPXCW__ __iBCS_NPXCW__ #endif +#else +#define __INITIAL_NPXCW__ __BDE_NPXCW__ +#endif #endif ___NPX87___ diff --git a/sys/arch/i386/include/pcb.h b/sys/arch/i386/include/pcb.h index 29fa36de5f63..58f4f5321c0c 100644 --- a/sys/arch/i386/include/pcb.h +++ b/sys/arch/i386/include/pcb.h @@ -60,9 +60,11 @@ struct pcb { * Software pcb (extension) */ int pcb_flags; +#ifdef notused #define FP_WASUSED 0x01 /* process has used fltng pnt hardware */ #define FP_NEEDSSAVE 0x02 /* ... that needs save on next context switch */ #define FP_NEEDSRESTORE 0x04 /* ... that needs restore on next DNA fault */ +#endif #define FP_USESEMC 0x08 /* process uses EMC memory-mapped mode */ #define FM_TRAP 0x10 /* process entered kernel on a trap frame */ #define FP_SOFTFP 0x20 /* process using software fltng pnt emulator */ diff --git a/sys/arch/i386/include/specialreg.h b/sys/arch/i386/include/specialreg.h index eb1a5b48c547..0c91f4b37f21 100644 --- a/sys/arch/i386/include/specialreg.h +++ b/sys/arch/i386/include/specialreg.h @@ -34,12 +34,26 @@ */ /* - * 386 Special registers: + * Bits in 386 special registers: */ #define CR0_PE 0x00000001 /* Protected mode Enable */ -#define CR0_MP 0x00000002 /* "Math" Present (e.g. npx), wait for it */ -#define CR0_EM 0x00000004 /* EMulate NPX, e.g. trap, don't execute code */ -#define CR0_TS 0x00000008 /* Process has done Task Switch, do NPX save */ -#define CR0_ET 0x00000010 /* 32 bit (if set) vs 16 bit (387 vs 287) */ -#define CR0_PG 0x80000000 /* Paging Enable */ +#define CR0_MP 0x00000002 /* "Math" Present (NPX or NPX emulator) */ +#ifdef notused +#define CR0_EM 0x00000004 /* EMulate non-NPX coproc. (trap ESC only) */ +#endif +#define CR0_TS 0x00000008 /* Task Switched (if MP, trap ESC and WAIT) */ +#ifdef notused +#define CR0_ET 0x00000010 /* Extension Type (387 (if set) vs 287) */ +#endif +#define CR0_PG 0x80000000 /* PaGing enable */ + +/* + * Bits in 486 special registers: + */ + +#define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ +#define CR0_WP 0x00010000 /* Write Protect (honor ~PG_W in all modes) */ +#ifdef notyet +#define CR0_AM 0x00040000 /* Alignment Mask (set to enable AC flag) */ +#endif diff --git a/sys/arch/i386/isa/icu.s b/sys/arch/i386/isa/icu.s index 0d9bea1e1abb..cd0b856e4a54 100644 --- a/sys/arch/i386/isa/icu.s +++ b/sys/arch/i386/isa/icu.s @@ -109,7 +109,7 @@ doreti: ALIGN32 1: cmpl $0,_netisr # check for softint s/traps jne 1f - cmpl $0,_want_resched + cmpl $0,_astpending jne 1f pop %es # none, going back to base pri @@ -171,8 +171,9 @@ doreti: 1: cmpw $0x1f,13*4(%esp) # to user? jne 2f # nope, leave - cmpl $0,_want_resched + cmpl $0,_astpending je 2f + movl $0,_astpending call _trap 2: pop %es diff --git a/sys/arch/i386/isa/npx.c b/sys/arch/i386/isa/npx.c index 57bcb755bb99..8fcf1704d551 100644 --- a/sys/arch/i386/isa/npx.c +++ b/sys/arch/i386/isa/npx.c @@ -33,7 +33,7 @@ * * @(#)npx.c 7.2 (Berkeley) 5/12/91 */ -static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/isa/Attic/npx.c,v 1.2 1993/04/03 02:18:02 cgd Exp $"; +static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/isa/Attic/npx.c,v 1.3 1993/05/09 23:03:41 deraadt Exp $"; #include "npx.h" #if NNPX > 0 @@ -46,191 +46,502 @@ static char rcsid[] = "$Header: /cvsroot/src/sys/arch/i386/isa/Attic/npx.c,v 1.2 #include "machine/pcb.h" #include "machine/trap.h" #include "ioctl.h" +#include "i386/isa/icu.h" #include "machine/specialreg.h" #include "i386/isa/isa_device.h" -#include "icu.h" +#include "i386/isa/isa.h" + /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ -int npxprobe(), npxattach(), npxintr(); +#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", }; -struct proc *npxproc; /* process who owns device, otherwise zero */ -struct pcb *npxpcb; /* owners context structure */ -int npxexists; -extern long npx0mask; +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; /* - * Probe routine - look device, otherwise set emulator bit + * 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; -{ static status, control; +{ + 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 - - /* insure EM bit off */ - load_cr0(rcr0() & ~CR0_EM); /* stop emulating */ - asm(" fninit "); /* put device in known state */ - - /* check for a proper status of zero */ - status = 0x5a5a; - asm (" fnstsw %0 " : "=m" (status) : "m" (status) ); - - if ((status&0xff) == 0) { - - /* good, now check for a proper control word */ - asm (" fnstcw %0 " : "=m" (status) : "m" (status)); - - if ((status&0x103f) == 0x3f) { - /* then we have a numeric coprocessor */ - /* XXX should force an exception here to generate an intr */ - return (1); + /* + * 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 */ + return 16; + } + if (npx_intrs_while_probing != 0) { + /* + * Bad, we are stuck with IRQ13. + */ + npx_irq13 = 1; + npx0mask = dvp->id_irq; /* npxattach too late */ + return 16; + } + /* + * Worse, even IRQ13 is broken. Use emulator. + */ } } - - /* insure EM bit on */ - load_cr0(rcr0() | CR0_EM); /* start emulating */ - return (0); + /* + * 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 16; } /* * Attach routine - announce which it is, and wire into system */ +int npxattach(dvp) struct isa_device *dvp; { - + if (npx_ex16) + printf("npx%d: exception 16\n", dvp->id_unit); + else if (npx_irq13) + ; + else if (npx_exists) + printf("npx%d: error reporting broken, using emulator\n", + dvp->id_unit); + else + printf("npx%d: emulator\n", dvp->id_unit); npxinit(__INITIAL_NPXCW__); - npxexists++; - npx0mask = dvp->id_irq; + return (1); } /* * Initialize floating point unit. */ -npxinit(control) { - static short wd; +void +npxinit(control) + u_int control; +{ + struct save87 dummy; - if (npxexists == 0) return; + 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(); +} - - wd = control; - wd = 0x272; - load_cr0(rcr0() & ~CR0_EM); /* stop emulating */ - asm (" fninit"); - asm(" fldcw %0" : : "g" (wd)); - if (curpcb) { -#if __GNUC__ >= 2 - asm(" fnsave 0(%0) " : : "a" (&curpcb->pcb_savefpu) ); -#else - asm(" fnsave %0 " : : "g" (curpcb->pcb_savefpu) ); -#endif - curpcb->pcb_flags |= FP_NEEDSRESTORE; +/* + * Free coprocessor (if we have it). + */ +void +npxexit(p) + struct proc *p; +{ + if (p == npxproc) { + start_emulating(); + npxproc = NULL; } - load_cr0(rcr0() | CR0_EM); /* start emulating */ - outb(0xb1,0); /* reset processor */ } /* - * Load floating point context and record ownership to suite + * 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. */ -npxload() { - - if (npxproc) panic ("npxload"); - npxproc = curproc; - npxpcb = curpcb; -#if __GNUC__ >= 2 - asm(" frstor 0(%0) " : : "a" (&curpcb->pcb_savefpu) ); -#else - asm(" frstor %0 " : : "g" (curpcb->pcb_savefpu) ); -#endif -} - -/* - * Unload floating point context and relinquish ownership - */ -npxunload() { - - if (npxproc == 0) panic ("npxunload"); -#if __GNUC__ >= 2 - asm(" fsave 0(%0) " : : "a" (&npxpcb->pcb_savefpu) ); -#else - asm(" fsave %0 " : : "g" (npxpcb->pcb_savefpu) ); -#endif - npxproc = 0 ; -} - -/* - * Record information needed in processing an exception and clear status word - */ -npxintr(frame) struct intrframe frame; { +void +npxintr(frame) + struct intrframe frame; +{ int code; -static status; - outb(0xf0,0); /* reset processor */ -/*pg("npxintr");*/ - - asm (" fnstsw %0 " : "=m" (status) : "m" (status) ); - /* sync state in process context structure, in advance of debugger/process looking for it */ - if (npxproc == 0 || npxexists == 0) panic ("npxintr"); -#if __GNUC__ >= 2 - asm (" fnsave 0(%0) " : : "a" (&npxpcb->pcb_savefpu) ); -#else - asm (" fnsave %0 " : : "g" (npxpcb->pcb_savefpu) ); -#endif + 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 = ???; + /* + * Encode the appropriate code for detailed information on + * this exception. + */ + code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else - code = 0; /* XXX */ + code = 0; /* XXX */ #endif - -/*if((pg("status %x", status) & 0x7f) == 't') {*/ - /* pass exception to process, which may not be the current one */ - if (npxproc == curproc) { - /* Q: what if in an interrupt, or in trap processing? */ - if (ISPL(frame.if_cs) == SEL_UPL) { - curproc->p_regs = (int *)&frame.if_es; - curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */ - } /* else printf("*");*/ trapsignal(curproc, SIGFPE, code); - curpcb->pcb_flags &= ~FM_TRAP; /* used by sendsig */ + curpcb->pcb_flags &= ~FM_TRAP; } else { - /* printf("P");*/ + /* + * 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); } -/*}*/ - - /* clear the exception so we can catch others like it */ - asm (" fnclex"); } /* * 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. */ -npxdna() { -/*pg("npxdna");*/ - - - if (npxexists == 0) return(0); - load_cr0(rcr0() & ~CR0_EM); /* stop emulating */ - if (curpcb->pcb_flags & FP_NEEDSRESTORE) -#if __GNUC__ >= 2 - asm(" frstor 0(%0) " : : "a" (&curpcb->pcb_savefpu)); -#else - asm(" frstor %0 " : : "g" (curpcb->pcb_savefpu)); -#endif - curpcb->pcb_flags |= FP_WASUSED | FP_NEEDSSAVE; - curpcb->pcb_flags &= ~FP_NEEDSRESTORE; +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; - npxpcb = curpcb; + /* + * 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); } -#endif + +/* + * 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 */