Skip to content

Commit 2904df6

Browse files
Dapeng Misean-jc
authored andcommitted
KVM: x86/pmu: Disable interception of select PMU MSRs for mediated vPMUs
For vCPUs with a mediated vPMU, disable interception of counter MSRs for PMCs that are exposed to the guest, and for GLOBAL_CTRL and related MSRs if they are fully supported according to the vCPU model, i.e. if the MSRs and all bits supported by hardware exist from the guest's point of view. Do NOT passthrough event selector or fixed counter control MSRs, so that KVM can enforce userspace-defined event filters, e.g. to prevent use of AnyThread events (which is unfortunately a setting in the fixed counter control MSR). Defer support for nested passthrough of mediated PMU MSRs to the future, as the logic for nested MSR interception is unfortunately vendor specific. Suggested-by: Sean Christopherson <seanjc@google.com> Co-developed-by: Mingwei Zhang <mizhang@google.com> Signed-off-by: Mingwei Zhang <mizhang@google.com> Co-developed-by: Sandipan Das <sandipan.das@amd.com> Signed-off-by: Sandipan Das <sandipan.das@amd.com> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com> [sean: squash patches, massage changelog, refresh VMX MSRs on filter change] Tested-by: Xudong Hao <xudong.hao@intel.com> Tested-by: Manali Shukla <manali.shukla@amd.com> Link: https://patch.msgid.link/20251206001720.468579-23-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent d3ba32d commit 2904df6

6 files changed

Lines changed: 127 additions & 36 deletions

File tree

arch/x86/kvm/pmu.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -717,27 +717,41 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
717717
return 0;
718718
}
719719

720-
bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
720+
static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
721721
{
722722
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
723723

724724
if (!kvm_vcpu_has_mediated_pmu(vcpu))
725725
return true;
726726

727+
/*
728+
* Note! Check *host* PMU capabilities, not KVM's PMU capabilities, as
729+
* KVM's capabilities are constrained based on KVM support, i.e. KVM's
730+
* capabilities themselves may be a subset of hardware capabilities.
731+
*/
732+
return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp ||
733+
pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
734+
}
735+
736+
bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
737+
{
738+
return kvm_need_any_pmc_intercept(vcpu) ||
739+
!kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
740+
}
741+
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
742+
743+
bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
744+
{
745+
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
746+
727747
/*
728748
* VMware allows access to these Pseduo-PMCs even when read via RDPMC
729749
* in Ring3 when CR4.PCE=0.
730750
*/
731751
if (enable_vmware_backdoor)
732752
return true;
733753

734-
/*
735-
* Note! Check *host* PMU capabilities, not KVM's PMU capabilities, as
736-
* KVM's capabilities are constrained based on KVM support, i.e. KVM's
737-
* capabilities themselves may be a subset of hardware capabilities.
738-
*/
739-
return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp ||
740-
pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed ||
754+
return kvm_need_any_pmc_intercept(vcpu) ||
741755
pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||
742756
pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1);
743757
}
@@ -934,11 +948,12 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
934948
* in the global controls). Emulate that behavior when refreshing the
935949
* PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL.
936950
*/
937-
if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) {
951+
if (pmu->nr_arch_gp_counters &&
952+
(kvm_pmu_has_perf_global_ctrl(pmu) || kvm_vcpu_has_mediated_pmu(vcpu)))
938953
pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0);
939-
if (kvm_vcpu_has_mediated_pmu(vcpu))
940-
kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl);
941-
}
954+
955+
if (kvm_vcpu_has_mediated_pmu(vcpu))
956+
kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl);
942957

943958
bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters);
944959
bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX,

arch/x86/kvm/pmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu);
239239
void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu);
240240

241241
bool is_vmware_backdoor_pmc(u32 pmc_idx);
242+
bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu);
242243
bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu);
243244

244245
extern struct kvm_pmu_ops intel_pmu_ops;

arch/x86/kvm/svm/svm.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,40 @@ void svm_vcpu_free_msrpm(void *msrpm)
730730
__free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE));
731731
}
732732

