diff options
author | mini <mini@FreeBSD.org> | 2002-09-16 19:25:59 +0000 |
---|---|---|
committer | mini <mini@FreeBSD.org> | 2002-09-16 19:25:59 +0000 |
commit | 5375a15c910d0210dbf79d04fceb3ab24a2c9422 (patch) | |
tree | 58b9e4682b90291b26e95a790d5bf4c34c4357a2 /sys | |
parent | 62e41a5a7c826f21a8548bd3d7fcbc218809544e (diff) | |
download | FreeBSD-src-5375a15c910d0210dbf79d04fceb3ab24a2c9422.zip FreeBSD-src-5375a15c910d0210dbf79d04fceb3ab24a2c9422.tar.gz |
Add kernel support needed for the KSE-aware libpthread:
- Maintain fpu state across signals.
- Save and restore FPU state properly in ucontext_t's.
Reviewed by: bde, deischen, julian
Approved by: -arch
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/amd64/fpu.c | 187 | ||||
-rw-r--r-- | sys/amd64/isa/npx.c | 187 | ||||
-rw-r--r-- | sys/i386/isa/npx.c | 187 |
3 files changed, 456 insertions, 105 deletions
diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c index 956d2f2..29e5f06 100644 --- a/sys/amd64/amd64/fpu.c +++ b/sys/amd64/amd64/fpu.c @@ -74,6 +74,7 @@ #include <machine/resource.h> #include <machine/specialreg.h> #include <machine/segments.h> +#include <machine/ucontext.h> #ifndef SMP #include <i386/isa/icu.h> @@ -151,17 +152,11 @@ void stop_emulating(void); (cpu_fxsr ? \ (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_sw : \ (thread)->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (cpu_fxsr ? \ - &(pcb)->pcb_save.sv_xmm.sv_ex_sw : \ - &(pcb)->pcb_save.sv_87.sv_ex_sw) #else /* CPU_ENABLE_SSE */ #define GET_FPU_CW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_cw) #define GET_FPU_SW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (&(pcb)->pcb_save.sv_87.sv_ex_sw) #endif /* CPU_ENABLE_SSE */ typedef u_char bool_t; @@ -190,6 +185,8 @@ static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif +static union savefpu npx_cleanstate; +static bool_t npx_cleanstate_ready; static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; @@ -461,6 +458,7 @@ npx_attach(dev) device_t dev; { int flags; + register_t s; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; @@ -497,6 +495,14 @@ npx_attach(dev) } npxinit(__INITIAL_NPXCW__); + if (npx_cleanstate_ready == 0) { + s = intr_disable(); + stop_emulating(); + fpusave(&npx_cleanstate); + start_emulating(); + npx_cleanstate_ready = 1; + intr_restore(s); + } #ifdef I586_CPU_XXX if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < @@ -543,8 +549,6 @@ npxinit(control) fninit(); #endif fldcw(&control); - if (PCPU_GET(curpcb) != NULL) - fpusave(&PCPU_GET(curpcb)->pcb_save); start_emulating(); intr_restore(savecrit); } @@ -559,15 +563,14 @@ npxexit(td) register_t savecrit; savecrit = intr_disable(); - if (td == PCPU_GET(fpcurthread)) + if (curthread == PCPU_GET(fpcurthread)) npxsave(&PCPU_GET(curpcb)->pcb_save); intr_restore(savecrit); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; - masked_exceptions = PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_cw - & PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_sw & 0x7f; + masked_exceptions = GET_FPU_CW(td) & GET_FPU_SW(td) & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). @@ -581,6 +584,19 @@ npxexit(td) #endif } +int +npxformat() +{ + + if (!npx_exists) + return (_MC_FPFMT_NODEV); +#ifdef CPU_ENABLE_SSE + if (cpu_fxsr) + return (_MC_FPFMT_XMM); +#endif + return (_MC_FPFMT_387); +} + /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user @@ -774,7 +790,6 @@ npxtrap() { register_t savecrit; u_short control, status; - u_long *exstat; if (!npx_exists) { printf("npxtrap: fpcurthread = %p, curthread = %p, npx_exists = %d\n", @@ -796,11 +811,7 @@ npxtrap() fnstsw(&status); } - exstat = GET_FPU_EXSW_PTR(curthread->td_pcb); - *exstat = status; - if (PCPU_GET(fpcurthread) != curthread) - GET_FPU_SW(curthread) &= ~0x80bf; - else + if (PCPU_GET(fpcurthread) == curthread) fnclex(); intr_restore(savecrit); return (fpetable[status & ((~control & 0x3f) | 0x40)]); @@ -813,17 +824,29 @@ npxtrap() * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ + +static int err_count = 0; + int npxdna() { - u_long *exstat; + struct pcb *pcb; register_t s; + u_short control; if (!npx_exists) return (0); + if (PCPU_GET(fpcurthread) == curthread) { + printf("npxdna: fpcurthread == curthread %d times\n", + ++err_count); + stop_emulating(); + return (1); + } if (PCPU_GET(fpcurthread) != NULL) { - printf("npxdna: fpcurthread = %p, curthread = %p\n", - PCPU_GET(fpcurthread), curthread); + printf("npxdna: fpcurthread = %p (%d), curthread = %p (%d)\n", + PCPU_GET(fpcurthread), + PCPU_GET(fpcurthread)->td_proc->p_pid, + curthread, curthread->td_proc->p_pid); panic("npxdna"); } s = intr_disable(); @@ -832,22 +855,35 @@ npxdna() * Record new context early in case frstor causes an IRQ13. */ PCPU_SET(fpcurthread, curthread); + pcb = PCPU_GET(curpcb); - exstat = GET_FPU_EXSW_PTR(PCPU_GET(curpcb)); - *exstat = 0; - /* - * 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. - */ - fpurstor(&PCPU_GET(curpcb)->pcb_save); + if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + /* + * This is the first time this thread has used the FPU or + * the PCB doesn't contain a clean FPU state. Explicitly + * initialize the FPU and load the default control word. + */ + fninit(); + control = __INITIAL_NPXCW__; + fldcw(&control); + pcb->pcb_flags |= PCB_NPXINITDONE; + } else { + /* + * 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. + */ + fpurstor(&pcb->pcb_save); + } intr_restore(s); return (1); @@ -888,6 +924,87 @@ npxsave(addr) PCPU_SET(fpcurthread, NULL); } +/* + * This should be called with interrupts disabled and only when the owning + * FPU thread is non-null. + */ +void +npxdrop() +{ + struct thread *td; + + td = PCPU_GET(fpcurthread); + PCPU_SET(fpcurthread, NULL); + td->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; + start_emulating(); +} + +/* + * Get the state of the FPU without dropping ownership (if possible). + * It returns the FPU ownership status. + */ +int +npxgetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return (_MC_FPOWNED_NONE); + + if ((td->td_pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + if (npx_cleanstate_ready) + bcopy(&npx_cleanstate, addr, sizeof(npx_cleanstate)); + else + bzero(addr, sizeof(*addr)); + return (_MC_FPOWNED_NONE); + } + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpusave(addr); +#ifdef CPU_ENABLE_SSE + if (!cpu_fxsr) +#endif + /* + * fnsave initializes the FPU and destroys whatever + * context it contains. Make sure the FPU owner + * starts with a clean state next time. + */ + npxdrop(); + intr_restore(s); + return (_MC_FPOWNED_FPU); + } else { + intr_restore(s); + bcopy(&td->td_pcb->pcb_save, addr, sizeof(*addr)); + return (_MC_FPOWNED_PCB); + } +} + +/* + * Set the state of the FPU. + */ +void +npxsetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return; + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpurstor(addr); + intr_restore(s); + } else { + intr_restore(s); + bcopy(addr, &td->td_pcb->pcb_save, sizeof(*addr)); + } +} + static void fpusave(addr) union savefpu *addr; diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c index 956d2f2..29e5f06 100644 --- a/sys/amd64/isa/npx.c +++ b/sys/amd64/isa/npx.c @@ -74,6 +74,7 @@ #include <machine/resource.h> #include <machine/specialreg.h> #include <machine/segments.h> +#include <machine/ucontext.h> #ifndef SMP #include <i386/isa/icu.h> @@ -151,17 +152,11 @@ void stop_emulating(void); (cpu_fxsr ? \ (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_sw : \ (thread)->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (cpu_fxsr ? \ - &(pcb)->pcb_save.sv_xmm.sv_ex_sw : \ - &(pcb)->pcb_save.sv_87.sv_ex_sw) #else /* CPU_ENABLE_SSE */ #define GET_FPU_CW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_cw) #define GET_FPU_SW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (&(pcb)->pcb_save.sv_87.sv_ex_sw) #endif /* CPU_ENABLE_SSE */ typedef u_char bool_t; @@ -190,6 +185,8 @@ static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif +static union savefpu npx_cleanstate; +static bool_t npx_cleanstate_ready; static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; @@ -461,6 +458,7 @@ npx_attach(dev) device_t dev; { int flags; + register_t s; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; @@ -497,6 +495,14 @@ npx_attach(dev) } npxinit(__INITIAL_NPXCW__); + if (npx_cleanstate_ready == 0) { + s = intr_disable(); + stop_emulating(); + fpusave(&npx_cleanstate); + start_emulating(); + npx_cleanstate_ready = 1; + intr_restore(s); + } #ifdef I586_CPU_XXX if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < @@ -543,8 +549,6 @@ npxinit(control) fninit(); #endif fldcw(&control); - if (PCPU_GET(curpcb) != NULL) - fpusave(&PCPU_GET(curpcb)->pcb_save); start_emulating(); intr_restore(savecrit); } @@ -559,15 +563,14 @@ npxexit(td) register_t savecrit; savecrit = intr_disable(); - if (td == PCPU_GET(fpcurthread)) + if (curthread == PCPU_GET(fpcurthread)) npxsave(&PCPU_GET(curpcb)->pcb_save); intr_restore(savecrit); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; - masked_exceptions = PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_cw - & PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_sw & 0x7f; + masked_exceptions = GET_FPU_CW(td) & GET_FPU_SW(td) & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). @@ -581,6 +584,19 @@ npxexit(td) #endif } +int +npxformat() +{ + + if (!npx_exists) + return (_MC_FPFMT_NODEV); +#ifdef CPU_ENABLE_SSE + if (cpu_fxsr) + return (_MC_FPFMT_XMM); +#endif + return (_MC_FPFMT_387); +} + /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user @@ -774,7 +790,6 @@ npxtrap() { register_t savecrit; u_short control, status; - u_long *exstat; if (!npx_exists) { printf("npxtrap: fpcurthread = %p, curthread = %p, npx_exists = %d\n", @@ -796,11 +811,7 @@ npxtrap() fnstsw(&status); } - exstat = GET_FPU_EXSW_PTR(curthread->td_pcb); - *exstat = status; - if (PCPU_GET(fpcurthread) != curthread) - GET_FPU_SW(curthread) &= ~0x80bf; - else + if (PCPU_GET(fpcurthread) == curthread) fnclex(); intr_restore(savecrit); return (fpetable[status & ((~control & 0x3f) | 0x40)]); @@ -813,17 +824,29 @@ npxtrap() * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ + +static int err_count = 0; + int npxdna() { - u_long *exstat; + struct pcb *pcb; register_t s; + u_short control; if (!npx_exists) return (0); + if (PCPU_GET(fpcurthread) == curthread) { + printf("npxdna: fpcurthread == curthread %d times\n", + ++err_count); + stop_emulating(); + return (1); + } if (PCPU_GET(fpcurthread) != NULL) { - printf("npxdna: fpcurthread = %p, curthread = %p\n", - PCPU_GET(fpcurthread), curthread); + printf("npxdna: fpcurthread = %p (%d), curthread = %p (%d)\n", + PCPU_GET(fpcurthread), + PCPU_GET(fpcurthread)->td_proc->p_pid, + curthread, curthread->td_proc->p_pid); panic("npxdna"); } s = intr_disable(); @@ -832,22 +855,35 @@ npxdna() * Record new context early in case frstor causes an IRQ13. */ PCPU_SET(fpcurthread, curthread); + pcb = PCPU_GET(curpcb); - exstat = GET_FPU_EXSW_PTR(PCPU_GET(curpcb)); - *exstat = 0; - /* - * 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. - */ - fpurstor(&PCPU_GET(curpcb)->pcb_save); + if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + /* + * This is the first time this thread has used the FPU or + * the PCB doesn't contain a clean FPU state. Explicitly + * initialize the FPU and load the default control word. + */ + fninit(); + control = __INITIAL_NPXCW__; + fldcw(&control); + pcb->pcb_flags |= PCB_NPXINITDONE; + } else { + /* + * 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. + */ + fpurstor(&pcb->pcb_save); + } intr_restore(s); return (1); @@ -888,6 +924,87 @@ npxsave(addr) PCPU_SET(fpcurthread, NULL); } +/* + * This should be called with interrupts disabled and only when the owning + * FPU thread is non-null. + */ +void +npxdrop() +{ + struct thread *td; + + td = PCPU_GET(fpcurthread); + PCPU_SET(fpcurthread, NULL); + td->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; + start_emulating(); +} + +/* + * Get the state of the FPU without dropping ownership (if possible). + * It returns the FPU ownership status. + */ +int +npxgetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return (_MC_FPOWNED_NONE); + + if ((td->td_pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + if (npx_cleanstate_ready) + bcopy(&npx_cleanstate, addr, sizeof(npx_cleanstate)); + else + bzero(addr, sizeof(*addr)); + return (_MC_FPOWNED_NONE); + } + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpusave(addr); +#ifdef CPU_ENABLE_SSE + if (!cpu_fxsr) +#endif + /* + * fnsave initializes the FPU and destroys whatever + * context it contains. Make sure the FPU owner + * starts with a clean state next time. + */ + npxdrop(); + intr_restore(s); + return (_MC_FPOWNED_FPU); + } else { + intr_restore(s); + bcopy(&td->td_pcb->pcb_save, addr, sizeof(*addr)); + return (_MC_FPOWNED_PCB); + } +} + +/* + * Set the state of the FPU. + */ +void +npxsetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return; + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpurstor(addr); + intr_restore(s); + } else { + intr_restore(s); + bcopy(addr, &td->td_pcb->pcb_save, sizeof(*addr)); + } +} + static void fpusave(addr) union savefpu *addr; diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index 956d2f2..29e5f06 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -74,6 +74,7 @@ #include <machine/resource.h> #include <machine/specialreg.h> #include <machine/segments.h> +#include <machine/ucontext.h> #ifndef SMP #include <i386/isa/icu.h> @@ -151,17 +152,11 @@ void stop_emulating(void); (cpu_fxsr ? \ (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_sw : \ (thread)->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (cpu_fxsr ? \ - &(pcb)->pcb_save.sv_xmm.sv_ex_sw : \ - &(pcb)->pcb_save.sv_87.sv_ex_sw) #else /* CPU_ENABLE_SSE */ #define GET_FPU_CW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_cw) #define GET_FPU_SW(thread) \ (thread->td_pcb->pcb_save.sv_87.sv_env.en_sw) -#define GET_FPU_EXSW_PTR(pcb) \ - (&(pcb)->pcb_save.sv_87.sv_ex_sw) #endif /* CPU_ENABLE_SSE */ typedef u_char bool_t; @@ -190,6 +185,8 @@ static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; #endif +static union savefpu npx_cleanstate; +static bool_t npx_cleanstate_ready; static bool_t npx_ex16; static bool_t npx_exists; static bool_t npx_irq13; @@ -461,6 +458,7 @@ npx_attach(dev) device_t dev; { int flags; + register_t s; if (resource_int_value("npx", 0, "flags", &flags) != 0) flags = 0; @@ -497,6 +495,14 @@ npx_attach(dev) } npxinit(__INITIAL_NPXCW__); + if (npx_cleanstate_ready == 0) { + s = intr_disable(); + stop_emulating(); + fpusave(&npx_cleanstate); + start_emulating(); + npx_cleanstate_ready = 1; + intr_restore(s); + } #ifdef I586_CPU_XXX if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && timezero("i586_bzero()", i586_bzero) < @@ -543,8 +549,6 @@ npxinit(control) fninit(); #endif fldcw(&control); - if (PCPU_GET(curpcb) != NULL) - fpusave(&PCPU_GET(curpcb)->pcb_save); start_emulating(); intr_restore(savecrit); } @@ -559,15 +563,14 @@ npxexit(td) register_t savecrit; savecrit = intr_disable(); - if (td == PCPU_GET(fpcurthread)) + if (curthread == PCPU_GET(fpcurthread)) npxsave(&PCPU_GET(curpcb)->pcb_save); intr_restore(savecrit); #ifdef NPX_DEBUG if (npx_exists) { u_int masked_exceptions; - masked_exceptions = PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_cw - & PCPU_GET(curpcb)->pcb_save.sv_87.sv_env.en_sw & 0x7f; + masked_exceptions = GET_FPU_CW(td) & GET_FPU_SW(td) & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). @@ -581,6 +584,19 @@ npxexit(td) #endif } +int +npxformat() +{ + + if (!npx_exists) + return (_MC_FPFMT_NODEV); +#ifdef CPU_ENABLE_SSE + if (cpu_fxsr) + return (_MC_FPFMT_XMM); +#endif + return (_MC_FPFMT_387); +} + /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user @@ -774,7 +790,6 @@ npxtrap() { register_t savecrit; u_short control, status; - u_long *exstat; if (!npx_exists) { printf("npxtrap: fpcurthread = %p, curthread = %p, npx_exists = %d\n", @@ -796,11 +811,7 @@ npxtrap() fnstsw(&status); } - exstat = GET_FPU_EXSW_PTR(curthread->td_pcb); - *exstat = status; - if (PCPU_GET(fpcurthread) != curthread) - GET_FPU_SW(curthread) &= ~0x80bf; - else + if (PCPU_GET(fpcurthread) == curthread) fnclex(); intr_restore(savecrit); return (fpetable[status & ((~control & 0x3f) | 0x40)]); @@ -813,17 +824,29 @@ npxtrap() * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ + +static int err_count = 0; + int npxdna() { - u_long *exstat; + struct pcb *pcb; register_t s; + u_short control; if (!npx_exists) return (0); + if (PCPU_GET(fpcurthread) == curthread) { + printf("npxdna: fpcurthread == curthread %d times\n", + ++err_count); + stop_emulating(); + return (1); + } if (PCPU_GET(fpcurthread) != NULL) { - printf("npxdna: fpcurthread = %p, curthread = %p\n", - PCPU_GET(fpcurthread), curthread); + printf("npxdna: fpcurthread = %p (%d), curthread = %p (%d)\n", + PCPU_GET(fpcurthread), + PCPU_GET(fpcurthread)->td_proc->p_pid, + curthread, curthread->td_proc->p_pid); panic("npxdna"); } s = intr_disable(); @@ -832,22 +855,35 @@ npxdna() * Record new context early in case frstor causes an IRQ13. */ PCPU_SET(fpcurthread, curthread); + pcb = PCPU_GET(curpcb); - exstat = GET_FPU_EXSW_PTR(PCPU_GET(curpcb)); - *exstat = 0; - /* - * 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. - */ - fpurstor(&PCPU_GET(curpcb)->pcb_save); + if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + /* + * This is the first time this thread has used the FPU or + * the PCB doesn't contain a clean FPU state. Explicitly + * initialize the FPU and load the default control word. + */ + fninit(); + control = __INITIAL_NPXCW__; + fldcw(&control); + pcb->pcb_flags |= PCB_NPXINITDONE; + } else { + /* + * 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. + */ + fpurstor(&pcb->pcb_save); + } intr_restore(s); return (1); @@ -888,6 +924,87 @@ npxsave(addr) PCPU_SET(fpcurthread, NULL); } +/* + * This should be called with interrupts disabled and only when the owning + * FPU thread is non-null. + */ +void +npxdrop() +{ + struct thread *td; + + td = PCPU_GET(fpcurthread); + PCPU_SET(fpcurthread, NULL); + td->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; + start_emulating(); +} + +/* + * Get the state of the FPU without dropping ownership (if possible). + * It returns the FPU ownership status. + */ +int +npxgetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return (_MC_FPOWNED_NONE); + + if ((td->td_pcb->pcb_flags & PCB_NPXINITDONE) == 0) { + if (npx_cleanstate_ready) + bcopy(&npx_cleanstate, addr, sizeof(npx_cleanstate)); + else + bzero(addr, sizeof(*addr)); + return (_MC_FPOWNED_NONE); + } + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpusave(addr); +#ifdef CPU_ENABLE_SSE + if (!cpu_fxsr) +#endif + /* + * fnsave initializes the FPU and destroys whatever + * context it contains. Make sure the FPU owner + * starts with a clean state next time. + */ + npxdrop(); + intr_restore(s); + return (_MC_FPOWNED_FPU); + } else { + intr_restore(s); + bcopy(&td->td_pcb->pcb_save, addr, sizeof(*addr)); + return (_MC_FPOWNED_PCB); + } +} + +/* + * Set the state of the FPU. + */ +void +npxsetregs(td, addr) + struct thread *td; + union savefpu *addr; +{ + register_t s; + + if (!npx_exists) + return; + + s = intr_disable(); + if (curthread == PCPU_GET(fpcurthread)) { + fpurstor(addr); + intr_restore(s); + } else { + intr_restore(s); + bcopy(addr, &td->td_pcb->pcb_save, sizeof(*addr)); + } +} + static void fpusave(addr) union savefpu *addr; |