svn commit: r189423 - in head/sys: amd64/amd64 amd64/ia32 amd64/include amd64/linux32 compat/linux i386/i386 i386/include i386/isa i386/linux

John Baldwin jhb at FreeBSD.org
Thu Mar 5 11:42:12 PST 2009


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


More information about the svn-src-head mailing list