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
This commit is contained in:
parent
217c09dffc
commit
e1b708897e
@ -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 savefpu *addr)
|
||||
|
||||
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();
|
||||
|
@ -716,7 +716,7 @@ SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0,
|
||||
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_strings)
|
||||
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;
|
||||
|
@ -729,6 +729,7 @@ ia32_setregs(td, entry, stack, ps_strings)
|
||||
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;
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -841,6 +841,7 @@ exec_linux_setregs(td, entry, stack, ps_strings)
|
||||
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;
|
||||
|
@ -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_ */
|
||||
|
@ -1362,7 +1362,7 @@ SYSCTL_PROC(_machdep, OID_AUTO, idle, CTLTYPE_STRING | CTLFLAG_RW, 0, 0,
|
||||
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_strings)
|
||||
* 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
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -89,9 +89,6 @@ MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures");
|
||||
#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
|
||||
|
Loading…
x
Reference in New Issue
Block a user