KVM: x86: Add support for VMware backdoor Pseudo-PMCs
VMware exposes the following Pseudo PMCs: 0x10000: Physical host TSC 0x10001: Elapsed real time in ns 0x10002: Elapsed apparent time in ns For more info refer to: https://www.vmware.com/files/pdf/techpaper/Timekeeping-In-VirtualMachines.pdf VMware allows access to these Pseduo-PMCs even when read via RDPMC in Ring3 and CR4.PCE=0. Therefore, commit modifies x86 emulator to allow access to these PMCs in this situation. In addition, emulation of these PMCs were added to kvm_pmu_rdpmc(). Signed-off-by: Arbel Moshe <arbel.moshe@oracle.com> Signed-off-by: Liran Alon <liran.alon@oracle.com> Reviewed-by: Radim Krčmář <rkrcmar@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
9718420e9f
commit
2d7921c499
|
@ -30,6 +30,7 @@
|
|||
#include "x86.h"
|
||||
#include "tss.h"
|
||||
#include "mmu.h"
|
||||
#include "pmu.h"
|
||||
|
||||
/*
|
||||
* Operand types
|
||||
|
@ -4293,6 +4294,13 @@ static int check_rdpmc(struct x86_emulate_ctxt *ctxt)
|
|||
u64 cr4 = ctxt->ops->get_cr(ctxt, 4);
|
||||
u64 rcx = reg_read(ctxt, VCPU_REGS_RCX);
|
||||
|
||||
/*
|
||||
* VMware allows access to these Pseduo-PMCs even when read via RDPMC
|
||||
* in Ring3 when CR4.PCE=0.
|
||||
*/
|
||||
if (enable_vmware_backdoor && is_vmware_backdoor_pmc(rcx))
|
||||
return X86EMUL_CONTINUE;
|
||||
|
||||
if ((!(cr4 & X86_CR4_PCE) && ctxt->ops->cpl(ctxt)) ||
|
||||
ctxt->ops->check_pmc(ctxt, rcx))
|
||||
return emulate_gp(ctxt, 0);
|
||||
|
|
|
@ -244,12 +244,49 @@ int kvm_pmu_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx)
|
|||
return kvm_x86_ops->pmu_ops->is_valid_msr_idx(vcpu, idx);
|
||||
}
|
||||
|
||||
bool is_vmware_backdoor_pmc(u32 pmc_idx)
|
||||
{
|
||||
switch (pmc_idx) {
|
||||
case VMWARE_BACKDOOR_PMC_HOST_TSC:
|
||||
case VMWARE_BACKDOOR_PMC_REAL_TIME:
|
||||
case VMWARE_BACKDOOR_PMC_APPARENT_TIME:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
|
||||
{
|
||||
u64 ctr_val;
|
||||
|
||||
switch (idx) {
|
||||
case VMWARE_BACKDOOR_PMC_HOST_TSC:
|
||||
ctr_val = rdtsc();
|
||||
break;
|
||||
case VMWARE_BACKDOOR_PMC_REAL_TIME:
|
||||
ctr_val = ktime_get_boot_ns();
|
||||
break;
|
||||
case VMWARE_BACKDOOR_PMC_APPARENT_TIME:
|
||||
ctr_val = ktime_get_boot_ns() +
|
||||
vcpu->kvm->arch.kvmclock_offset;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
*data = ctr_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
|
||||
{
|
||||
bool fast_mode = idx & (1u << 31);
|
||||
struct kvm_pmc *pmc;
|
||||
u64 ctr_val;
|
||||
|
||||
if (is_vmware_backdoor_pmc(idx))
|
||||
return kvm_pmu_rdpmc_vmware(vcpu, idx, data);
|
||||
|
||||
pmc = kvm_x86_ops->pmu_ops->msr_idx_to_pmc(vcpu, idx);
|
||||
if (!pmc)
|
||||
return 1;
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
/* retrieve the 4 bits for EN and PMI out of IA32_FIXED_CTR_CTRL */
|
||||
#define fixed_ctrl_field(ctrl_reg, idx) (((ctrl_reg) >> ((idx)*4)) & 0xf)
|
||||
|
||||
#define VMWARE_BACKDOOR_PMC_HOST_TSC 0x10000
|
||||
#define VMWARE_BACKDOOR_PMC_REAL_TIME 0x10001
|
||||
#define VMWARE_BACKDOOR_PMC_APPARENT_TIME 0x10002
|
||||
|
||||
struct kvm_event_hw_type_mapping {
|
||||
u8 eventsel;
|
||||
u8 unit_mask;
|
||||
|
@ -114,6 +118,8 @@ void kvm_pmu_reset(struct kvm_vcpu *vcpu);
|
|||
void kvm_pmu_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_pmu_destroy(struct kvm_vcpu *vcpu);
|
||||
|
||||
bool is_vmware_backdoor_pmc(u32 pmc_idx);
|
||||
|
||||
extern struct kvm_pmu_ops intel_pmu_ops;
|
||||
extern struct kvm_pmu_ops amd_pmu_ops;
|
||||
#endif /* __KVM_X86_PMU_H */
|
||||
|
|
|
@ -5932,23 +5932,30 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu *vcpu, int *r)
|
|||
|
||||
static bool is_vmware_backdoor_opcode(struct x86_emulate_ctxt *ctxt)
|
||||
{
|
||||
if (ctxt->opcode_len != 1)
|
||||
return false;
|
||||
|
||||
switch (ctxt->b) {
|
||||
case 0xe4: /* IN */
|
||||
case 0xe5:
|
||||
case 0xec:
|
||||
case 0xed:
|
||||
case 0xe6: /* OUT */
|
||||
case 0xe7:
|
||||
case 0xee:
|
||||
case 0xef:
|
||||
case 0x6c: /* INS */
|
||||
case 0x6d:
|
||||
case 0x6e: /* OUTS */
|
||||
case 0x6f:
|
||||
return true;
|
||||
switch (ctxt->opcode_len) {
|
||||
case 1:
|
||||
switch (ctxt->b) {
|
||||
case 0xe4: /* IN */
|
||||
case 0xe5:
|
||||
case 0xec:
|
||||
case 0xed:
|
||||
case 0xe6: /* OUT */
|
||||
case 0xe7:
|
||||
case 0xee:
|
||||
case 0xef:
|
||||
case 0x6c: /* INS */
|
||||
case 0x6d:
|
||||
case 0x6e: /* OUTS */
|
||||
case 0x6f:
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (ctxt->b) {
|
||||
case 0x33: /* RDPMC */
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue