Use hooks, just like done for other special purpose registers.
This includes moving XCR0 checks from hvmemul_get_fpu() to the emulator
itself as well as adding support for XGETBV emulation.
For now fuzzer reads will obtain the real values (minus the fuzzing of
the hook pointer itself).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Reviewed-by: George Dunlap <george.dunlap@citrix.com> [tracing parts]
return X86EMUL_OKAY;
}
+#define fuzz_read_xcr emul_test_read_xcr
+
enum {
MSRI_IA32_SYSENTER_CS,
MSRI_IA32_SYSENTER_ESP,
SET(write_io),
SET(read_cr),
SET(write_cr),
+ SET(read_xcr),
SET(read_msr),
SET(write_msr),
SET(wbinvd),
HOOK_write_cr,
HOOK_read_dr,
HOOK_write_dr,
+ HOOK_read_xcr,
HOOK_read_msr,
HOOK_write_msr,
HOOK_wbinvd,
MAYBE_DISABLE_HOOK(write_io);
MAYBE_DISABLE_HOOK(read_cr);
MAYBE_DISABLE_HOOK(write_cr);
+ MAYBE_DISABLE_HOOK(read_xcr);
MAYBE_DISABLE_HOOK(read_msr);
MAYBE_DISABLE_HOOK(write_msr);
MAYBE_DISABLE_HOOK(wbinvd);
.read_segment = read_segment,
.cpuid = emul_test_cpuid,
.read_cr = emul_test_read_cr,
+ .read_xcr = emul_test_read_xcr,
.read_msr = read_msr,
.get_fpu = emul_test_get_fpu,
.put_fpu = emul_test_put_fpu,
return X86EMUL_UNHANDLEABLE;
}
+int emul_test_read_xcr(
+ unsigned int reg,
+ uint64_t *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ uint32_t lo, hi;
+
+ ASSERT(cpu_has_xsave);
+
+ switch ( reg )
+ {
+ case 0:
+ break;
+
+ case 1:
+ if ( cpu_has_xgetbv1 )
+ break;
+ /* fall through */
+ default:
+ x86_emul_hw_exception(13 /* #GP */, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+
+ asm ( "xgetbv" : "=a" (lo), "=d" (hi) : "c" (reg) );
+ *val = lo | ((uint64_t)hi << 32);
+
+ return X86EMUL_OKAY;
+}
+
int emul_test_get_fpu(
void (*exception_callback)(void *, struct cpu_user_regs *),
void *exception_callback_arg,
(res.b & (1U << 5)) != 0; \
})
+#define cpu_has_xgetbv1 ({ \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ if ( !(res.c & (1U << 27)) ) \
+ res.a = 0; \
+ else \
+ emul_test_cpuid(0xd, 1, &res, NULL); \
+ (res.a & (1U << 2)) != 0; \
+})
+
#define cpu_has_bmi1 ({ \
struct cpuid_leaf res; \
emul_test_cpuid(7, 0, &res, NULL); \
unsigned long *val,
struct x86_emulate_ctxt *ctxt);
+int emul_test_read_xcr(
+ unsigned int reg,
+ uint64_t *val,
+ struct x86_emulate_ctxt *ctxt);
+
int emul_test_get_fpu(
void (*exception_callback)(void *, struct cpu_user_regs *),
void *exception_callback_arg,
return rc;
}
+static int hvmemul_read_xcr(
+ unsigned int reg,
+ uint64_t *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ int rc = x86emul_read_xcr(reg, val, ctxt);
+
+ if ( rc == X86EMUL_OKAY )
+ HVMTRACE_LONG_2D(XCR_READ, reg, TRC_PAR_LONG(*val));
+
+ return rc;
+}
+
+static int hvmemul_write_xcr(
+ unsigned int reg,
+ uint64_t val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ HVMTRACE_LONG_2D(XCR_WRITE, reg, TRC_PAR_LONG(val));
+
+ return x86emul_write_xcr(reg, val, ctxt);
+}
+
static int hvmemul_read_msr(
unsigned int reg,
uint64_t *val,
{
struct vcpu *curr = current;
- switch ( type )
- {
- case X86EMUL_FPU_fpu:
- case X86EMUL_FPU_wait:
- case X86EMUL_FPU_mmx:
- case X86EMUL_FPU_xmm:
- break;
- case X86EMUL_FPU_ymm:
- if ( !(curr->arch.xcr0 & X86_XCR0_SSE) ||
- !(curr->arch.xcr0 & X86_XCR0_YMM) )
- return X86EMUL_UNHANDLEABLE;
- break;
- default:
- return X86EMUL_UNHANDLEABLE;
- }
-
if ( !curr->fpu_dirtied )
hvm_funcs.fpu_dirty_intercept();
else if ( type == X86EMUL_FPU_fpu )
.write_io = hvmemul_write_io,
.read_cr = hvmemul_read_cr,
.write_cr = hvmemul_write_cr,
+ .read_xcr = hvmemul_read_xcr,
+ .write_xcr = hvmemul_write_xcr,
.read_msr = hvmemul_read_msr,
.write_msr = hvmemul_write_msr,
.wbinvd = hvmemul_wbinvd,
.write_io = hvmemul_write_io_discard,
.read_cr = hvmemul_read_cr,
.write_cr = hvmemul_write_cr,
+ .read_xcr = hvmemul_read_xcr,
+ .write_xcr = hvmemul_write_xcr,
.read_msr = hvmemul_read_msr,
.write_msr = hvmemul_write_msr_discard,
.wbinvd = hvmemul_wbinvd_discard,
.write_cr = write_cr,
.read_dr = read_dr,
.write_dr = write_dr,
+ .write_xcr = x86emul_write_xcr,
.read_msr = read_msr,
.write_msr = write_msr,
.cpuid = pv_emul_cpuid,
})
#include "x86_emulate/x86_emulate.c"
+
+int x86emul_read_xcr(unsigned int reg, uint64_t *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ switch ( reg )
+ {
+ case 0:
+ *val = current->arch.xcr0;
+ return X86EMUL_OKAY;
+
+ case 1:
+ if ( current->domain->arch.cpuid->xstate.xgetbv1 )
+ break;
+ /* fall through */
+ default:
+ x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+
+ *val = xgetbv(reg);
+
+ return X86EMUL_OKAY;
+}
+
+int x86emul_write_xcr(unsigned int reg, uint64_t val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ switch ( reg )
+ {
+ case 0:
+ break;
+
+ default:
+ gp_fault:
+ x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+
+ if ( unlikely(handle_xsetbv(reg, val) != 0) )
+ goto gp_fault;
+
+ return X86EMUL_OKAY;
+}
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
+ uint64_t xcr0;
int rc;
fail_if(!ops->get_fpu);
ASSERT(type != X86EMUL_FPU_none);
+
+ if ( type < X86EMUL_FPU_ymm || !ops->read_xcr ||
+ ops->read_xcr(0, &xcr0, ctxt) != X86EMUL_OKAY )
+ {
+ ASSERT(!ctxt->event_pending);
+ xcr0 = 0;
+ }
+
+ switch ( type )
+ {
+ case X86EMUL_FPU_ymm:
+ if ( !(xcr0 & X86_XCR0_SSE) || !(xcr0 & X86_XCR0_YMM) )
+ return X86EMUL_UNHANDLEABLE;
+ break;
+
+ default:
+ break;
+ }
+
rc = ops->get_fpu(fpu_handle_exception, fic, type, ctxt);
if ( rc == X86EMUL_OKAY )
_regs.eflags |= X86_EFLAGS_AC;
break;
-#ifdef __XEN__
+ case 0xd0: /* xgetbv */
+ generate_exception_if(vex.pfx, EXC_UD);
+ if ( !ops->read_cr || !ops->read_xcr ||
+ ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
+ cr4 = 0;
+ generate_exception_if(!(cr4 & X86_CR4_OSXSAVE), EXC_UD);
+ rc = ops->read_xcr(_regs.ecx, &msr_val, ctxt);
+ if ( rc != X86EMUL_OKAY )
+ goto done;
+ _regs.r(ax) = (uint32_t)msr_val;
+ _regs.r(dx) = msr_val >> 32;
+ break;
+
case 0xd1: /* xsetbv */
generate_exception_if(vex.pfx, EXC_UD);
- if ( !ops->read_cr || ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
+ if ( !ops->read_cr || !ops->write_xcr ||
+ ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
cr4 = 0;
generate_exception_if(!(cr4 & X86_CR4_OSXSAVE), EXC_UD);
- generate_exception_if(!mode_ring0() ||
- handle_xsetbv(_regs.ecx,
- _regs.eax | (_regs.rdx << 32)),
- EXC_GP, 0);
+ generate_exception_if(!mode_ring0(), EXC_GP, 0);
+ rc = ops->write_xcr(_regs.ecx,
+ _regs.eax | ((uint64_t)_regs.edx << 32), ctxt);
+ if ( rc != X86EMUL_OKAY )
+ goto done;
break;
-#endif
case 0xd4: /* vmfunc */
generate_exception_if(vex.pfx, EXC_UD);
unsigned long val,
struct x86_emulate_ctxt *ctxt);
+ /*
+ * read_xcr: Read from extended control register.
+ * @reg: [IN ] Register to read.
+ */
+ int (*read_xcr)(
+ unsigned int reg,
+ uint64_t *val,
+ struct x86_emulate_ctxt *ctxt);
+
+ /*
+ * write_xcr: Write to extended control register.
+ * @reg: [IN ] Register to write.
+ */
+ int (*write_xcr)(
+ unsigned int reg,
+ uint64_t val,
+ struct x86_emulate_ctxt *ctxt);
+
/*
* read_msr: Read from model-specific register.
* @reg: [IN ] Register to read.
void x86_emulate_free_state(struct x86_emulate_state *state);
#endif
+int x86emul_read_xcr(unsigned int reg, uint64_t *val,
+ struct x86_emulate_ctxt *ctxt);
+int x86emul_write_xcr(unsigned int reg, uint64_t val,
+ struct x86_emulate_ctxt *ctxt);
+
#endif
static inline void x86_emul_hw_exception(
#define DO_TRC_HVM_CR_WRITE64 DEFAULT_HVM_REGACCESS
#define DO_TRC_HVM_DR_READ DEFAULT_HVM_REGACCESS
#define DO_TRC_HVM_DR_WRITE DEFAULT_HVM_REGACCESS
+#define DO_TRC_HVM_XCR_READ64 DEFAULT_HVM_REGACCESS
+#define DO_TRC_HVM_XCR_WRITE64 DEFAULT_HVM_REGACCESS
#define DO_TRC_HVM_MSR_READ DEFAULT_HVM_REGACCESS
#define DO_TRC_HVM_MSR_WRITE DEFAULT_HVM_REGACCESS
#define DO_TRC_HVM_RDTSC DEFAULT_HVM_REGACCESS
void xstate_init(struct cpuinfo_x86 *c);
unsigned int xstate_ctxt_size(u64 xcr0);
+static inline uint64_t xgetbv(unsigned int index)
+{
+ uint32_t lo, hi;
+
+ ASSERT(index); /* get_xcr0() should be used instead. */
+ asm volatile ( ".byte 0x0f,0x01,0xd0" /* xgetbv */
+ : "=a" (lo), "=d" (hi) : "c" (index) );
+
+ return lo | ((uint64_t)hi << 32);
+}
+
static inline bool xstate_all(const struct vcpu *v)
{
/*
#define TRC_HVM_TRAP (TRC_HVM_HANDLER + 0x23)
#define TRC_HVM_TRAP_DEBUG (TRC_HVM_HANDLER + 0x24)
#define TRC_HVM_VLAPIC (TRC_HVM_HANDLER + 0x25)
+#define TRC_HVM_XCR_READ64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x26)
+#define TRC_HVM_XCR_WRITE64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x27)
#define TRC_HVM_IOPORT_WRITE (TRC_HVM_HANDLER + 0x216)
#define TRC_HVM_IOMEM_WRITE (TRC_HVM_HANDLER + 0x217)