[PATCH V5 6/7] KVM: x86/pmu: Emulate RDPMC on performance metrics
From: Zide Chen
Date: Wed Jun 24 2026 - 23:55:20 EST
If the host has the PERF_METRICS capability but it's not present on
the guest, RDPMC interception must be enabled and KVM should inject
an #GP when the guest attempts to read it.
If the guest has PERF_METRICS, but RDPMC interception is enabled for
other reasons, KVM needs to emulate RDPMC with type 2000H.
For simplicity, Metrics Clear Mode is not supported.
Signed-off-by: Zide Chen <zide.chen@xxxxxxxxx>
---
v5: new patch.
---
arch/x86/kvm/pmu.c | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 7aafc5db1346..af5b14f44e4b 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -755,6 +755,16 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
return 0;
}
+static int kvm_pmu_rdpmc_metrics(struct kvm_vcpu *vcpu,
+ unsigned idx, u64 *data)
+{
+ if (!kvm_vcpu_has_perf_metrics(vcpu))
+ return 1;
+
+ *data = vcpu_to_pmu(vcpu)->perf_metrics;
+ return 0;
+}
+
int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -767,15 +777,18 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
if (is_vmware_backdoor_pmc(idx))
return kvm_pmu_rdpmc_vmware(vcpu, idx, data);
- pmc = kvm_pmu_call(rdpmc_ecx_to_pmc)(vcpu, idx, &mask);
- if (!pmc)
- return 1;
-
if (!kvm_is_cr4_bit_set(vcpu, X86_CR4_PCE) &&
(kvm_x86_call(get_cpl)(vcpu) != 0) &&
kvm_is_cr0_bit_set(vcpu, X86_CR0_PE))
return 1;
+ if (idx & INTEL_PMC_FIXED_RDPMC_METRICS)
+ return kvm_pmu_rdpmc_metrics(vcpu, idx, data);
+
+ pmc = kvm_pmu_call(rdpmc_ecx_to_pmc)(vcpu, idx, &mask);
+ if (!pmc)
+ return 1;
+
*data = pmc_read_counter(pmc) & mask;
return 0;
}
@@ -803,6 +816,14 @@ bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
+{
+ if (!(kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS))
+ return false;
+
+ return !kvm_vcpu_has_perf_metrics(vcpu);
+}
+
bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -815,6 +836,7 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)
return true;
return kvm_need_any_pmc_intercept(vcpu) ||
+ kvm_need_perf_metrics_intercept(vcpu) ||
pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||
pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1);
}
--
2.54.0