OSDN Git Service

KVM: x86: Add support for VMware backdoor Pseudo-PMCs
authorArbel Moshe <arbel.moshe@oracle.com>
Mon, 12 Mar 2018 11:12:53 +0000 (13:12 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 16 Mar 2018 21:02:01 +0000 (22:02 +0100)
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>
arch/x86/kvm/emulate.c
arch/x86/kvm/pmu.c
arch/x86/kvm/pmu.h
arch/x86/kvm/x86.c

index 821f633..dd88158 100644 (file)
@@ -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);
index 026db42..58ead7d 100644 (file)
@@ -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;
index a9a62b9..ba8898e 100644 (file)
@@ -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 */
index 291ad53..9e1496c 100644 (file)
@@ -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;