Date: Thu, 5 Mar 2009 19:42:11 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r189423 - in head/sys: amd64/amd64 amd64/ia32 amd64/include amd64/linux32 compat/linux i386/i386 i386/include i386/isa i386/linux Message-ID: <200903051942.n25JgBh2097613@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jhb Date: Thu Mar 5 19:42:11 2009 New Revision: 189423 URL: http://svn.freebsd.org/changeset/base/189423 Log: A better fix for handling different FPU initial control words for different ABIs: - Store the FPU initial control word in the pcb for each thread. - When first using the FPU, load the initial control word after restoring the clean state if it is not the standard control word. - Provide a correct control word for Linux/i386 binaries under FreeBSD/amd64. - Adjust the control word returned for fpugetregs()/npxgetregs() when a thread hasn't used the FPU yet to reflect the real initial control word for the current ABI. - The Linux/i386 ABI for FreeBSD/i386 now properly sets the right control word instead of trashing whatever the current state of the FPU is. Reviewed by: bde Modified: head/sys/amd64/amd64/fpu.c head/sys/amd64/amd64/machdep.c head/sys/amd64/ia32/ia32_signal.c head/sys/amd64/include/pcb.h head/sys/amd64/linux32/linux32_sysvec.c head/sys/compat/linux/linux_misc.h head/sys/i386/i386/machdep.c head/sys/i386/include/pcb.h head/sys/i386/isa/npx.c head/sys/i386/linux/linux_sysvec.c Modified: head/sys/amd64/amd64/fpu.c ============================================================================== --- head/sys/amd64/amd64/fpu.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/amd64/amd64/fpu.c Thu Mar 5 19:42:11 2009 (r189423) @@ -390,7 +390,6 @@ fpudna(void) { struct pcb *pcb; register_t s; - u_short control; if (PCPU_GET(fpcurthread) == curthread) { printf("fpudna: fpcurthread == curthread %d times\n", @@ -421,10 +420,8 @@ fpudna(void) * explicitly load sanitized registers. */ fxrstor(&fpu_cleanstate); - if (pcb->pcb_flags & PCB_32BIT) { - control = __INITIAL_FPUCW_I386__; - fldcw(&control); - } + if (pcb->pcb_initial_fpucw != __INITIAL_FPUCW__) + fldcw(&pcb->pcb_initial_fpucw); pcb->pcb_flags |= PCB_FPUINITDONE; } else fxrstor(&pcb->pcb_save); @@ -457,6 +454,7 @@ fpugetregs(struct thread *td, struct sav if ((td->td_pcb->pcb_flags & PCB_FPUINITDONE) == 0) { 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: head/sys/amd64/amd64/machdep.c ============================================================================== --- head/sys/amd64/amd64/machdep.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/amd64/amd64/machdep.c Thu Mar 5 19:42:11 2009 (r189423) @@ -716,7 +716,7 @@ SYSCTL_PROC(_machdep, OID_AUTO, idle, CT idle_sysctl, "A", "currently selected idle function"); /* - * Clear registers on exec + * Reset registers to default values on exec. */ void exec_setregs(td, entry, stack, ps_strings) @@ -743,6 +743,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: head/sys/amd64/ia32/ia32_signal.c ============================================================================== --- head/sys/amd64/ia32/ia32_signal.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/amd64/ia32/ia32_signal.c Thu Mar 5 19:42:11 2009 (r189423) @@ -729,6 +729,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: head/sys/amd64/include/pcb.h ============================================================================== --- head/sys/amd64/include/pcb.h Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/amd64/include/pcb.h Thu Mar 5 19:42:11 2009 (r189423) @@ -74,6 +74,7 @@ struct pcb { u_int64_t pcb_dr7; struct savefpu pcb_save; + uint16_t pcb_initial_fpucw; caddr_t pcb_onfault; /* copyin/out fault recovery */ Modified: head/sys/amd64/linux32/linux32_sysvec.c ============================================================================== --- head/sys/amd64/linux32/linux32_sysvec.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/amd64/linux32/linux32_sysvec.c Thu Mar 5 19:42:11 2009 (r189423) @@ -841,6 +841,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: head/sys/compat/linux/linux_misc.h ============================================================================== --- head/sys/compat/linux/linux_misc.h Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/compat/linux/linux_misc.h Thu Mar 5 19:42:11 2009 (r189423) @@ -60,4 +60,9 @@ extern const char *linux_platform; */ #define LINUX_AT_EXECFN 31 /* filename of program */ +/* Linux sets the i387 to extended precision. */ +#if defined(__i386__) || defined(__amd64__) +#define __LINUX_NPXCW__ 0x37f +#endif + #endif /* _LINUX_MISC_H_ */ Modified: head/sys/i386/i386/machdep.c ============================================================================== --- head/sys/i386/i386/machdep.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/i386/i386/machdep.c Thu Mar 5 19:42:11 2009 (r189423) @@ -1362,7 +1362,7 @@ SYSCTL_PROC(_machdep, OID_AUTO, idle, CT idle_sysctl, "A", "currently selected idle function"); /* - * Clear registers on exec + * Reset registers to default values on exec. */ void exec_setregs(td, entry, stack, ps_strings) @@ -1427,6 +1427,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: head/sys/i386/include/pcb.h ============================================================================== --- head/sys/i386/include/pcb.h Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/i386/include/pcb.h Thu Mar 5 19:42:11 2009 (r189423) @@ -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: head/sys/i386/isa/npx.c ============================================================================== --- head/sys/i386/isa/npx.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/i386/isa/npx.c Thu Mar 5 19:42:11 2009 (r189423) @@ -141,11 +141,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; @@ -793,6 +801,8 @@ npxdna(void) * load sanitized registers. */ fpurstor(&npx_cleanstate); + if (pcb->pcb_initial_npxcw != __INITIAL_NPXCW__) + fldcw(&pcb->pcb_initial_npxcw); pcb->pcb_flags |= PCB_NPXINITDONE; } else { /* @@ -891,6 +901,7 @@ npxgetregs(td, addr) if ((td->td_pcb->pcb_flags & PCB_NPXINITDONE) == 0) { 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: head/sys/i386/linux/linux_sysvec.c ============================================================================== --- head/sys/i386/linux/linux_sysvec.c Thu Mar 5 19:20:17 2009 (r189422) +++ head/sys/i386/linux/linux_sysvec.c Thu Mar 5 19:42:11 2009 (r189423) @@ -89,9 +89,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; @@ -930,16 +927,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__; } static void
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903051942.n25JgBh2097613>