733+
static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
734+
{
735+
bool intercept = !kvm_vcpu_has_mediated_pmu(vcpu);
736+
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
737+
int i;
738+
739+
if (!enable_mediated_pmu)
740+
return;
741+
742+
/* Legacy counters are always available for AMD CPUs with a PMU. */
743+
for (i = 0; i < min(pmu->nr_arch_gp_counters, AMD64_NUM_COUNTERS); i++)
744+
svm_set_intercept_for_msr(vcpu, MSR_K7_PERFCTR0 + i,
745+
MSR_TYPE_RW, intercept);
746+
747+
intercept |= !guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE);
748+
for (i = 0; i < pmu->nr_arch_gp_counters; i++)
749+
svm_set_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i,
750+
MSR_TYPE_RW, intercept);
751+
752+
for ( ; i < kvm_pmu_cap.num_counters_gp; i++)
753+
svm_enable_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i,
754+
MSR_TYPE_RW);
755+
756+
intercept = kvm_need_perf_global_ctrl_intercept(vcpu);
757+
svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL,
758+
MSR_TYPE_RW, intercept);
759+
svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS,
760+
MSR_TYPE_RW, intercept);
761+
svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
762+
MSR_TYPE_RW, intercept);
763+
svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET,
764+
MSR_TYPE_RW, intercept);
765+
}
766+
733767
static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
734768
{
735769
struct vcpu_svm *svm = to_svm(vcpu);
@@ -798,6 +832,8 @@ static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
798832
if (sev_es_guest(vcpu->kvm))
799833
sev_es_recalc_msr_intercepts(vcpu);
800834

835+
svm_recalc_pmu_msr_intercepts(vcpu);
836+
801837
/*
802838
* x2APIC intercepts are modified on-demand and cannot be filtered by
803839
* userspace.

arch/x86/kvm/vmx/pmu_intel.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,6 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
128128
return &counters[array_index_nospec(idx, num_counters)];
129129
}
130130

131-
static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
132-
{
133-
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
134-
return 0;
135-
136-
return vcpu->arch.perf_capabilities;
137-
}
138-
139-
static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
140-
{
141-
return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
142-
}
143-
144131
static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
145132
{
146133
if (!fw_writes_is_enabled(pmu_to_vcpu(pmu)))

arch/x86/kvm/vmx/pmu_intel.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44

55
#include <linux/kvm_host.h>
66

7+
#include "cpuid.h"
8+
9+
static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
10+
{
11+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
12+
return 0;
13+
14+
return vcpu->arch.perf_capabilities;
15+
}
16+
17+
static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
18+
{
19+
return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
20+
}
21+
722
bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
823
int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu);
924

arch/x86/kvm/vmx/vmx.c

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4228,6 +4228,53 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu)
42284228
}
42294229
}
42304230

4231+
static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
4232+
{
4233+
bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu);
4234+
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
4235+
struct vcpu_vmx *vmx = to_vmx(vcpu);
4236+
bool intercept = !has_mediated_pmu;
4237+
int i;
4238+
4239+
if (!enable_mediated_pmu)
4240+
return;
4241+
4242+
vm_entry_controls_changebit(vmx, VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL,
4243+
has_mediated_pmu);
4244+
4245+
vm_exit_controls_changebit(vmx, VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL |
4246+
VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL,
4247+
has_mediated_pmu);
4248+
4249+
for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
4250+
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
4251+
MSR_TYPE_RW, intercept);
4252+
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, MSR_TYPE_RW,
4253+
intercept || !fw_writes_is_enabled(vcpu));
4254+
}
4255+
for ( ; i < kvm_pmu_cap.num_counters_gp; i++) {
4256+
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
4257+
MSR_TYPE_RW, true);
4258+
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i,
4259+
MSR_TYPE_RW, true);
4260+
}
4261+
4262+
for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
4263+
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i,
4264+
MSR_TYPE_RW, intercept);
4265+
for ( ; i < kvm_pmu_cap.num_counters_fixed; i++)
4266+
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i,
4267+
MSR_TYPE_RW, true);
4268+
4269+
intercept = kvm_need_perf_global_ctrl_intercept(vcpu);
4270+
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_STATUS,
4271+
MSR_TYPE_RW, intercept);
4272+
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL,
4273+
MSR_TYPE_RW, intercept);
4274+
vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,
4275+
MSR_TYPE_RW, intercept);
4276+
}
4277+
42314278
static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
42324279
{
42334280
bool intercept;
@@ -4294,17 +4341,7 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
42944341
vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, intercept);
42954342
}
42964343

4297-
if (enable_mediated_pmu) {
4298-
bool is_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu);
4299-
struct vcpu_vmx *vmx = to_vmx(vcpu);
4300-
4301-
vm_entry_controls_changebit(vmx,
4302-
VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu);
4303-
4304-
vm_exit_controls_changebit(vmx,
4305-
VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL |
4306-
VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu);
4307-
}
4344+
vmx_recalc_pmu_msr_intercepts(vcpu);
43084345

43094346
/*
43104347
* x2APIC and LBR MSR intercepts are modified on-demand and cannot be

0 commit comments

Comments
 (0)