Date: Wed, 25 Mar 2009 17:22:15 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org Subject: svn commit: r190418 - in stable/7/sys: . amd64/amd64 amd64/ia32 amd64/include amd64/linux32 compat/linux contrib/pf dev/ath/ath_hal dev/cxgb i386/i386 i386/include i386/isa i386/linux Message-ID: <200903251722.n2PHMFLg082656@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jhb Date: Wed Mar 25 17:22:15 2009 New Revision: 190418 URL: http://svn.freebsd.org/changeset/base/190418 Log: Allow different ABIs to use different initial control words for the FPU on amd64 and i386. This fixes a bug were 32-bit binaries would run with a different floating point exception mask under FreeBSD/amd64 vs FreeBSD/i386. This commit also includes a few other minor changes to keep the code in sync with 8. Approved by: re (kib) Modified: stable/7/sys/ (props changed) stable/7/sys/amd64/amd64/fpu.c stable/7/sys/amd64/amd64/machdep.c stable/7/sys/amd64/amd64/trap.c stable/7/sys/amd64/ia32/ia32_signal.c stable/7/sys/amd64/include/fpu.h stable/7/sys/amd64/include/pcb.h stable/7/sys/amd64/linux32/linux32_sysvec.c stable/7/sys/compat/linux/linux_misc.h stable/7/sys/contrib/pf/ (props changed) stable/7/sys/dev/ath/ath_hal/ (props changed) stable/7/sys/dev/cxgb/ (props changed) stable/7/sys/i386/i386/machdep.c stable/7/sys/i386/i386/mp_machdep.c stable/7/sys/i386/include/npx.h stable/7/sys/i386/include/pcb.h stable/7/sys/i386/isa/npx.c stable/7/sys/i386/linux/linux_sysvec.c Modified: stable/7/sys/amd64/amd64/fpu.c ============================================================================== --- stable/7/sys/amd64/amd64/fpu.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/amd64/fpu.c Wed Mar 25 17:22:15 2009 (r190418) @@ -98,16 +98,15 @@ typedef u_char bool_t; static void fpu_clean_state(void); -int hw_float = 1; -SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, - CTLFLAG_RD, &hw_float, 0, - "Floatingpoint instructions executed in hardware"); +SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, + NULL, 1, "Floating point instructions executed in hardware"); static struct savefpu fpu_cleanstate; -static bool_t fpu_cleanstate_ready; /* - * Initialize floating point unit. + * Initialize the floating point unit. On the boot CPU we generate a + * clean state that is used to initialize the floating point unit when + * it is first used by a process. */ void fpuinit(void) @@ -117,22 +116,22 @@ fpuinit(void) u_short control; savecrit = intr_disable(); - PCPU_SET(fpcurthread, 0); stop_emulating(); fninit(); control = __INITIAL_FPUCW__; fldcw(&control); mxcsr = __INITIAL_MXCSR__; ldmxcsr(mxcsr); - fxsave(&fpu_cleanstate); - if (fpu_cleanstate.sv_env.en_mxcsr_mask) - cpu_mxcsr_mask = fpu_cleanstate.sv_env.en_mxcsr_mask; - else - cpu_mxcsr_mask = 0xFFBF; + if (PCPU_GET(cpuid) == 0) { + fxsave(&fpu_cleanstate); + if (fpu_cleanstate.sv_env.en_mxcsr_mask) + cpu_mxcsr_mask = fpu_cleanstate.sv_env.en_mxcsr_mask; + else + cpu_mxcsr_mask = 0xFFBF; + bzero(fpu_cleanstate.sv_fp, sizeof(fpu_cleanstate.sv_fp)); + bzero(fpu_cleanstate.sv_xmm, sizeof(fpu_cleanstate.sv_xmm)); + } start_emulating(); - bzero(fpu_cleanstate.sv_fp, sizeof(fpu_cleanstate.sv_fp)); - bzero(fpu_cleanstate.sv_xmm, sizeof(fpu_cleanstate.sv_xmm)); - fpu_cleanstate_ready = 1; intr_restore(savecrit); } @@ -386,8 +385,8 @@ fputrap() static int err_count = 0; -int -fpudna() +void +fpudna(void) { struct pcb *pcb; register_t s; @@ -396,7 +395,7 @@ fpudna() printf("fpudna: fpcurthread == curthread %d times\n", ++err_count); stop_emulating(); - return (1); + return; } if (PCPU_GET(fpcurthread) != NULL) { printf("fpudna: fpcurthread = %p (%d), curthread = %p (%d)\n", @@ -421,12 +420,12 @@ fpudna() * explicitly load sanitized registers. */ fxrstor(&fpu_cleanstate); + if (pcb->pcb_initial_fpucw != __INITIAL_FPUCW__) + fldcw(&pcb->pcb_initial_fpucw); pcb->pcb_flags |= PCB_FPUINITDONE; } else fxrstor(&pcb->pcb_save); intr_restore(s); - - return (1); } /* @@ -454,10 +453,8 @@ fpugetregs(struct thread *td, struct sav register_t s; if ((td->td_pcb->pcb_flags & PCB_FPUINITDONE) == 0) { - if (fpu_cleanstate_ready) - bcopy(&fpu_cleanstate, addr, sizeof(fpu_cleanstate)); - else - bzero(addr, sizeof(*addr)); + bcopy(&fpu_cleanstate, addr, sizeof(fpu_cleanstate)); + addr->sv_env.en_cw = td->td_pcb->pcb_initial_fpucw; return (_MC_FPOWNED_NONE); } s = intr_disable(); Modified: stable/7/sys/amd64/amd64/machdep.c ============================================================================== --- stable/7/sys/amd64/amd64/machdep.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/amd64/machdep.c Wed Mar 25 17:22:15 2009 (r190418) @@ -586,7 +586,7 @@ cpu_idle(void) void (*cpu_idle_hook)(void) = cpu_idle_default; /* - * Clear registers on exec + * Reset registers to default values on exec. */ void exec_setregs(td, entry, stack, ps_strings) @@ -613,6 +613,7 @@ exec_setregs(td, entry, stack, ps_string pcb->pcb_es = _udatasel; pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; + pcb->pcb_initial_fpucw = __INITIAL_FPUCW__; bzero((char *)regs, sizeof(struct trapframe)); regs->tf_rip = entry; Modified: stable/7/sys/amd64/amd64/trap.c ============================================================================== --- stable/7/sys/amd64/amd64/trap.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/amd64/trap.c Wed Mar 25 17:22:15 2009 (r190418) @@ -421,13 +421,8 @@ trap(struct trapframe *frame) case T_DNA: /* transparent fault (due to context switch "late") */ - if (fpudna()) - goto userout; - printf("pid %d killed due to lack of floating point\n", - p->p_pid); - i = SIGKILL; - ucode = 0; - break; + fpudna(); + goto userout; case T_FPOPFLT: /* FPU operand fetch fault */ ucode = ILL_COPROC; @@ -455,11 +450,9 @@ trap(struct trapframe *frame) * XXX this should be fatal unless the kernel has * registered such use. */ - if (fpudna()) { - printf("fpudna in kernel mode!\n"); - goto out; - } - break; + fpudna(); + printf("fpudna in kernel mode!\n"); + goto out; case T_STKFLT: /* stack fault */ break; Modified: stable/7/sys/amd64/ia32/ia32_signal.c ============================================================================== --- stable/7/sys/amd64/ia32/ia32_signal.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/ia32/ia32_signal.c Wed Mar 25 17:22:15 2009 (r190418) @@ -727,6 +727,7 @@ ia32_setregs(td, entry, stack, ps_string pcb->pcb_es = _udatasel; pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; + pcb->pcb_initial_fpucw = __INITIAL_FPUCW_I386__; bzero((char *)regs, sizeof(struct trapframe)); regs->tf_rip = entry; Modified: stable/7/sys/amd64/include/fpu.h ============================================================================== --- stable/7/sys/amd64/include/fpu.h Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/include/fpu.h Wed Mar 25 17:22:15 2009 (r190418) @@ -92,11 +92,12 @@ struct savefpu { * SSE2 based math. For FreeBSD/amd64, we go with the default settings. */ #define __INITIAL_FPUCW__ 0x037F +#define __INITIAL_FPUCW_I386__ 0x127F #define __INITIAL_MXCSR__ 0x1F80 #define __INITIAL_MXCSR_MASK__ 0xFFBF #ifdef _KERNEL -int fpudna(void); +void fpudna(void); void fpudrop(void); void fpuexit(struct thread *td); int fpuformat(void); Modified: stable/7/sys/amd64/include/pcb.h ============================================================================== --- stable/7/sys/amd64/include/pcb.h Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/include/pcb.h Wed Mar 25 17:22:15 2009 (r190418) @@ -55,6 +55,13 @@ struct pcb { register_t pcb_rip; register_t pcb_fsbase; register_t pcb_gsbase; + u_long pcb_flags; +#define PCB_DBREGS 0x02 /* process using debug registers */ +#define PCB_FPUINITDONE 0x08 /* fpu state is initialized */ +#define PCB_GS32BIT 0x20 /* linux gs switch */ +#define PCB_32BIT 0x40 /* process has 32 bit context (segs etc) */ +#define PCB_FULLCTX 0x80 /* full context restore on sysret */ + u_int32_t pcb_ds; u_int32_t pcb_es; u_int32_t pcb_fs; @@ -67,12 +74,7 @@ struct pcb { u_int64_t pcb_dr7; struct savefpu pcb_save; - u_long pcb_flags; -#define PCB_DBREGS 0x02 /* process using debug registers */ -#define PCB_FPUINITDONE 0x08 /* fpu state is initialized */ -#define PCB_GS32BIT 0x20 /* linux gs switch */ -#define PCB_32BIT 0x40 /* process has 32 bit context (segs etc) */ -#define PCB_FULLCTX 0x80 /* full context restore on sysret */ + uint16_t pcb_initial_fpucw; caddr_t pcb_onfault; /* copyin/out fault recovery */ Modified: stable/7/sys/amd64/linux32/linux32_sysvec.c ============================================================================== --- stable/7/sys/amd64/linux32/linux32_sysvec.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/amd64/linux32/linux32_sysvec.c Wed Mar 25 17:22:15 2009 (r190418) @@ -832,6 +832,7 @@ exec_linux_setregs(td, entry, stack, ps_ pcb->pcb_es = _udatasel; pcb->pcb_fs = _udatasel; pcb->pcb_gs = _udatasel; + pcb->pcb_initial_fpucw = __LINUX_NPXCW__; bzero((char *)regs, sizeof(struct trapframe)); regs->tf_rip = entry; Modified: stable/7/sys/compat/linux/linux_misc.h ============================================================================== --- stable/7/sys/compat/linux/linux_misc.h Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/compat/linux/linux_misc.h Wed Mar 25 17:22:15 2009 (r190418) @@ -45,4 +45,9 @@ #define LINUX_MREMAP_MAYMOVE 1 #define LINUX_MREMAP_FIXED 2 +/* Linux sets the i387 to extended precision. */ +#if defined(__i386__) || defined(__amd64__) +#define __LINUX_NPXCW__ 0x37f +#endif + #endif /* _LINUX_MISC_H_ */ Modified: stable/7/sys/i386/i386/machdep.c ============================================================================== --- stable/7/sys/i386/i386/machdep.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/i386/machdep.c Wed Mar 25 17:22:15 2009 (r190418) @@ -1188,7 +1188,7 @@ cpu_idle(void) void (*cpu_idle_hook)(void) = cpu_idle_default; /* - * Clear registers on exec + * Reset registers to default values on exec. */ void exec_setregs(td, entry, stack, ps_strings) @@ -1253,6 +1253,7 @@ exec_setregs(td, entry, stack, ps_string * emulators don't provide an entry point for initialization. */ td->td_pcb->pcb_flags &= ~FP_SOFTFP; + pcb->pcb_initial_npxcw = __INITIAL_NPXCW__; /* * Drop the FP state if we hold it, so that the process gets a Modified: stable/7/sys/i386/i386/mp_machdep.c ============================================================================== --- stable/7/sys/i386/i386/mp_machdep.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/i386/mp_machdep.c Wed Mar 25 17:22:15 2009 (r190418) @@ -575,7 +575,7 @@ init_secondary(void) cpu_setregs(); /* set up FPU state on the AP */ - npxinit(__INITIAL_NPXCW__); + npxinit(); /* set up SSE registers */ enable_sse(); Modified: stable/7/sys/i386/include/npx.h ============================================================================== --- stable/7/sys/i386/include/npx.h Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/include/npx.h Wed Mar 25 17:22:15 2009 (r190418) @@ -151,7 +151,7 @@ void npxdrop(void); void npxexit(struct thread *td); int npxformat(void); int npxgetregs(struct thread *td, union savefpu *addr); -void npxinit(u_short control); +void npxinit(void); void npxsave(union savefpu *addr); void npxsetregs(struct thread *td, union savefpu *addr); int npxtrap(void); Modified: stable/7/sys/i386/include/pcb.h ============================================================================== --- stable/7/sys/i386/include/pcb.h Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/include/pcb.h Wed Mar 25 17:22:15 2009 (r190418) @@ -61,6 +61,7 @@ struct pcb { int pcb_dr7; union savefpu pcb_save; + uint16_t pcb_initial_npxcw; u_int pcb_flags; #define FP_SOFTFP 0x01 /* process using software fltng pnt emulator */ #define PCB_DBREGS 0x02 /* process using debug registers */ Modified: stable/7/sys/i386/isa/npx.c ============================================================================== --- stable/7/sys/i386/isa/npx.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/isa/npx.c Wed Mar 25 17:22:15 2009 (r190418) @@ -132,11 +132,19 @@ 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 SET_FPU_CW(savefpu, value) do { \ + if (cpu_fxsr) \ + (savefpu)->sv_xmm.sv_env.en_cw = (value); \ + else \ + (savefpu)->sv_87.sv_env.en_cw = (value); \ +} while (0) #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 SET_FPU_CW(savefpu, value) \ + (savefpu)->sv_87.sv_env.en_cw = (value) #endif /* CPU_ENABLE_SSE */ typedef u_char bool_t; @@ -158,15 +166,13 @@ static long timezero(const char *funcnam int hw_float; /* XXX currently just alias for npx_exists */ -SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint, - CTLFLAG_RD, &hw_float, 0, - "Floatingpoint instructions executed in hardware"); +SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, + &hw_float, 0, "Floating point instructions executed in hardware"); static volatile u_int npx_intrs_while_probing; static volatile u_int npx_traps_while_probing; 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; @@ -368,19 +374,14 @@ npx_probe(dev) return (0); } /* - * Worse, even IRQ13 is broken. Use emulator. + * Worse, even IRQ13 is broken. */ } } - /* - * 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. - */ -#ifdef SMP - if (mp_ncpus > 1) - panic("npx0 cannot be emulated on an SMP system"); -#endif + + /* Probe failed. Floating point simply won't work. */ + device_printf(dev, "WARNING: no FPU!\n"); + /* FALLTHROUGH */ no_irq13: idt[IDT_MF] = save_idt_npxtrap; @@ -389,7 +390,7 @@ no_irq13: bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res); } bus_release_resource(dev, SYS_RES_IOPORT, ioport_rid, ioport_res); - return (0); + return (npx_exists ? 0 : ENXIO); } /* @@ -406,32 +407,34 @@ npx_attach(dev) if (npx_irq13) device_printf(dev, "IRQ 13 interface\n"); - else if (!npx_ex16) - device_printf(dev, "WARNING: no FPU!\n"); else if (!device_is_quiet(dev) || bootverbose) device_printf(dev, "INT 16 interface\n"); - npxinit(__INITIAL_NPXCW__); + npxinit(); - if (npx_cleanstate_ready == 0) { - s = intr_disable(); - stop_emulating(); - fpusave(&npx_cleanstate); - start_emulating(); + s = intr_disable(); + stop_emulating(); + fpusave(&npx_cleanstate); + start_emulating(); #ifdef CPU_ENABLE_SSE - if (cpu_fxsr) { - if (npx_cleanstate.sv_xmm.sv_env.en_mxcsr_mask) - cpu_mxcsr_mask = - npx_cleanstate.sv_xmm.sv_env.en_mxcsr_mask; - else - cpu_mxcsr_mask = 0xFFBF; - } + if (cpu_fxsr) { + if (npx_cleanstate.sv_xmm.sv_env.en_mxcsr_mask) + cpu_mxcsr_mask = + npx_cleanstate.sv_xmm.sv_env.en_mxcsr_mask; + else + cpu_mxcsr_mask = 0xFFBF; + bzero(npx_cleanstate.sv_xmm.sv_fp, + sizeof(npx_cleanstate.sv_xmm.sv_fp)); + bzero(npx_cleanstate.sv_xmm.sv_xmm, + sizeof(npx_cleanstate.sv_xmm.sv_xmm)); + /* XXX might need even more zeroing. */ + } else #endif - npx_cleanstate_ready = 1; - intr_restore(s); - } + bzero(npx_cleanstate.sv_87.sv_ac, + sizeof(npx_cleanstate.sv_87.sv_ac)); + intr_restore(s); #ifdef I586_CPU_XXX - if (cpu_class == CPUCLASS_586 && npx_ex16 && npx_exists && + if (cpu_class == CPUCLASS_586 && npx_ex16 && timezero("i586_bzero()", i586_bzero) < timezero("bzero()", bzero) * 4 / 5) { if (!(flags & NPX_DISABLE_I586_OPTIMIZED_BCOPY)) @@ -452,10 +455,11 @@ npx_attach(dev) * Initialize floating point unit. */ void -npxinit(u_short control) +npxinit(void) { static union savefpu dummy; register_t savecrit; + u_short control; if (!npx_exists) return; @@ -472,6 +476,7 @@ npxinit(u_short control) if (cpu_fxsr) fninit(); #endif + control = __INITIAL_NPXCW__; fldcw(&control); start_emulating(); intr_restore(savecrit); @@ -752,14 +757,10 @@ npxtrap() static int err_count = 0; int -npxdna() +npxdna(void) { struct pcb *pcb; register_t s; -#ifdef CPU_ENABLE_SSE - int mxcsr; -#endif - u_short control; if (!npx_exists) return (0); @@ -788,22 +789,16 @@ npxdna() /* * 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. + * load sanitized registers. */ - fninit(); - control = __INITIAL_NPXCW__; - fldcw(&control); -#ifdef CPU_ENABLE_SSE - if (cpu_fxsr) { - mxcsr = __INITIAL_MXCSR__; - ldmxcsr(mxcsr); - } -#endif + fpurstor(&npx_cleanstate); + if (pcb->pcb_initial_npxcw != __INITIAL_NPXCW__) + fldcw(&pcb->pcb_initial_npxcw); 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 + * The following fpurstor() 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., @@ -896,10 +891,8 @@ npxgetregs(td, addr) 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)); + bcopy(&npx_cleanstate, addr, sizeof(npx_cleanstate)); + SET_FPU_CW(addr, td->td_pcb->pcb_initial_npxcw); return (_MC_FPOWNED_NONE); } s = intr_disable(); Modified: stable/7/sys/i386/linux/linux_sysvec.c ============================================================================== --- stable/7/sys/i386/linux/linux_sysvec.c Wed Mar 25 17:02:05 2009 (r190417) +++ stable/7/sys/i386/linux/linux_sysvec.c Wed Mar 25 17:22:15 2009 (r190418) @@ -86,9 +86,6 @@ MALLOC_DEFINE(M_LINUX, "linux", "Linux m #define LINUX_SYS_linux_rt_sendsig 0 #define LINUX_SYS_linux_sendsig 0 -#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr))) -#define __LINUX_NPXCW__ 0x37f - extern char linux_sigcode[]; extern int linux_szsigcode; @@ -800,16 +797,15 @@ static void exec_linux_setregs(struct thread *td, u_long entry, u_long stack, u_long ps_strings) { - static const u_short control = __LINUX_NPXCW__; struct pcb *pcb = td->td_pcb; exec_setregs(td, entry, stack, ps_strings); /* Linux sets %gs to 0, we default to _udatasel */ - pcb->pcb_gs = 0; load_gs(0); + pcb->pcb_gs = 0; + load_gs(0); - /* Linux sets the i387 to extended precision. */ - fldcw(&control); + pcb->pcb_initial_npxcw = __LINUX_NPXCW__; } struct sysentvec linux_sysvec = {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903251722.n2PHMFLg082656>