Commit a7baead7 authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini

KVM: x86: Ensure guest's FPU state is loaded when accessing for emulation

Lock the FPU regs and reload the current thread's FPU state, which holds
the guest's FPU state, to the CPU registers if necessary prior to
accessing guest FPU state as part of emulation.  kernel_fpu_begin() can
be called from softirq context, therefore KVM must ensure softirqs are
disabled (locking the FPU regs disables softirqs) when touching CPU FPU
state.

Note, for all intents and purposes this reverts commit 6ab0b9fe
("x86,kvm: remove KVM emulator get_fpu / put_fpu"), but at the time it
was applied, removing get/put_fpu() was correct.  The re-introduction
of {get,put}_fpu() is necessitated by the deferring of FPU state load.

Fixes: 5f409e20 ("x86/fpu: Defer FPU state load until return to userspace")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarSean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent c9aef3b8
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "kvm_cache_regs.h" #include "kvm_cache_regs.h"
#include <asm/kvm_emulate.h> #include <asm/kvm_emulate.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <asm/fpu/api.h>
#include <asm/debugreg.h> #include <asm/debugreg.h>
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
...@@ -1075,8 +1076,23 @@ static void fetch_register_operand(struct operand *op) ...@@ -1075,8 +1076,23 @@ static void fetch_register_operand(struct operand *op)
} }
} }
static void emulator_get_fpu(void)
{
fpregs_lock();
fpregs_assert_state_consistent();
if (test_thread_flag(TIF_NEED_FPU_LOAD))
switch_fpu_return();
}
static void emulator_put_fpu(void)
{
fpregs_unlock();
}
static void read_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, int reg) static void read_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, int reg)
{ {
emulator_get_fpu();
switch (reg) { switch (reg) {
case 0: asm("movdqa %%xmm0, %0" : "=m"(*data)); break; case 0: asm("movdqa %%xmm0, %0" : "=m"(*data)); break;
case 1: asm("movdqa %%xmm1, %0" : "=m"(*data)); break; case 1: asm("movdqa %%xmm1, %0" : "=m"(*data)); break;
...@@ -1098,11 +1114,13 @@ static void read_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, int reg) ...@@ -1098,11 +1114,13 @@ static void read_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, int reg)
#endif #endif
default: BUG(); default: BUG();
} }
emulator_put_fpu();
} }
static void write_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, static void write_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data,
int reg) int reg)
{ {
emulator_get_fpu();
switch (reg) { switch (reg) {
case 0: asm("movdqa %0, %%xmm0" : : "m"(*data)); break; case 0: asm("movdqa %0, %%xmm0" : : "m"(*data)); break;
case 1: asm("movdqa %0, %%xmm1" : : "m"(*data)); break; case 1: asm("movdqa %0, %%xmm1" : : "m"(*data)); break;
...@@ -1124,10 +1142,12 @@ static void write_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data, ...@@ -1124,10 +1142,12 @@ static void write_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data,
#endif #endif
default: BUG(); default: BUG();
} }
emulator_put_fpu();
} }
static void read_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg) static void read_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
{ {
emulator_get_fpu();
switch (reg) { switch (reg) {
case 0: asm("movq %%mm0, %0" : "=m"(*data)); break; case 0: asm("movq %%mm0, %0" : "=m"(*data)); break;
case 1: asm("movq %%mm1, %0" : "=m"(*data)); break; case 1: asm("movq %%mm1, %0" : "=m"(*data)); break;
...@@ -1139,10 +1159,12 @@ static void read_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg) ...@@ -1139,10 +1159,12 @@ static void read_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
case 7: asm("movq %%mm7, %0" : "=m"(*data)); break; case 7: asm("movq %%mm7, %0" : "=m"(*data)); break;
default: BUG(); default: BUG();
} }
emulator_put_fpu();
} }
static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg) static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
{ {
emulator_get_fpu();
switch (reg) { switch (reg) {
case 0: asm("movq %0, %%mm0" : : "m"(*data)); break; case 0: asm("movq %0, %%mm0" : : "m"(*data)); break;
case 1: asm("movq %0, %%mm1" : : "m"(*data)); break; case 1: asm("movq %0, %%mm1" : : "m"(*data)); break;
...@@ -1154,6 +1176,7 @@ static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg) ...@@ -1154,6 +1176,7 @@ static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
case 7: asm("movq %0, %%mm7" : : "m"(*data)); break; case 7: asm("movq %0, %%mm7" : : "m"(*data)); break;
default: BUG(); default: BUG();
} }
emulator_put_fpu();
} }
static int em_fninit(struct x86_emulate_ctxt *ctxt) static int em_fninit(struct x86_emulate_ctxt *ctxt)
...@@ -1161,7 +1184,9 @@ static int em_fninit(struct x86_emulate_ctxt *ctxt) ...@@ -1161,7 +1184,9 @@ static int em_fninit(struct x86_emulate_ctxt *ctxt)
if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
return emulate_nm(ctxt); return emulate_nm(ctxt);
emulator_get_fpu();
asm volatile("fninit"); asm volatile("fninit");
emulator_put_fpu();
return X86EMUL_CONTINUE; return X86EMUL_CONTINUE;
} }
...@@ -1172,7 +1197,9 @@ static int em_fnstcw(struct x86_emulate_ctxt *ctxt) ...@@ -1172,7 +1197,9 @@ static int em_fnstcw(struct x86_emulate_ctxt *ctxt)
if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
return emulate_nm(ctxt); return emulate_nm(ctxt);
emulator_get_fpu();
asm volatile("fnstcw %0": "+m"(fcw)); asm volatile("fnstcw %0": "+m"(fcw));
emulator_put_fpu();
ctxt->dst.val = fcw; ctxt->dst.val = fcw;
...@@ -1186,7 +1213,9 @@ static int em_fnstsw(struct x86_emulate_ctxt *ctxt) ...@@ -1186,7 +1213,9 @@ static int em_fnstsw(struct x86_emulate_ctxt *ctxt)
if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
return emulate_nm(ctxt); return emulate_nm(ctxt);
emulator_get_fpu();
asm volatile("fnstsw %0": "+m"(fsw)); asm volatile("fnstsw %0": "+m"(fsw));
emulator_put_fpu();
ctxt->dst.val = fsw; ctxt->dst.val = fsw;
...@@ -4077,8 +4106,12 @@ static int em_fxsave(struct x86_emulate_ctxt *ctxt) ...@@ -4077,8 +4106,12 @@ static int em_fxsave(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
emulator_get_fpu();
rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state)); rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state));
emulator_put_fpu();
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
...@@ -4121,6 +4154,8 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt) ...@@ -4121,6 +4154,8 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
emulator_get_fpu();
if (size < __fxstate_size(16)) { if (size < __fxstate_size(16)) {
rc = fxregs_fixup(&fx_state, size); rc = fxregs_fixup(&fx_state, size);
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
...@@ -4136,6 +4171,8 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt) ...@@ -4136,6 +4171,8 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state)); rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state));
out: out:
emulator_put_fpu();
return rc; return rc;
} }
...@@ -5450,7 +5487,9 @@ static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt) ...@@ -5450,7 +5487,9 @@ static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
{ {
int rc; int rc;
emulator_get_fpu();
rc = asm_safe("fwait"); rc = asm_safe("fwait");
emulator_put_fpu();
if (unlikely(rc != X86EMUL_CONTINUE)) if (unlikely(rc != X86EMUL_CONTINUE))
return emulate_exception(ctxt, MF_VECTOR, 0, false); return emulate_exception(ctxt, MF_VECTOR, 0, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment