KVM: x86/pmu: Prioritize VMX interception over #GP on RDPMC due to bad index
authorSean Christopherson <seanjc@google.com>
Tue, 9 Jan 2024 23:02:27 +0000 (15:02 -0800)
committerSean Christopherson <seanjc@google.com>
Tue, 30 Jan 2024 23:28:02 +0000 (15:28 -0800)
Apply the pre-intercepts RDPMC validity check only to AMD, and rename all
relevant functions to make it as clear as possible that the check is not a
standard PMC index check.  On Intel, the basic rule is that only invalid
opcodes and privilege/permission/mode checks have priority over VM-Exit,
i.e. RDPMC with an invalid index should VM-Exit, not #GP.  While the SDM
doesn't explicitly call out RDPMC, it _does_ explicitly use RDMSR of a
non-existent MSR as an example where VM-Exit has priority over #GP, and
RDPMC is effectively just a variation of RDMSR.

Manually testing on various Intel CPUs confirms this behavior, and the
inverted priority was introduced for SVM compatibility, i.e. was not an
intentional change for Intel PMUs.  On AMD, *all* exceptions on RDPMC have
priority over VM-Exit.

Check for a NULL kvm_pmu_ops.check_rdpmc_early instead of using a RET0
static call so as to provide a convenient location to document the
difference between Intel and AMD, and to again try to make it as obvious
as possible that the early check is a one-off thing, not a generic "is
this PMC valid?" helper.

Fixes: 8061252ee0d2 ("KVM: SVM: Add intercept checks for remaining twobyte instructions")
Cc: Jim Mattson <jmattson@google.com>
Tested-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Link: https://lore.kernel.org/r/20240109230250.424295-8-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/include/asm/kvm-x86-pmu-ops.h
arch/x86/kvm/emulate.c
arch/x86/kvm/kvm_emulate.h
arch/x86/kvm/pmu.c
arch/x86/kvm/pmu.h
arch/x86/kvm/svm/pmu.c
arch/x86/kvm/vmx/pmu_intel.c
arch/x86/kvm/x86.c

index d7eebee4450c6bf5f1507ab35ad34b1a1036f226..f0cd482221330c2f1631f3286dedb189ece6be8c 100644 (file)
@@ -15,7 +15,7 @@ BUILD_BUG_ON(1)
 KVM_X86_PMU_OP(pmc_idx_to_pmc)
 KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
 KVM_X86_PMU_OP(msr_idx_to_pmc)
-KVM_X86_PMU_OP(is_valid_rdpmc_ecx)
+KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)
 KVM_X86_PMU_OP(is_valid_msr)
 KVM_X86_PMU_OP(get_msr)
 KVM_X86_PMU_OP(set_msr)
index e223043ef5b26f23be5b2f0606641f66c5cd18aa..695ab5b6055cc75d25d29e9dbe49609aa6717b58 100644 (file)
@@ -3962,7 +3962,7 @@ static int check_rdpmc(struct x86_emulate_ctxt *ctxt)
         * protected mode.
         */
        if ((!(cr4 & X86_CR4_PCE) && ctxt->ops->cpl(ctxt)) ||
-           ctxt->ops->check_pmc(ctxt, rcx))
+           ctxt->ops->check_rdpmc_early(ctxt, rcx))
                return emulate_gp(ctxt, 0);
 
        return X86EMUL_CONTINUE;
index e6d149825169dda3ace396ca979923c4a2d108e8..4351149484fb622309fb310790963f40ddff8120 100644 (file)
@@ -208,7 +208,7 @@ struct x86_emulate_ops {
        int (*set_msr_with_filter)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data);
        int (*get_msr_with_filter)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 *pdata);
        int (*get_msr)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 *pdata);
-       int (*check_pmc)(struct x86_emulate_ctxt *ctxt, u32 pmc);
+       int (*check_rdpmc_early)(struct x86_emulate_ctxt *ctxt, u32 pmc);
        int (*read_pmc)(struct x86_emulate_ctxt *ctxt, u32 pmc, u64 *pdata);
        void (*halt)(struct x86_emulate_ctxt *ctxt);
        void (*wbinvd)(struct x86_emulate_ctxt *ctxt);
index 30945fea69883d408a02569122f6a28006970cfe..0b0d804ee239ace6f645753d8af8a736e6e8c58d 100644 (file)
@@ -524,10 +524,20 @@ void kvm_pmu_handle_event(struct kvm_vcpu *vcpu)
                kvm_pmu_cleanup(vcpu);
 }
 
-/* check if idx is a valid index to access PMU */
-bool kvm_pmu_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
+int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
 {
-       return static_call(kvm_x86_pmu_is_valid_rdpmc_ecx)(vcpu, idx);
+       /*
+        * On Intel, VMX interception has priority over RDPMC exceptions that
+        * aren't already handled by the emulator, i.e. there are no additional
+        * check needed for Intel PMUs.
+        *
+        * On AMD, _all_ exceptions on RDPMC have priority over SVM intercepts,
+        * i.e. an invalid PMC results in a #GP, not #VMEXIT.
+        */
+       if (!kvm_pmu_ops.check_rdpmc_early)
+               return 0;
+
+       return static_call(kvm_x86_pmu_check_rdpmc_early)(vcpu, idx);
 }
 
 bool is_vmware_backdoor_pmc(u32 pmc_idx)
index 87ecf22f5b250d5b9ecddc00d9fa2a8cd077722c..51bbb01b21c878b606f6f03cdbadb6614d40a66e 100644 (file)
@@ -23,7 +23,7 @@ struct kvm_pmu_ops {
        struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
                unsigned int idx, u64 *mask);
        struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);
-       bool (*is_valid_rdpmc_ecx)(struct kvm_vcpu *vcpu, unsigned int idx);
+       int (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);
        bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
        int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
        int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
@@ -215,7 +215,7 @@ static inline bool pmc_is_globally_enabled(struct kvm_pmc *pmc)
 void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
 void kvm_pmu_handle_event(struct kvm_vcpu *vcpu);
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
-bool kvm_pmu_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx);
+int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
 bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr);
 int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
 int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
index 1fafc46f61c9889581cfc7920dfa11a8a3da7548..e886300f0f974c78d54d931a1b11ac11f6ca8d6f 100644 (file)
@@ -73,11 +73,14 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr,
        return amd_pmc_idx_to_pmc(pmu, idx);
 }
 
-static bool amd_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
+static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
 {
        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 
-       return idx < pmu->nr_arch_gp_counters;
+       if (idx >= pmu->nr_arch_gp_counters)
+               return -EINVAL;
+
+       return 0;
 }
 
 /* idx is the ECX register of RDPMC instruction */
@@ -229,7 +232,7 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = {
        .pmc_idx_to_pmc = amd_pmc_idx_to_pmc,
        .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
        .msr_idx_to_pmc = amd_msr_idx_to_pmc,
-       .is_valid_rdpmc_ecx = amd_is_valid_rdpmc_ecx,
+       .check_rdpmc_early = amd_check_rdpmc_early,
        .is_valid_msr = amd_is_valid_msr,
        .get_msr = amd_pmu_get_msr,
        .set_msr = amd_pmu_set_msr,
index ec4feaef3d5503b1f188a322d69172e5e4b88dff..1b1f888ad32bc498828828dcaee05cbef7414014 100644 (file)
@@ -55,17 +55,6 @@ static struct kvm_pmc *intel_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
        }
 }
 
-static bool intel_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
-{
-       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-       bool fixed = idx & (1u << 30);
-
-       idx &= ~(3u << 30);
-
-       return fixed ? idx < pmu->nr_arch_fixed_counters
-                    : idx < pmu->nr_arch_gp_counters;
-}
-
 static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
                                            unsigned int idx, u64 *mask)
 {
@@ -718,7 +707,6 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = {
        .pmc_idx_to_pmc = intel_pmc_idx_to_pmc,
        .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
        .msr_idx_to_pmc = intel_msr_idx_to_pmc,
-       .is_valid_rdpmc_ecx = intel_is_valid_rdpmc_ecx,
        .is_valid_msr = intel_is_valid_msr,
        .get_msr = intel_pmu_get_msr,
        .set_msr = intel_pmu_set_msr,
index 363b1c08020578b090b53d74482d9c8912629ec9..cbee277254f01023cd6c9f545fd52cc4f5c153c5 100644 (file)
@@ -8389,12 +8389,9 @@ static int emulator_get_msr(struct x86_emulate_ctxt *ctxt,
        return kvm_get_msr(emul_to_vcpu(ctxt), msr_index, pdata);
 }
 
-static int emulator_check_pmc(struct x86_emulate_ctxt *ctxt,
-                             u32 pmc)
+static int emulator_check_rdpmc_early(struct x86_emulate_ctxt *ctxt, u32 pmc)
 {
-       if (kvm_pmu_is_valid_rdpmc_ecx(emul_to_vcpu(ctxt), pmc))
-               return 0;
-       return -EINVAL;
+       return kvm_pmu_check_rdpmc_early(emul_to_vcpu(ctxt), pmc);
 }
 
 static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,
@@ -8526,7 +8523,7 @@ static const struct x86_emulate_ops emulate_ops = {
        .set_msr_with_filter = emulator_set_msr_with_filter,
        .get_msr_with_filter = emulator_get_msr_with_filter,
        .get_msr             = emulator_get_msr,
-       .check_pmc           = emulator_check_pmc,
+       .check_rdpmc_early   = emulator_check_rdpmc_early,
        .read_pmc            = emulator_read_pmc,
        .halt                = emulator_halt,
        .wbinvd              = emulator_wbinvd,