npx patches from Bruce Evans. patchkit 10002

This commit is contained in:
deraadt 1993-05-09 23:02:34 +00:00
parent 5ae997897e
commit 9948c39839
9 changed files with 632 additions and 201 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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___

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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 */