summaryrefslogtreecommitdiff
path: root/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/include/asm/fpu/internal.h34
-rw-r--r--arch/x86/kernel/fpu/core.c4
-rw-r--r--arch/x86/kernel/traps.c2
-rw-r--r--arch/x86/kvm/x86.c2
-rw-r--r--arch/x86/mm/mpx.c2
5 files changed, 29 insertions, 15 deletions
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
index 89c6ec80c1ac..11055f51e67a 100644
--- a/arch/x86/include/asm/fpu/internal.h
+++ b/arch/x86/include/asm/fpu/internal.h
@@ -265,9 +265,15 @@ static inline void fpu_fxsave(struct fpu *fpu)
/*
* These must be called with preempt disabled. Returns
- * 'true' if the FPU state is still intact.
+ * 'true' if the FPU state is still intact and we can
+ * keep registers active.
+ *
+ * The legacy FNSAVE instruction cleared all FPU state
+ * unconditionally, so registers are essentially destroyed.
+ * Modern FPU state can be kept in registers, if there are
+ * no pending FP exceptions. (Note the FIXME below.)
*/
-static inline int fpu_save_init(struct fpu *fpu)
+static inline int copy_fpregs_to_fpstate(struct fpu *fpu)
{
if (use_xsave()) {
xsave_state(&fpu->state->xsave);
@@ -276,13 +282,16 @@ static inline int fpu_save_init(struct fpu *fpu)
* xsave header may indicate the init state of the FP.
*/
if (!(fpu->state->xsave.header.xfeatures & XSTATE_FP))
- return 1;
- } else if (use_fxsr()) {
- fpu_fxsave(fpu);
+ goto keep_fpregs;
} else {
- asm volatile("fnsave %[fx]; fwait"
- : [fx] "=m" (fpu->state->fsave));
- return 0;
+ if (use_fxsr()) {
+ fpu_fxsave(fpu);
+ } else {
+ /* FNSAVE always clears FPU registers: */
+ asm volatile("fnsave %[fx]; fwait"
+ : [fx] "=m" (fpu->state->fsave));
+ goto drop_fpregs;
+ }
}
/*
@@ -295,9 +304,14 @@ static inline int fpu_save_init(struct fpu *fpu)
*/
if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) {
asm volatile("fnclex");
- return 0;
+ goto drop_fpregs;
}
+
+keep_fpregs:
return 1;
+
+drop_fpregs:
+ return 0;
}
extern void fpu__save(struct fpu *fpu);
@@ -448,7 +462,7 @@ switch_fpu_prepare(struct fpu *old_fpu, struct fpu *new_fpu, int cpu)
(use_eager_fpu() || new_fpu->counter > 5);
if (old_fpu->fpregs_active) {
- if (!fpu_save_init(old_fpu))
+ if (!copy_fpregs_to_fpstate(old_fpu))
old_fpu->last_cpu = -1;
else
old_fpu->last_cpu = cpu;
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index b20d4ea8e132..ca88608a62a5 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -102,7 +102,7 @@ void __kernel_fpu_begin(void)
kernel_fpu_disable();
if (fpu->fpregs_active) {
- fpu_save_init(fpu);
+ copy_fpregs_to_fpstate(fpu);
} else {
this_cpu_write(fpu_fpregs_owner_ctx, NULL);
if (!use_eager_fpu())
@@ -196,7 +196,7 @@ void fpu__save(struct fpu *fpu)
if (use_eager_fpu()) {
__save_fpu(fpu);
} else {
- fpu_save_init(fpu);
+ copy_fpregs_to_fpstate(fpu);
fpregs_deactivate(fpu);
}
}
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index a65586edbb57..f028f1da3480 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -395,7 +395,7 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
* It is not directly accessible, though, so we need to
* do an xsave and then pull it out of the xsave buffer.
*/
- fpu_save_init(&tsk->thread.fpu);
+ copy_fpregs_to_fpstate(&tsk->thread.fpu);
xsave_buf = &(tsk->thread.fpu.state->xsave);
bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR);
if (!bndcsr)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0b58b9397098..d90bf4afa2b0 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7058,7 +7058,7 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
return;
vcpu->guest_fpu_loaded = 0;
- fpu_save_init(&vcpu->arch.guest_fpu);
+ copy_fpregs_to_fpstate(&vcpu->arch.guest_fpu);
__kernel_fpu_end();
++vcpu->stat.fpu_reload;
kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu);
diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c
index 5563be313fd6..3287215be60a 100644
--- a/arch/x86/mm/mpx.c
+++ b/arch/x86/mm/mpx.c
@@ -357,7 +357,7 @@ static __user void *task_get_bounds_dir(struct task_struct *tsk)
* The bounds directory pointer is stored in a register
* only accessible if we first do an xsave.
*/
- fpu_save_init(&tsk->thread.fpu);
+ copy_fpregs_to_fpstate(&tsk->thread.fpu);
bndcsr = get_xsave_addr(&tsk->thread.fpu.state->xsave, XSTATE_BNDCSR);
if (!bndcsr)
return MPX_INVALID_BOUNDS_DIR;