diff options
author | jhb <jhb@FreeBSD.org> | 2009-03-05 19:42:11 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2009-03-05 19:42:11 +0000 |
commit | e1b708897ea547f25fbc5fc1f73bb2b0738c5757 (patch) | |
tree | 156c16702c286b02c436fdf8c8a1140bab925e8b /sys/amd64 | |
parent | 217c09dffc7d5e0d80e98fe648855f9e3c1eee22 (diff) | |
download | FreeBSD-src-e1b708897ea547f25fbc5fc1f73bb2b0738c5757.zip FreeBSD-src-e1b708897ea547f25fbc5fc1f73bb2b0738c5757.tar.gz |
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
Diffstat (limited to 'sys/amd64')
-rw-r--r-- | sys/amd64/amd64/fpu.c | 8 | ||||
-rw-r--r-- | sys/amd64/amd64/machdep.c | 3 | ||||
-rw-r--r-- | sys/amd64/ia32/ia32_signal.c | 1 | ||||
-rw-r--r-- | sys/amd64/include/pcb.h | 1 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_sysvec.c | 1 |
5 files changed, 8 insertions, 6 deletions
diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c index 111d6d8..8f16c46 100644 --- a/sys/amd64/amd64/fpu.c +++ b/sys/amd64/amd64/fpu.c @@ -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(); diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 2bfde47..6ba3820 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -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; diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c index d3b9ae3..019faba 100644 --- a/sys/amd64/ia32/ia32_signal.c +++ b/sys/amd64/ia32/ia32_signal.c @@ -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; diff --git a/sys/amd64/include/pcb.h b/sys/amd64/include/pcb.h index ac14c55..e6a5add 100644 --- a/sys/amd64/include/pcb.h +++ b/sys/amd64/include/pcb.h @@ -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 */ diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index 312687a..a834c98 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -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; |