KVM: x86: Move FPU register accessors into fpu.h

Hyper-v XMM fast hypercalls use XMM registers to pass input/output
parameters. To access these, hyperv.c can reuse some FPU register
accessors defined in emulator.c. Move them to a common location so both
can access them.

While at it, reorder the parameters of these accessor methods to make
them more readable.

Cc: Alexander Graf <graf@amazon.com>
Cc: Evgeny Iakovlev <eyakovl@amazon.de>
Signed-off-by: Siddharth Chandrasekaran <sidcha@amazon.de>
Message-Id: <01a85a6560714d4d3637d3d86e5eba65073318fa.1622019133.git.sidcha@amazon.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Siddharth Chandrasekaran 2021-05-26 10:56:08 +02:00 committed by Paolo Bonzini
parent a9d6496d66
commit 43e5146436
3 changed files with 158 additions and 122 deletions

View File

@ -22,7 +22,6 @@
#include "kvm_cache_regs.h" #include "kvm_cache_regs.h"
#include "kvm_emulate.h" #include "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>
@ -1081,116 +1080,14 @@ 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(sse128_t *data, int reg)
{
emulator_get_fpu();
switch (reg) {
case 0: asm("movdqa %%xmm0, %0" : "=m"(*data)); break;
case 1: asm("movdqa %%xmm1, %0" : "=m"(*data)); break;
case 2: asm("movdqa %%xmm2, %0" : "=m"(*data)); break;
case 3: asm("movdqa %%xmm3, %0" : "=m"(*data)); break;
case 4: asm("movdqa %%xmm4, %0" : "=m"(*data)); break;
case 5: asm("movdqa %%xmm5, %0" : "=m"(*data)); break;
case 6: asm("movdqa %%xmm6, %0" : "=m"(*data)); break;
case 7: asm("movdqa %%xmm7, %0" : "=m"(*data)); break;
#ifdef CONFIG_X86_64
case 8: asm("movdqa %%xmm8, %0" : "=m"(*data)); break;
case 9: asm("movdqa %%xmm9, %0" : "=m"(*data)); break;
case 10: asm("movdqa %%xmm10, %0" : "=m"(*data)); break;
case 11: asm("movdqa %%xmm11, %0" : "=m"(*data)); break;
case 12: asm("movdqa %%xmm12, %0" : "=m"(*data)); break;
case 13: asm("movdqa %%xmm13, %0" : "=m"(*data)); break;
case 14: asm("movdqa %%xmm14, %0" : "=m"(*data)); break;
case 15: asm("movdqa %%xmm15, %0" : "=m"(*data)); break;
#endif
default: BUG();
}
emulator_put_fpu();
}
static void write_sse_reg(sse128_t *data, int reg)
{
emulator_get_fpu();
switch (reg) {
case 0: asm("movdqa %0, %%xmm0" : : "m"(*data)); break;
case 1: asm("movdqa %0, %%xmm1" : : "m"(*data)); break;
case 2: asm("movdqa %0, %%xmm2" : : "m"(*data)); break;
case 3: asm("movdqa %0, %%xmm3" : : "m"(*data)); break;
case 4: asm("movdqa %0, %%xmm4" : : "m"(*data)); break;
case 5: asm("movdqa %0, %%xmm5" : : "m"(*data)); break;
case 6: asm("movdqa %0, %%xmm6" : : "m"(*data)); break;
case 7: asm("movdqa %0, %%xmm7" : : "m"(*data)); break;
#ifdef CONFIG_X86_64
case 8: asm("movdqa %0, %%xmm8" : : "m"(*data)); break;
case 9: asm("movdqa %0, %%xmm9" : : "m"(*data)); break;
case 10: asm("movdqa %0, %%xmm10" : : "m"(*data)); break;
case 11: asm("movdqa %0, %%xmm11" : : "m"(*data)); break;
case 12: asm("movdqa %0, %%xmm12" : : "m"(*data)); break;
case 13: asm("movdqa %0, %%xmm13" : : "m"(*data)); break;
case 14: asm("movdqa %0, %%xmm14" : : "m"(*data)); break;
case 15: asm("movdqa %0, %%xmm15" : : "m"(*data)); break;
#endif
default: BUG();
}
emulator_put_fpu();
}
static void read_mmx_reg(u64 *data, int reg)
{
emulator_get_fpu();
switch (reg) {
case 0: asm("movq %%mm0, %0" : "=m"(*data)); break;
case 1: asm("movq %%mm1, %0" : "=m"(*data)); break;
case 2: asm("movq %%mm2, %0" : "=m"(*data)); break;
case 3: asm("movq %%mm3, %0" : "=m"(*data)); break;
case 4: asm("movq %%mm4, %0" : "=m"(*data)); break;
case 5: asm("movq %%mm5, %0" : "=m"(*data)); break;
case 6: asm("movq %%mm6, %0" : "=m"(*data)); break;
case 7: asm("movq %%mm7, %0" : "=m"(*data)); break;
default: BUG();
}
emulator_put_fpu();
}
static void write_mmx_reg(u64 *data, int reg)
{
emulator_get_fpu();
switch (reg) {
case 0: asm("movq %0, %%mm0" : : "m"(*data)); break;
case 1: asm("movq %0, %%mm1" : : "m"(*data)); break;
case 2: asm("movq %0, %%mm2" : : "m"(*data)); break;
case 3: asm("movq %0, %%mm3" : : "m"(*data)); break;
case 4: asm("movq %0, %%mm4" : : "m"(*data)); break;
case 5: asm("movq %0, %%mm5" : : "m"(*data)); break;
case 6: asm("movq %0, %%mm6" : : "m"(*data)); break;
case 7: asm("movq %0, %%mm7" : : "m"(*data)); break;
default: BUG();
}
emulator_put_fpu();
}
static int em_fninit(struct x86_emulate_ctxt *ctxt) 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(); kvm_fpu_get();
asm volatile("fninit"); asm volatile("fninit");
emulator_put_fpu(); kvm_fpu_put();
return X86EMUL_CONTINUE; return X86EMUL_CONTINUE;
} }
@ -1201,9 +1098,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(); kvm_fpu_get();
asm volatile("fnstcw %0": "+m"(fcw)); asm volatile("fnstcw %0": "+m"(fcw));
emulator_put_fpu(); kvm_fpu_put();
ctxt->dst.val = fcw; ctxt->dst.val = fcw;
@ -1217,9 +1114,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(); kvm_fpu_get();
asm volatile("fnstsw %0": "+m"(fsw)); asm volatile("fnstsw %0": "+m"(fsw));
emulator_put_fpu(); kvm_fpu_put();
ctxt->dst.val = fsw; ctxt->dst.val = fsw;
@ -1238,7 +1135,7 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
op->type = OP_XMM; op->type = OP_XMM;
op->bytes = 16; op->bytes = 16;
op->addr.xmm = reg; op->addr.xmm = reg;
read_sse_reg(&op->vec_val, reg); kvm_read_sse_reg(reg, &op->vec_val);
return; return;
} }
if (ctxt->d & Mmx) { if (ctxt->d & Mmx) {
@ -1289,7 +1186,7 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
op->type = OP_XMM; op->type = OP_XMM;
op->bytes = 16; op->bytes = 16;
op->addr.xmm = ctxt->modrm_rm; op->addr.xmm = ctxt->modrm_rm;
read_sse_reg(&op->vec_val, ctxt->modrm_rm); kvm_read_sse_reg(ctxt->modrm_rm, &op->vec_val);
return rc; return rc;
} }
if (ctxt->d & Mmx) { if (ctxt->d & Mmx) {
@ -1866,10 +1763,10 @@ static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op)
op->bytes * op->count); op->bytes * op->count);
break; break;
case OP_XMM: case OP_XMM:
write_sse_reg(&op->vec_val, op->addr.xmm); kvm_write_sse_reg(op->addr.xmm, &op->vec_val);
break; break;
case OP_MM: case OP_MM:
write_mmx_reg(&op->mm_val, op->addr.mm); kvm_write_mmx_reg(op->addr.mm, &op->mm_val);
break; break;
case OP_NONE: case OP_NONE:
/* no writeback */ /* no writeback */
@ -4124,11 +4021,11 @@ static int em_fxsave(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
emulator_get_fpu(); kvm_fpu_get();
rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state)); rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state));
emulator_put_fpu(); kvm_fpu_put();
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
@ -4172,7 +4069,7 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
emulator_get_fpu(); kvm_fpu_get();
if (size < __fxstate_size(16)) { if (size < __fxstate_size(16)) {
rc = fxregs_fixup(&fx_state, size); rc = fxregs_fixup(&fx_state, size);
@ -4189,7 +4086,7 @@ 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(); kvm_fpu_put();
return rc; return rc;
} }
@ -5437,9 +5334,9 @@ static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
{ {
int rc; int rc;
emulator_get_fpu(); kvm_fpu_get();
rc = asm_safe("fwait"); rc = asm_safe("fwait");
emulator_put_fpu(); kvm_fpu_put();
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);
@ -5450,7 +5347,7 @@ static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
static void fetch_possible_mmx_operand(struct operand *op) static void fetch_possible_mmx_operand(struct operand *op)
{ {
if (op->type == OP_MM) if (op->type == OP_MM)
read_mmx_reg(&op->mm_val, op->addr.mm); kvm_read_mmx_reg(op->addr.mm, &op->mm_val);
} }
static int fastop(struct x86_emulate_ctxt *ctxt, fastop_t fop) static int fastop(struct x86_emulate_ctxt *ctxt, fastop_t fop)

140
arch/x86/kvm/fpu.h Normal file
View File

@ -0,0 +1,140 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __KVM_FPU_H_
#define __KVM_FPU_H_
#include <asm/fpu/api.h>
typedef u32 __attribute__((vector_size(16))) sse128_t;
#define __sse128_u union { sse128_t vec; u64 as_u64[2]; u32 as_u32[4]; }
#define sse128_lo(x) ({ __sse128_u t; t.vec = x; t.as_u64[0]; })
#define sse128_hi(x) ({ __sse128_u t; t.vec = x; t.as_u64[1]; })
#define sse128_l0(x) ({ __sse128_u t; t.vec = x; t.as_u32[0]; })
#define sse128_l1(x) ({ __sse128_u t; t.vec = x; t.as_u32[1]; })
#define sse128_l2(x) ({ __sse128_u t; t.vec = x; t.as_u32[2]; })
#define sse128_l3(x) ({ __sse128_u t; t.vec = x; t.as_u32[3]; })
#define sse128(lo, hi) ({ __sse128_u t; t.as_u64[0] = lo; t.as_u64[1] = hi; t.vec; })
static inline void _kvm_read_sse_reg(int reg, sse128_t *data)
{
switch (reg) {
case 0: asm("movdqa %%xmm0, %0" : "=m"(*data)); break;
case 1: asm("movdqa %%xmm1, %0" : "=m"(*data)); break;
case 2: asm("movdqa %%xmm2, %0" : "=m"(*data)); break;
case 3: asm("movdqa %%xmm3, %0" : "=m"(*data)); break;
case 4: asm("movdqa %%xmm4, %0" : "=m"(*data)); break;
case 5: asm("movdqa %%xmm5, %0" : "=m"(*data)); break;
case 6: asm("movdqa %%xmm6, %0" : "=m"(*data)); break;
case 7: asm("movdqa %%xmm7, %0" : "=m"(*data)); break;
#ifdef CONFIG_X86_64
case 8: asm("movdqa %%xmm8, %0" : "=m"(*data)); break;
case 9: asm("movdqa %%xmm9, %0" : "=m"(*data)); break;
case 10: asm("movdqa %%xmm10, %0" : "=m"(*data)); break;
case 11: asm("movdqa %%xmm11, %0" : "=m"(*data)); break;
case 12: asm("movdqa %%xmm12, %0" : "=m"(*data)); break;
case 13: asm("movdqa %%xmm13, %0" : "=m"(*data)); break;
case 14: asm("movdqa %%xmm14, %0" : "=m"(*data)); break;
case 15: asm("movdqa %%xmm15, %0" : "=m"(*data)); break;
#endif
default: BUG();
}
}
static inline void _kvm_write_sse_reg(int reg, const sse128_t *data)
{
switch (reg) {
case 0: asm("movdqa %0, %%xmm0" : : "m"(*data)); break;
case 1: asm("movdqa %0, %%xmm1" : : "m"(*data)); break;
case 2: asm("movdqa %0, %%xmm2" : : "m"(*data)); break;
case 3: asm("movdqa %0, %%xmm3" : : "m"(*data)); break;
case 4: asm("movdqa %0, %%xmm4" : : "m"(*data)); break;
case 5: asm("movdqa %0, %%xmm5" : : "m"(*data)); break;
case 6: asm("movdqa %0, %%xmm6" : : "m"(*data)); break;
case 7: asm("movdqa %0, %%xmm7" : : "m"(*data)); break;
#ifdef CONFIG_X86_64
case 8: asm("movdqa %0, %%xmm8" : : "m"(*data)); break;
case 9: asm("movdqa %0, %%xmm9" : : "m"(*data)); break;
case 10: asm("movdqa %0, %%xmm10" : : "m"(*data)); break;
case 11: asm("movdqa %0, %%xmm11" : : "m"(*data)); break;
case 12: asm("movdqa %0, %%xmm12" : : "m"(*data)); break;
case 13: asm("movdqa %0, %%xmm13" : : "m"(*data)); break;
case 14: asm("movdqa %0, %%xmm14" : : "m"(*data)); break;
case 15: asm("movdqa %0, %%xmm15" : : "m"(*data)); break;
#endif
default: BUG();
}
}
static inline void _kvm_read_mmx_reg(int reg, u64 *data)
{
switch (reg) {
case 0: asm("movq %%mm0, %0" : "=m"(*data)); break;
case 1: asm("movq %%mm1, %0" : "=m"(*data)); break;
case 2: asm("movq %%mm2, %0" : "=m"(*data)); break;
case 3: asm("movq %%mm3, %0" : "=m"(*data)); break;
case 4: asm("movq %%mm4, %0" : "=m"(*data)); break;
case 5: asm("movq %%mm5, %0" : "=m"(*data)); break;
case 6: asm("movq %%mm6, %0" : "=m"(*data)); break;
case 7: asm("movq %%mm7, %0" : "=m"(*data)); break;
default: BUG();
}
}
static inline void _kvm_write_mmx_reg(int reg, const u64 *data)
{
switch (reg) {
case 0: asm("movq %0, %%mm0" : : "m"(*data)); break;
case 1: asm("movq %0, %%mm1" : : "m"(*data)); break;
case 2: asm("movq %0, %%mm2" : : "m"(*data)); break;
case 3: asm("movq %0, %%mm3" : : "m"(*data)); break;
case 4: asm("movq %0, %%mm4" : : "m"(*data)); break;
case 5: asm("movq %0, %%mm5" : : "m"(*data)); break;
case 6: asm("movq %0, %%mm6" : : "m"(*data)); break;
case 7: asm("movq %0, %%mm7" : : "m"(*data)); break;
default: BUG();
}
}
static inline void kvm_fpu_get(void)
{
fpregs_lock();
fpregs_assert_state_consistent();
if (test_thread_flag(TIF_NEED_FPU_LOAD))
switch_fpu_return();
}
static inline void kvm_fpu_put(void)
{
fpregs_unlock();
}
static inline void kvm_read_sse_reg(int reg, sse128_t *data)
{
kvm_fpu_get();
_kvm_read_sse_reg(reg, data);
kvm_fpu_put();
}
static inline void kvm_write_sse_reg(int reg, const sse128_t *data)
{
kvm_fpu_get();
_kvm_write_sse_reg(reg, data);
kvm_fpu_put();
}
static inline void kvm_read_mmx_reg(int reg, u64 *data)
{
kvm_fpu_get();
_kvm_read_mmx_reg(reg, data);
kvm_fpu_put();
}
static inline void kvm_write_mmx_reg(int reg, const u64 *data)
{
kvm_fpu_get();
_kvm_write_mmx_reg(reg, data);
kvm_fpu_put();
}
#endif

View File

@ -13,6 +13,7 @@
#define _ASM_X86_KVM_X86_EMULATE_H #define _ASM_X86_KVM_X86_EMULATE_H
#include <asm/desc_defs.h> #include <asm/desc_defs.h>
#include "fpu.h"
struct x86_emulate_ctxt; struct x86_emulate_ctxt;
enum x86_intercept; enum x86_intercept;
@ -236,8 +237,6 @@ struct x86_emulate_ops {
int (*set_xcr)(struct x86_emulate_ctxt *ctxt, u32 index, u64 xcr); int (*set_xcr)(struct x86_emulate_ctxt *ctxt, u32 index, u64 xcr);
}; };
typedef u32 __attribute__((vector_size(16))) sse128_t;
/* Type, address-of, and value of an instruction's operand. */ /* Type, address-of, and value of an instruction's operand. */
struct operand { struct operand {
enum { OP_REG, OP_MEM, OP_MEM_STR, OP_IMM, OP_XMM, OP_MM, OP_NONE } type; enum { OP_REG, OP_MEM, OP_MEM_STR, OP_IMM, OP_XMM, OP_MM, OP_NONE } type;