AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/17 19:12 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_pmu_rdpmc",
    "kvm_pmu_set_msr",
    "kvm_pmu_get_msr"
  ],
  "KMSANReasoning": "The patch introduces support for Intel Topdown Perf Metrics in KVM. It adds a `perf_metrics` field to `struct kvm_pmu` and `perf_capabilities` to `struct kvm_host_values`. `struct kvm_pmu` is embedded in the vCPU structure, which is zero-allocated during creation, and `perf_metrics` is explicitly zeroed in `intel_pmu_reset()`. `kvm_host` is a global variable, so `perf_capabilities` is zero-initialized and safely populated via `rdmsrq_safe()`.\n\nThe patch also increases `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4, expanding the `fixed_counters` array in `struct kvm_pmu`. This array is zero-allocated and safely initialized in `intel_pmu_refresh()`.\n\nAdditionally, the patch refactors `rdpmc_ecx_to_pmc` into `emulate_rdpmc`. The new `emulate_rdpmc` implementations (`intel_emulate_rdpmc`, `amd_emulate_rdpmc`) correctly populate the `*data` pointer on success (returning 0) and leave it untouched on failure (returning 1), matching the previous behavior. The caller (`kvm_emulate_rdpmc`) only reads `data` upon success.\n\nThere are no uninitialized memory reads, info-leaks to user space, or complex uninitialized data structures introduced. Any potential out-of-bounds accesses or memory corruption bugs would be caught by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies KVM's x86 PMU emulation to support the PERF_METRICS MSR and replaces the rdpmc_ecx_to_pmc callback with emulate_rdpmc. These changes affect the core KVM PMU logic and are reachable from guest code via rdpmc instructions and MSR accesses. Fuzzing should focus on the PMU MSR accessors and rdpmc emulation entry points.",
  "WorthFuzzing": true
}

1/1 2026/08/17 19:12 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 66c1e60858ff274eea4c8f7af453b5f206016660\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 17 19:12:44 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h\nindex 4a223c2793e3f..4b50ed058aed3 100644\n--- a/arch/x86/include/asm/kvm-x86-pmu-ops.h\n+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h\n@@ -13,7 +13,7 @@\n  * KVM_X86_PMU_OP_OPTIONAL() can be used for those functions that can have\n  * a NULL definition.\n  */\n-KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)\n+KVM_X86_PMU_OP(emulate_rdpmc)\n KVM_X86_PMU_OP(msr_idx_to_pmc)\n KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)\n KVM_X86_PMU_OP(is_valid_msr)\ndiff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h\nindex 283847619ff88..7166efdf79cef 100644\n--- a/arch/x86/include/asm/kvm_host.h\n+++ b/arch/x86/include/asm/kvm_host.h\n@@ -294,6 +294,7 @@ struct kvm_host_values {\n \tu64 xss;\n \tu64 s_cet;\n \tu64 arch_capabilities;\n+\tu64 perf_capabilities;\n };\n extern struct kvm_host_values kvm_host;\n \n@@ -562,7 +563,7 @@ struct kvm_pmc {\n #define KVM_MAX_NR_GP_COUNTERS\t\tKVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \\\n \t\t\t\t\t\tKVM_MAX_NR_AMD_GP_COUNTERS)\n \n-#define KVM_MAX_NR_INTEL_FIXED_COUNTERS\t3\n+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS\t4\n #define KVM_MAX_NR_AMD_FIXED_COUNTERS\t0\n #define KVM_MAX_NR_FIXED_COUNTERS\tKVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \\\n \t\t\t\t\t\tKVM_MAX_NR_AMD_FIXED_COUNTERS)\n@@ -582,6 +583,7 @@ struct kvm_pmu {\n \tu64 global_status_rsvd;\n \tu64 reserved_bits;\n \tu64 raw_event_mask;\n+\tu64 perf_metrics;\n \tstruct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];\n \tstruct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];\n \ndiff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h\nindex 18c4be75e9271..fdcaeb6c8352a 100644\n--- a/arch/x86/include/asm/msr-index.h\n+++ b/arch/x86/include/asm/msr-index.h\n@@ -331,6 +331,7 @@\n #define PERF_CAP_PEBS_FORMAT\t\t0xf00\n #define PERF_CAP_FW_WRITES\t\tBIT_ULL(13)\n #define PERF_CAP_PEBS_BASELINE\t\tBIT_ULL(14)\n+#define PERF_CAP_PERF_METRICS\t\tBIT_ULL(15)\n #define PERF_CAP_PEBS_TIMING_INFO\tBIT_ULL(17)\n #define PERF_CAP_PEBS_MASK\t\t(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \\\n \t\t\t\t\t PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \\\ndiff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c\nindex 66fa7140d65d9..2b2cc6bf3ce71 100644\n--- a/arch/x86/kvm/msrs.c\n+++ b/arch/x86/kvm/msrs.c\n@@ -217,9 +217,9 @@ static const u32 msrs_to_save_base[] = {\n \n static const u32 msrs_to_save_pmu[] = {\n \tMSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,\n-\tMSR_ARCH_PERFMON_FIXED_CTR0 + 2,\n+\tMSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,\n \tMSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,\n-\tMSR_CORE_PERF_GLOBAL_CTRL,\n+\tMSR_CORE_PERF_GLOBAL_CTRL, MSR_PERF_METRICS,\n \tMSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,\n \n \t/* This part of MSRs should match KVM_MAX_NR_INTEL_GP_COUNTERS. */\n@@ -2608,6 +2608,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)\n \t\t     intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2))\n \t\t\treturn;\n \t\tbreak;\n+\tcase MSR_PERF_METRICS:\n+\t\tif (!(kvm_caps.supported_perf_cap \u0026 PERF_CAP_PERF_METRICS))\n+\t\t\treturn;\n+\t\tbreak;\n \tcase MSR_ARCH_PERFMON_PERFCTR0 ...\n \t     MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:\n \t\tif (msr_index - MSR_ARCH_PERFMON_PERFCTR0 \u003e=\n@@ -2671,7 +2675,7 @@ void kvm_init_msr_lists(void)\n {\n \tunsigned i;\n \n-\tBUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 3,\n+\tBUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 4,\n \t\t\t \"Please update the fixed PMCs in msrs_to_save_pmu[]\");\n \n \tnum_msrs_to_save = 0;\ndiff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c\nindex a7d60c8785cd4..dab0bcabaa8fd 100644\n--- a/arch/x86/kvm/pmu.c\n+++ b/arch/x86/kvm/pmu.c\n@@ -19,6 +19,7 @@\n #include \u003clinux/moduleparam.h\u003e\n #include \u003casm/perf_event.h\u003e\n #include \u003casm/cpu_device_id.h\u003e\n+#include \u003casm/cpuid/api.h\u003e\n #include \"x86.h\"\n #include \"cpuid.h\"\n #include \"lapic.h\"\n@@ -99,7 +100,8 @@ static const struct x86_cpu_id vmx_pebs_pdist_cpu[] = {\n  *        all perf counters (both gp and fixed). The mapping relationship\n  *        between pmc and perf counters is as the following:\n  *        * Intel: [0 .. KVM_MAX_NR_INTEL_GP_COUNTERS-1] \u003c=\u003e gp counters\n- *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX + 2] \u003c=\u003e fixed\n+ *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX +\n+ *                  KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1] \u003c=\u003e fixed\n  *        * AMD:   [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H\n  *          and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] \u003c=\u003e gp counters\n  */\n@@ -134,6 +136,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)\n {\n \tbool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;\n \tint min_nr_gp_ctrs = pmu_ops-\u003eMIN_NR_GP_COUNTERS;\n+\tunion cpuid10_edx edx;\n+\tu32 eax, ebx, ecx;\n \n \t/*\n \t * Hybrid PMUs don't play nice with virtualization without careful\n@@ -181,6 +185,19 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)\n \tkvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,\n \t\t\t\t\t     KVM_MAX_NR_FIXED_COUNTERS);\n \n+\t/*\n+\t * Currently, KVM doesn't support non-contiguous fixed counters; make\n+\t * sure only contiguous ones are retained in kvm_pmu_cap.\n+\t */\n+\tif (kvm_host_pmu.version \u003e= 5) {\n+\t\tcpuid(0xa, \u0026eax, \u0026ebx, \u0026ecx, \u0026edx.full);\n+\t\tif (kvm_pmu_cap.num_counters_fixed \u003e edx.split.num_counters_fixed)\n+\t\t\tkvm_pmu_cap.num_counters_fixed = edx.split.num_counters_fixed;\n+\t}\n+\n+\tif (!enable_mediated_pmu \u0026\u0026 kvm_pmu_cap.num_counters_fixed \u003e 3)\n+\t\tkvm_pmu_cap.num_counters_fixed = 3;\n+\n \tkvm_pmu_eventsel.INSTRUCTIONS_RETIRED =\n \t\tperf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);\n \tkvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED =\n@@ -751,8 +768,6 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\n int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\n {\n \tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n-\tstruct kvm_pmc *pmc;\n-\tu64 mask = ~0ull;\n \n \tif (!pmu-\u003eversion)\n \t\treturn 1;\n@@ -760,17 +775,12 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\n \tif (is_vmware_backdoor_pmc(idx))\n \t\treturn kvm_pmu_rdpmc_vmware(vcpu, idx, data);\n \n-\tpmc = kvm_pmu_call(rdpmc_ecx_to_pmc)(vcpu, idx, \u0026mask);\n-\tif (!pmc)\n-\t\treturn 1;\n-\n \tif (!kvm_is_cr4_bit_set(vcpu, X86_CR4_PCE) \u0026\u0026\n \t    (kvm_x86_call(get_cpl)(vcpu) != 0) \u0026\u0026\n \t    kvm_is_cr0_bit_set(vcpu, X86_CR0_PE))\n \t\treturn 1;\n \n-\t*data = pmc_read_counter(pmc) \u0026 mask;\n-\treturn 0;\n+\treturn kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data);\n }\n \n static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)\n@@ -789,9 +799,16 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)\n \t       pmu-\u003enr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;\n }\n \n+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)\n+{\n+\treturn (kvm_host.perf_capabilities \u0026 PERF_CAP_PERF_METRICS) \u0026\u0026\n+\t\t!kvm_vcpu_has_perf_metrics(vcpu);\n+}\n+\n bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)\n {\n \treturn kvm_need_any_pmc_intercept(vcpu) ||\n+\t       kvm_need_perf_metrics_intercept(vcpu) ||\n \t       !kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));\n }\n EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);\n@@ -808,6 +825,7 @@ bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu)\n \t\treturn true;\n \n \treturn kvm_need_any_pmc_intercept(vcpu) ||\n+\t       kvm_need_perf_metrics_intercept(vcpu) ||\n \t       pmu-\u003ecounter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) ||\n \t       pmu-\u003ecounter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1);\n }\ndiff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h\nindex 090c9bbb74f48..eb6bbc658f5b4 100644\n--- a/arch/x86/kvm/pmu.h\n+++ b/arch/x86/kvm/pmu.h\n@@ -24,8 +24,8 @@\n #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED\n \n struct kvm_pmu_ops {\n-\tstruct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,\n-\t\tunsigned int idx, u64 *mask);\n+\tint (*emulate_rdpmc)(struct kvm_vcpu *vcpu, unsigned int idx,\n+\t\t\t     u64 *data);\n \tstruct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);\n \tint (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);\n \tbool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);\n@@ -271,6 +271,19 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu)\n \t\t\t\t  X86_PMC_IDX_MAX);\n }\n \n+static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)\n+{\n+\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))\n+\t\treturn 0;\n+\n+\treturn vcpu-\u003earch.perf_capabilities;\n+}\n+\n+static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)\n+{\n+\treturn kvm_vcpu_get_perf_caps(vcpu) \u0026 PERF_CAP_PERF_METRICS;\n+}\n+\n void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);\n int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);\n int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);\ndiff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c\nindex c18286545a7ac..0517fd4bbcd7a 100644\n--- a/arch/x86/kvm/svm/pmu.c\n+++ b/arch/x86/kvm/svm/pmu.c\n@@ -84,10 +84,15 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)\n }\n \n /* idx is the ECX register of RDPMC instruction */\n-static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,\n-\tunsigned int idx, u64 *mask)\n+static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)\n {\n-\treturn amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);\n+\tstruct kvm_pmc *pmc = amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);\n+\n+\tif (!pmc)\n+\t\treturn 1;\n+\n+\t*data = pmc_read_counter(pmc);\n+\treturn 0;\n }\n \n static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)\n@@ -302,7 +307,7 @@ static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc)\n }\n \n struct kvm_pmu_ops amd_pmu_ops __initdata = {\n-\t.rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,\n+\t.emulate_rdpmc = amd_emulate_rdpmc,\n \t.msr_idx_to_pmc = amd_msr_idx_to_pmc,\n \t.check_rdpmc_early = amd_check_rdpmc_early,\n \t.is_valid_msr = amd_is_valid_msr,\ndiff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c\nindex 7ed79894d11d2..e81b8a5423c87 100644\n--- a/arch/x86/kvm/vmx/nested.c\n+++ b/arch/x86/kvm/vmx/nested.c\n@@ -717,6 +717,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,\n \tnested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);\n \tnested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS);\n \tnested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL);\n+\n+\tnested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);\n }\n \n /*\ndiff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c\nindex bfa8612fb4508..3ca61e4b0ba68 100644\n--- a/arch/x86/kvm/vmx/pmu_intel.c\n+++ b/arch/x86/kvm/vmx/pmu_intel.c\n@@ -31,6 +31,7 @@\n  */\n #define INTEL_RDPMC_GP\t\t0\n #define INTEL_RDPMC_FIXED\tINTEL_PMC_FIXED_RDPMC_BASE\n+#define INTEL_RDPMC_METRICS\tINTEL_PMC_FIXED_RDPMC_METRICS\n \n #define INTEL_RDPMC_TYPE_MASK\tGENMASK(31, 16)\n #define INTEL_RDPMC_INDEX_MASK\tGENMASK(15, 0)\n@@ -85,14 +86,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)\n \t}\n }\n \n-static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,\n-\t\t\t\t\t    unsigned int idx, u64 *mask)\n+static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,\n+\t\t\t       u64 *data)\n {\n \tunsigned int type = idx \u0026 INTEL_RDPMC_TYPE_MASK;\n \tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n-\tstruct kvm_pmc *counters;\n+\tstruct kvm_pmc *counters, *pmc;\n \tunsigned int num_counters;\n-\tu64 bitmask;\n \n \t/*\n \t * The encoding of ECX for RDPMC is different for architectural versus\n@@ -105,7 +105,9 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,\n \t * as KVM doesn't support such PMUs.\n \t */\n \tif (WARN_ON_ONCE(!pmu-\u003eversion))\n-\t\treturn NULL;\n+\t\treturn 1;\n+\n+\tidx \u0026= INTEL_RDPMC_INDEX_MASK;\n \n \t/*\n \t * General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs\n@@ -119,23 +121,34 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,\n \tcase INTEL_RDPMC_FIXED:\n \t\tcounters = pmu-\u003efixed_counters;\n \t\tnum_counters = pmu-\u003enr_arch_fixed_counters;\n-\t\tbitmask = pmu-\u003ecounter_bitmask[KVM_PMC_FIXED];\n \t\tbreak;\n \tcase INTEL_RDPMC_GP:\n \t\tcounters = pmu-\u003egp_counters;\n \t\tnum_counters = pmu-\u003enr_arch_gp_counters;\n-\t\tbitmask = pmu-\u003ecounter_bitmask[KVM_PMC_GP];\n \t\tbreak;\n+\tcase INTEL_RDPMC_METRICS:\n+\t\tif (!kvm_vcpu_has_perf_metrics(vcpu))\n+\t\t\treturn 1;\n+\n+\t\t/*\n+\t\t * The index in ECX[15:0] is implementation specific, but no\n+\t\t * platform currently supports a non-zero index.\n+\t\t */\n+\t\tif (idx)\n+\t\t\treturn 1;\n+\n+\t\t*data = pmu-\u003eperf_metrics;\n+\t\treturn 0;\n \tdefault:\n-\t\treturn NULL;\n+\t\treturn 1;\n \t}\n \n-\tidx \u0026= INTEL_RDPMC_INDEX_MASK;\n \tif (idx \u003e= num_counters)\n-\t\treturn NULL;\n+\t\treturn 1;\n \n-\t*mask \u0026= bitmask;\n-\treturn \u0026counters[array_index_nospec(idx, num_counters)];\n+\tpmc = \u0026counters[array_index_nospec(idx, num_counters)];\n+\t*data = pmc_read_counter(pmc);\n+\treturn 0;\n }\n \n static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)\n@@ -189,14 +202,16 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)\n \tswitch (msr) {\n \tcase MSR_CORE_PERF_FIXED_CTR_CTRL:\n \t\treturn kvm_pmu_has_perf_global_ctrl(pmu);\n+\tcase MSR_PERF_METRICS:\n+\t\treturn kvm_vcpu_has_perf_metrics(vcpu);\n \tcase MSR_IA32_PEBS_ENABLE:\n-\t\tret = vcpu_get_perf_capabilities(vcpu) \u0026 PERF_CAP_PEBS_FORMAT;\n+\t\tret = kvm_vcpu_get_perf_caps(vcpu) \u0026 PERF_CAP_PEBS_FORMAT;\n \t\tbreak;\n \tcase MSR_IA32_DS_AREA:\n \t\tret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);\n \t\tbreak;\n \tcase MSR_PEBS_DATA_CFG:\n-\t\tperf_capabilities = vcpu_get_perf_capabilities(vcpu);\n+\t\tperf_capabilities = kvm_vcpu_get_perf_caps(vcpu);\n \t\tret = (perf_capabilities \u0026 PERF_CAP_PEBS_BASELINE) \u0026\u0026\n \t\t\t((perf_capabilities \u0026 PERF_CAP_PEBS_FORMAT) \u003e 3);\n \t\tbreak;\n@@ -346,6 +361,9 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n \tcase MSR_CORE_PERF_FIXED_CTR_CTRL:\n \t\tmsr_info-\u003edata = pmu-\u003efixed_ctr_ctrl;\n \t\tbreak;\n+\tcase MSR_PERF_METRICS:\n+\t\tmsr_info-\u003edata = pmu-\u003eperf_metrics;\n+\t\tbreak;\n \tcase MSR_IA32_PEBS_ENABLE:\n \t\tmsr_info-\u003edata = pmu-\u003epebs_enable;\n \t\tbreak;\n@@ -395,6 +413,14 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n \t\tif (pmu-\u003efixed_ctr_ctrl != data)\n \t\t\treprogram_fixed_counters(pmu, data);\n \t\tbreak;\n+\tcase MSR_PERF_METRICS:\n+\t\t/*\n+\t\t * Bits [63:32] are ignored on hardware that supports only\n+\t\t * level-1 metrics, but may be valid on hardware that supports\n+\t\t * level-2 metrics. Preserve guest writes verbatim.\n+\t\t */\n+\t\tpmu-\u003eperf_metrics = data;\n+\t\tbreak;\n \tcase MSR_IA32_PEBS_ENABLE:\n \t\tif (data \u0026 pmu-\u003epebs_enable_rsvd)\n \t\t\treturn 1;\n@@ -465,11 +491,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n  * different perf_event is already utilizing the requested counter, but the end\n  * result is the same (ignoring the fact that using a general purpose counter\n  * will likely exacerbate counter contention).\n- *\n- * Forcibly inlined to allow asserting on @index at build time, and there should\n- * never be more than one user.\n  */\n-static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)\n+static u64 intel_get_fixed_pmc_eventsel(unsigned int index)\n {\n \tconst enum perf_hw_id fixed_pmc_perf_ids[] = {\n \t\t[0] = PERF_COUNT_HW_INSTRUCTIONS,\n@@ -478,8 +501,13 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)\n \t};\n \tu64 eventsel;\n \n-\tBUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);\n-\tBUILD_BUG_ON(index \u003e= KVM_MAX_NR_INTEL_FIXED_COUNTERS);\n+\t/*\n+\t * Fixed counters 3 and above don't have a corresponding generic\n+\t * hardware perf event, and KVM does not intend to emulate them on\n+\t * non-mediated vPMU.\n+\t */\n+\tif (index \u003e= ARRAY_SIZE(fixed_pmc_perf_ids))\n+\t\treturn 0;\n \n \t/*\n \t * Yell if perf reports support for a fixed counter but perf doesn't\n@@ -549,7 +577,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)\n \t\tpmu-\u003eraw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);\n \t}\n \n-\tperf_capabilities = vcpu_get_perf_capabilities(vcpu);\n+\tperf_capabilities = kvm_vcpu_get_perf_caps(vcpu);\n \tif (intel_pmu_lbr_is_compatible(vcpu) \u0026\u0026\n \t    (perf_capabilities \u0026 PERF_CAP_LBR_FMT))\n \t\tmemcpy(\u0026lbr_desc-\u003erecords, \u0026vmx_lbr_caps, sizeof(vmx_lbr_caps));\n@@ -575,6 +603,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)\n \tcounter_rsvd = ~((BIT_ULL(pmu-\u003enr_arch_gp_counters) - 1) |\n \t\t\t ((BIT_ULL(pmu-\u003enr_arch_fixed_counters) - 1) \u003c\u003c KVM_FIXED_PMC_BASE_IDX));\n \tpmu-\u003eglobal_ctrl_rsvd = counter_rsvd;\n+\tif (perf_capabilities \u0026 PERF_CAP_PERF_METRICS)\n+\t\tpmu-\u003eglobal_ctrl_rsvd \u0026= ~GLOBAL_CTRL_EN_PERF_METRICS;\n \n \t/*\n \t * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)\n@@ -631,6 +661,9 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu)\n \n static void intel_pmu_reset(struct kvm_vcpu *vcpu)\n {\n+\tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n+\n+\tpmu-\u003eperf_metrics = 0;\n \tintel_pmu_release_guest_lbr_event(vcpu);\n }\n \n@@ -801,6 +834,9 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)\n \tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n \tu64 global_status, toggle;\n \n+\tif (kvm_vcpu_has_perf_metrics(vcpu))\n+\t\twrmsrq(MSR_PERF_METRICS, pmu-\u003eperf_metrics);\n+\n \trdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);\n \ttoggle = pmu-\u003eglobal_status ^ global_status;\n \tif (global_status \u0026 toggle)\n@@ -829,10 +865,23 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)\n \t */\n \tif (pmu-\u003efixed_ctr_ctrl_hw)\n \t\twrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);\n+\n+\tif (kvm_vcpu_has_perf_metrics(vcpu)) {\n+\t\tpmu-\u003eperf_metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);\n+\t\t/*\n+\t\t * The SDM requires restoring fixed counter 3 before\n+\t\t * PERF_METRICS. However, this path writes 0 to PERF_METRICS\n+\t\t * before fixed counter 3. For this all-zero case, the\n+\t\t * resulting hardware state is therefore the same regardless\n+\t\t * of write order.\n+\t\t */\n+\t\tif (pmu-\u003eperf_metrics)\n+\t\t\twrmsrq(MSR_PERF_METRICS, 0);\n+\t}\n }\n \n struct kvm_pmu_ops intel_pmu_ops __initdata = {\n-\t.rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,\n+\t.emulate_rdpmc = intel_emulate_rdpmc,\n \t.msr_idx_to_pmc = intel_msr_idx_to_pmc,\n \t.is_valid_msr = intel_is_valid_msr,\n \t.get_msr = intel_pmu_get_msr,\ndiff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h\nindex 5d9357640aa18..dd447d8b6fdde 100644\n--- a/arch/x86/kvm/vmx/pmu_intel.h\n+++ b/arch/x86/kvm/vmx/pmu_intel.h\n@@ -4,19 +4,9 @@\n \n #include \u003clinux/kvm_host.h\u003e\n \n-#include \"cpuid.h\"\n-\n-static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)\n-{\n-\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))\n-\t\treturn 0;\n-\n-\treturn vcpu-\u003earch.perf_capabilities;\n-}\n-\n static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)\n {\n-\treturn (vcpu_get_perf_capabilities(vcpu) \u0026 PERF_CAP_FW_WRITES) != 0;\n+\treturn (kvm_vcpu_get_perf_caps(vcpu) \u0026 PERF_CAP_FW_WRITES) != 0;\n }\n \n bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);\ndiff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c\nindex e3bfe6aca1a0e..8cb9d642277aa 100644\n--- a/arch/x86/kvm/vmx/vmx.c\n+++ b/arch/x86/kvm/vmx/vmx.c\n@@ -4277,6 +4277,10 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)\n \t\t\t\t  MSR_TYPE_RW, intercept);\n \tvmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,\n \t\t\t\t  MSR_TYPE_RW, intercept);\n+\n+\tintercept = !has_mediated_pmu || !kvm_vcpu_has_perf_metrics(vcpu);\n+\tvmx_set_intercept_for_msr(vcpu, MSR_PERF_METRICS,\n+\t\t\t\t  MSR_TYPE_RW, intercept);\n }\n \n static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)\n@@ -8023,14 +8027,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)\n static __init u64 vmx_get_perf_capabilities(void)\n {\n \tu64 perf_cap = PERF_CAP_FW_WRITES;\n-\tu64 host_perf_cap = 0;\n \n \tif (!enable_pmu)\n \t\treturn 0;\n \n-\tif (boot_cpu_has(X86_FEATURE_PDCM))\n-\t\trdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);\n-\n \tif (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) \u0026\u0026\n \t    !enable_mediated_pmu) {\n \t\tx86_perf_get_lbr(\u0026vmx_lbr_caps);\n@@ -8043,11 +8043,11 @@ static __init u64 vmx_get_perf_capabilities(void)\n \t\tif (!vmx_lbr_caps.has_callstack)\n \t\t\tmemset(\u0026vmx_lbr_caps, 0, sizeof(vmx_lbr_caps));\n \t\telse if (vmx_lbr_caps.nr)\n-\t\t\tperf_cap |= host_perf_cap \u0026 PERF_CAP_LBR_FMT;\n+\t\t\tperf_cap |= kvm_host.perf_capabilities \u0026 PERF_CAP_LBR_FMT;\n \t}\n \n \tif (vmx_pebs_supported()) {\n-\t\tperf_cap |= host_perf_cap \u0026 PERF_CAP_PEBS_MASK;\n+\t\tperf_cap |= kvm_host.perf_capabilities \u0026 PERF_CAP_PEBS_MASK;\n \n \t\t/*\n \t\t * Disallow adaptive PEBS as it is functionally broken, can be\n@@ -8072,6 +8072,9 @@ static __init u64 vmx_get_perf_capabilities(void)\n \t\tperf_cap \u0026= ~PERF_CAP_PEBS_BASELINE;\n \t}\n \n+\tif (enable_mediated_pmu)\n+\t\tperf_cap |= kvm_host.perf_capabilities \u0026 PERF_CAP_PERF_METRICS;\n+\n \treturn perf_cap;\n }\n \ndiff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c\nindex d94b59140c452..d349224d27340 100644\n--- a/arch/x86/kvm/x86.c\n+++ b/arch/x86/kvm/x86.c\n@@ -7083,6 +7083,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)\n \tif (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))\n \t\trdmsrq(MSR_IA32_ARCH_CAPABILITIES, kvm_host.arch_capabilities);\n \n+\tif (boot_cpu_has(X86_FEATURE_PDCM))\n+\t\trdmsrq_safe(MSR_IA32_PERF_CAPABILITIES,\n+\t\t\t    \u0026kvm_host.perf_capabilities);\n+\n \tWARN_ON_ONCE(kvm_nr_uret_msrs);\n \n \tr = ops-\u003ehardware_setup();\ndiff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h\nindex eff29645719bc..e7745e2cd543a 100644\n--- a/tools/arch/x86/include/asm/msr-index.h\n+++ b/tools/arch/x86/include/asm/msr-index.h\n@@ -331,6 +331,7 @@\n #define PERF_CAP_PEBS_FORMAT\t\t0xf00\n #define PERF_CAP_FW_WRITES\t\tBIT_ULL(13)\n #define PERF_CAP_PEBS_BASELINE\t\tBIT_ULL(14)\n+#define PERF_CAP_PERF_METRICS\t\tBIT_ULL(15)\n #define PERF_CAP_PEBS_TIMING_INFO\tBIT_ULL(17)\n #define PERF_CAP_PEBS_MASK\t\t(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \\\n \t\t\t\t\t PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \\\ndiff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h\nindex 608ed83d7c6a6..6c19503e0bb7d 100644\n--- a/tools/testing/selftests/kvm/include/x86/pmu.h\n+++ b/tools/testing/selftests/kvm/include/x86/pmu.h\n@@ -52,6 +52,9 @@\n /* Fixed PMC controls, Intel only. */\n #define FIXED_PMC_GLOBAL_CTRL_ENABLE(_idx)\tBIT_ULL((32 + (_idx)))\n \n+/* PERF_METRICS enable, Intel only. */\n+#define PERF_METRICS_GLOBAL_CTRL_ENABLE\t\tBIT_ULL(48)\n+\n #define FIXED_PMC_KERNEL\t\t\tBIT_ULL(0)\n #define FIXED_PMC_USER\t\t\t\tBIT_ULL(1)\n #define FIXED_PMC_ANYTHREAD\t\t\tBIT_ULL(2)\ndiff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c\nindex c3e784e16348c..5abcf21d86124 100644\n--- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c\n+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c\n@@ -3,6 +3,7 @@\n  * Copyright (C) 2023, Tencent, Inc.\n  */\n #include \u003cx86intrin.h\u003e\n+#include \u003clinux/bitfield.h\u003e\n \n #include \"pmu.h\"\n #include \"processor.h\"\n@@ -254,17 +255,20 @@ do {\t\t\t\t\t\t\t\t\t\t\\\n \t);\t\t\t\t\t\t\t\t\t\\\n } while (0)\n \n-#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)\t\t\\\n+#define GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP)\t\t\t\t\\\n do {\t\t\t\t\t\t\t\t\t\t\\\n-\twrmsr(_pmc_msr, 0);\t\t\t\t\t\t\t\\\n-\t\t\t\t\t\t\t\t\t\t\\\n \tif (this_cpu_has(X86_FEATURE_CLFLUSHOPT))\t\t\t\t\\\n \t\tGUEST_MEASURE_EVENT(_ctrl_msr, _value, \"clflushopt %[m]\", FEP);\t\\\n \telse if (this_cpu_has(X86_FEATURE_CLFLUSH))\t\t\t\t\\\n \t\tGUEST_MEASURE_EVENT(_ctrl_msr, _value, \"clflush  %[m]\", FEP);\t\\\n \telse\t\t\t\t\t\t\t\t\t\\\n \t\tGUEST_MEASURE_EVENT(_ctrl_msr, _value, \"nop\", FEP);\t\t\\\n-\t\t\t\t\t\t\t\t\t\t\\\n+} while (0)\n+\n+#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\t\\\n+\twrmsr(_pmc_msr, 0);\t\t\t\t\t\t\t\\\n+\tGUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP);\t\t\t\t\\\n \tguest_assert_event_count(_idx, _pmc, _pmc_msr);\t\t\t\t\\\n } while (0)\n \n@@ -412,7 +416,7 @@ static void test_arch_events(u8 pmu_version, u64 perf_capabilities)\n  * other than PMCs in the future.\n  */\n #define MAX_NR_GP_COUNTERS\t8\n-#define MAX_NR_FIXED_COUNTERS\t3\n+#define MAX_NR_FIXED_COUNTERS\t4\n \n #define GUEST_ASSERT_PMC_MSR_ACCESS(insn, msr, expect_gp, vector)\t\t\\\n __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,\t\t\t\\\n@@ -653,8 +657,81 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)\n \tpmu_vm_free(vm, vcpus);\n }\n \n+static void __guest_test_perf_metrics(void)\n+{\n+\tint retiring, bad_spec, fe_bound, be_bound, sum;\n+\tu64 global_ctrl, metrics;\n+\n+\tif ((guest_get_pmu_version() \u003c 2) ||\t/* Does guest have GLOBAL_CTRL? */\n+\t    !this_cpu_has(X86_FEATURE_PDCM) ||\n+\t    !(rdmsr(MSR_IA32_PERF_CAPABILITIES) \u0026 PERF_CAP_PERF_METRICS))\n+\t\treturn;\n+\n+\twrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);\n+\twrmsr(MSR_CORE_PERF_FIXED_CTR3, 0);\n+\twrmsr(MSR_PERF_METRICS, 0);\n+\n+\t/* Enable fixed ctr3 (TOPDOWN.SLOTS) and PERF_METRICS. */\n+\twrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(3, FIXED_PMC_KERNEL));\n+\tglobal_ctrl = FIXED_PMC_GLOBAL_CTRL_ENABLE(3) |\n+\t\t      PERF_METRICS_GLOBAL_CTRL_ENABLE;\n+\n+\tGUEST_RUN_PAYLOAD(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl, \"\");\n+\n+\t/* Check test results. */\n+\tmetrics = rdmsr(MSR_PERF_METRICS);\n+\tretiring = FIELD_GET(GENMASK_ULL(7, 0), metrics);\n+\tbad_spec = FIELD_GET(GENMASK_ULL(15, 8), metrics);\n+\tfe_bound = FIELD_GET(GENMASK_ULL(23, 16), metrics);\n+\tbe_bound = FIELD_GET(GENMASK_ULL(31, 24), metrics);\n+\n+\t/*\n+\t * Be conservative: the measured payload definitely retires work, so\n+\t * Retiring should be non-zero.\n+\t */\n+\tGUEST_ASSERT_NE(metrics, 0);\n+\tGUEST_ASSERT_NE(retiring, 0);\n+\n+\t/*\n+\t * Each level-1 Topdown metric is an integer fraction of 0xff.\n+\t * A +/-3 error margin is chosen for a loose sanity check.\n+\t */\n+\tsum = retiring + bad_spec + fe_bound + be_bound;\n+\tGUEST_ASSERT(sum \u003e= 0xfc \u0026\u0026 sum \u003c= 0x102);\n+\n+\t/* Sanity check after PERF_METRICS disabled. */\n+\t__asm__ __volatile__(\"loop .\" : \"+c\"((int){NUM_LOOPS}));\n+\tGUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);\n+\twrmsr(MSR_PERF_METRICS, 0xdeaddead);\n+\n+\tguest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);\n+}\n+\n+static void guest_test_perf_metrics(void)\n+{\n+\t__guest_test_perf_metrics();\n+\tGUEST_DONE();\n+}\n+\n+static void test_perf_metrics(u8 pmu_version, u64 perf_capabilities)\n+{\n+\tstruct kvm_vcpu **vcpus;\n+\tstruct kvm_vm *vm;\n+\n+\tpr_info(\"Testing Perf Metrics, PMU version %u, perf_caps = %lx\\n\",\n+\t\tpmu_version, perf_capabilities);\n+\n+\tvm = pmu_vm_create_with_vcpus(1, guest_test_perf_metrics,\n+\t\t\t\t      pmu_version, perf_capabilities, \u0026vcpus);\n+\n+\trun_vcpu(vcpus[0]);\n+\n+\tpmu_vm_free(vm, vcpus);\n+}\n+\n static void test_intel_counters(void)\n {\n+\tu64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);\n \tu8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);\n \tunsigned int i;\n \tu8 v;\n@@ -662,6 +739,7 @@ static void test_intel_counters(void)\n \tconst u64 perf_caps[] = {\n \t\t0,\n \t\tPMU_CAP_FW_WRITES,\n+\t\tPERF_CAP_PERF_METRICS,\n \t};\n \n \t/*\n@@ -699,9 +777,14 @@ static void test_intel_counters(void)\n \t\t\tif (!kvm_has_perf_caps \u0026\u0026 perf_caps[i])\n \t\t\t\tcontinue;\n \n+\t\t\t/* Ignore unsupported features. */\n+\t\t\tif (perf_caps[i] \u0026 ~advertised_perf_caps)\n+\t\t\t\tcontinue;\n+\n \t\t\ttest_arch_events(v, perf_caps[i]);\n \t\t\ttest_gp_counters(v, perf_caps[i]);\n \t\t\ttest_fixed_counters(v, perf_caps[i]);\n+\t\t\ttest_perf_metrics(v, perf_caps[i]);\n \t\t}\n \t}\n }\n"
}

2/1 2026/08/17 19:12 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_pmu_rdpmc",
    "kvm_pmu_set_msr",
    "kvm_pmu_get_msr"
  ],
  "Reasoning": "The patch modifies KVM's x86 PMU emulation to support the PERF_METRICS MSR and replaces the rdpmc_ecx_to_pmc callback with emulate_rdpmc. These changes affect the core KVM PMU logic and are reachable from guest code via rdpmc instructions and MSR accesses. Fuzzing should focus on the PMU MSR accessors and rdpmc emulation entry points.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 66c1e60858ff274eea4c8f7af453b5f206016660
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 17 19:12:44 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h
index 4a223c2793e3f..4b50ed058aed3 100644
--- a/arch/x86/include/asm/kvm-x86-pmu-ops.h
+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h
@@ -13,7 +13,7 @@
  * KVM_X86_PMU_OP_OPTIONAL() can be used for those functions that can have
  * a NULL definition.
  */
-KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
+KVM_X86_PMU_OP(emulate_rdpmc)
 KVM_X86_PMU_OP(msr_idx_to_pmc)
 KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)
 KVM_X86_PMU_OP(is_valid_msr)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 283847619ff88..7166efdf79cef 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -294,6 +294,7 @@ struct kvm_host_values {
 	u64 xss;
 	u64 s_cet;
 	u64 arch_capabilities;
+	u64 perf_capabilities;
 };
 extern struct kvm_host_values kvm_host;
 
@@ -562,7 +563,7 @@ struct kvm_pmc {
 #define KVM_MAX_NR_GP_COUNTERS		KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \
 						KVM_MAX_NR_AMD_GP_COUNTERS)
 
-#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	3
+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	4
 #define KVM_MAX_NR_AMD_FIXED_COUNTERS	0
 #define KVM_MAX_NR_FIXED_COUNTERS	KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \
 						KVM_MAX_NR_AMD_FIXED_COUNTERS)
@@ -582,6 +583,7 @@ struct kvm_pmu {
 	u64 global_status_rsvd;
 	u64 reserved_bits;
 	u64 raw_event_mask;
+	u64 perf_metrics;
 	struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
 	struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
 
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e9271..fdcaeb6c8352a 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d9..2b2cc6bf3ce71 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -217,9 +217,9 @@ static const u32 msrs_to_save_base[] = {
 
 static const u32 msrs_to_save_pmu[] = {
 	MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
-	MSR_ARCH_PERFMON_FIXED_CTR0 + 2,
+	MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
 	MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
-	MSR_CORE_PERF_GLOBAL_CTRL,
+	MSR_CORE_PERF_GLOBAL_CTRL, MSR_PERF_METRICS,
 	MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
 
 	/* This part of MSRs should match KVM_MAX_NR_INTEL_GP_COUNTERS. */
@@ -2608,6 +2608,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)
 		     intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2))
 			return;
 		break;
+	case MSR_PERF_METRICS:
+		if (!(kvm_caps.supported_perf_cap & PERF_CAP_PERF_METRICS))
+			return;
+		break;
 	case MSR_ARCH_PERFMON_PERFCTR0 ...
 	     MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:
 		if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >=
@@ -2671,7 +2675,7 @@ void kvm_init_msr_lists(void)
 {
 	unsigned i;
 
-	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 3,
+	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 4,
 			 "Please update the fixed PMCs in msrs_to_save_pmu[]");
 
 	num_msrs_to_save = 0;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index a7d60c8785cd4..dab0bcabaa8fd 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -19,6 +19,7 @@
 #include <linux/moduleparam.h>
 #include <asm/perf_event.h>
 #include <asm/cpu_device_id.h>
+#include <asm/cpuid/api.h>
 #include "x86.h"
 #include "cpuid.h"
 #include "lapic.h"
@@ -99,7 +100,8 @@ static const struct x86_cpu_id vmx_pebs_pdist_cpu[] = {
  *        all perf counters (both gp and fixed). The mapping relationship
  *        between pmc and perf counters is as the following:
  *        * Intel: [0 .. KVM_MAX_NR_INTEL_GP_COUNTERS-1] <=> gp counters
- *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX + 2] <=> fixed
+ *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX +
+ *                  KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1] <=> fixed
  *        * AMD:   [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H
  *          and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] <=> gp counters
  */
@@ -134,6 +136,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 {
 	bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;
 	int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS;
+	union cpuid10_edx edx;
+	u32 eax, ebx, ecx;
 
 	/*
 	 * Hybrid PMUs don't play nice with virtualization without careful
@@ -181,6 +185,19 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 	kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,
 					     KVM_MAX_NR_FIXED_COUNTERS);
 
+	/*
+	 * Currently, KVM doesn't support non-contiguous fixed counters; make
+	 * sure only contiguous ones are retained in kvm_pmu_cap.
+	 */
+	if (kvm_host_pmu.version >= 5) {
+		cpuid(0xa, &eax, &ebx, &ecx, &edx.full);
+		if (kvm_pmu_cap.num_counters_fixed > edx.split.num_counters_fixed)
+			kvm_pmu_cap.num_counters_fixed = edx.split.num_counters_fixed;
+	}
+
+	if (!enable_mediated_pmu && kvm_pmu_cap.num_counters_fixed > 3)
+		kvm_pmu_cap.num_counters_fixed = 3;
+
 	kvm_pmu_eventsel.INSTRUCTIONS_RETIRED =
 		perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);
 	kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED =
@@ -751,8 +768,6 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *pmc;
-	u64 mask = ~0ull;
 
 	if (!pmu->version)
 		return 1;
@@ -760,17 +775,12 @@ 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;
 
-	*data = pmc_read_counter(pmc) & mask;
-	return 0;
+	return kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data);
 }
 
 static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
@@ -789,9 +799,16 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
 	       pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
 }
 
+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
+{
+	return (kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS) &&
+		!kvm_vcpu_has_perf_metrics(vcpu);
+}
+
 bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
 {
 	return kvm_need_any_pmc_intercept(vcpu) ||
+	       kvm_need_perf_metrics_intercept(vcpu) ||
 	       !kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
@@ -808,6 +825,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);
 }
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 090c9bbb74f48..eb6bbc658f5b4 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -24,8 +24,8 @@
 #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED
 
 struct kvm_pmu_ops {
-	struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
-		unsigned int idx, u64 *mask);
+	int (*emulate_rdpmc)(struct kvm_vcpu *vcpu, unsigned int idx,
+			     u64 *data);
 	struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);
 	int (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);
 	bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
@@ -271,6 +271,19 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu)
 				  X86_PMC_IDX_MAX);
 }
 
+static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
+{
+	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
+		return 0;
+
+	return vcpu->arch.perf_capabilities;
+}
+
+static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
+{
+	return kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS;
+}
+
 void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
 int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index c18286545a7ac..0517fd4bbcd7a 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -84,10 +84,15 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
 }
 
 /* idx is the ECX register of RDPMC instruction */
-static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-	unsigned int idx, u64 *mask)
+static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)
 {
-	return amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+	struct kvm_pmc *pmc = amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+
+	if (!pmc)
+		return 1;
+
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
@@ -302,7 +307,7 @@ static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc)
 }
 
 struct kvm_pmu_ops amd_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = amd_emulate_rdpmc,
 	.msr_idx_to_pmc = amd_msr_idx_to_pmc,
 	.check_rdpmc_early = amd_check_rdpmc_early,
 	.is_valid_msr = amd_is_valid_msr,
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 7ed79894d11d2..e81b8a5423c87 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -717,6 +717,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
 	nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);
 	nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS);
 	nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL);
+
+	nested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);
 }
 
 /*
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index bfa8612fb4508..3ca61e4b0ba68 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -31,6 +31,7 @@
  */
 #define INTEL_RDPMC_GP		0
 #define INTEL_RDPMC_FIXED	INTEL_PMC_FIXED_RDPMC_BASE
+#define INTEL_RDPMC_METRICS	INTEL_PMC_FIXED_RDPMC_METRICS
 
 #define INTEL_RDPMC_TYPE_MASK	GENMASK(31, 16)
 #define INTEL_RDPMC_INDEX_MASK	GENMASK(15, 0)
@@ -85,14 +86,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
 	}
 }
 
-static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-					    unsigned int idx, u64 *mask)
+static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
+			       u64 *data)
 {
 	unsigned int type = idx & INTEL_RDPMC_TYPE_MASK;
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *counters;
+	struct kvm_pmc *counters, *pmc;
 	unsigned int num_counters;
-	u64 bitmask;
 
 	/*
 	 * The encoding of ECX for RDPMC is different for architectural versus
@@ -105,7 +105,9 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	 * as KVM doesn't support such PMUs.
 	 */
 	if (WARN_ON_ONCE(!pmu->version))
-		return NULL;
+		return 1;
+
+	idx &= INTEL_RDPMC_INDEX_MASK;
 
 	/*
 	 * General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
@@ -119,23 +121,34 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	case INTEL_RDPMC_FIXED:
 		counters = pmu->fixed_counters;
 		num_counters = pmu->nr_arch_fixed_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
 		break;
 	case INTEL_RDPMC_GP:
 		counters = pmu->gp_counters;
 		num_counters = pmu->nr_arch_gp_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_GP];
 		break;
+	case INTEL_RDPMC_METRICS:
+		if (!kvm_vcpu_has_perf_metrics(vcpu))
+			return 1;
+
+		/*
+		 * The index in ECX[15:0] is implementation specific, but no
+		 * platform currently supports a non-zero index.
+		 */
+		if (idx)
+			return 1;
+
+		*data = pmu->perf_metrics;
+		return 0;
 	default:
-		return NULL;
+		return 1;
 	}
 
-	idx &= INTEL_RDPMC_INDEX_MASK;
 	if (idx >= num_counters)
-		return NULL;
+		return 1;
 
-	*mask &= bitmask;
-	return &counters[array_index_nospec(idx, num_counters)];
+	pmc = &counters[array_index_nospec(idx, num_counters)];
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
@@ -189,14 +202,16 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 	switch (msr) {
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		return kvm_pmu_has_perf_global_ctrl(pmu);
+	case MSR_PERF_METRICS:
+		return kvm_vcpu_has_perf_metrics(vcpu);
 	case MSR_IA32_PEBS_ENABLE:
-		ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
+		ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
 		break;
 	case MSR_IA32_DS_AREA:
 		ret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);
 		break;
 	case MSR_PEBS_DATA_CFG:
-		perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+		perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 		ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) &&
 			((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3);
 		break;
@@ -346,6 +361,9 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		msr_info->data = pmu->fixed_ctr_ctrl;
 		break;
+	case MSR_PERF_METRICS:
+		msr_info->data = pmu->perf_metrics;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		msr_info->data = pmu->pebs_enable;
 		break;
@@ -395,6 +413,14 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		if (pmu->fixed_ctr_ctrl != data)
 			reprogram_fixed_counters(pmu, data);
 		break;
+	case MSR_PERF_METRICS:
+		/*
+		 * Bits [63:32] are ignored on hardware that supports only
+		 * level-1 metrics, but may be valid on hardware that supports
+		 * level-2 metrics. Preserve guest writes verbatim.
+		 */
+		pmu->perf_metrics = data;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		if (data & pmu->pebs_enable_rsvd)
 			return 1;
@@ -465,11 +491,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
  * different perf_event is already utilizing the requested counter, but the end
  * result is the same (ignoring the fact that using a general purpose counter
  * will likely exacerbate counter contention).
- *
- * Forcibly inlined to allow asserting on @index at build time, and there should
- * never be more than one user.
  */
-static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
+static u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 {
 	const enum perf_hw_id fixed_pmc_perf_ids[] = {
 		[0] = PERF_COUNT_HW_INSTRUCTIONS,
@@ -478,8 +501,13 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 	};
 	u64 eventsel;
 
-	BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);
-	BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS);
+	/*
+	 * Fixed counters 3 and above don't have a corresponding generic
+	 * hardware perf event, and KVM does not intend to emulate them on
+	 * non-mediated vPMU.
+	 */
+	if (index >= ARRAY_SIZE(fixed_pmc_perf_ids))
+		return 0;
 
 	/*
 	 * Yell if perf reports support for a fixed counter but perf doesn't
@@ -549,7 +577,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 		pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
 	}
 
-	perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+	perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 	if (intel_pmu_lbr_is_compatible(vcpu) &&
 	    (perf_capabilities & PERF_CAP_LBR_FMT))
 		memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps));
@@ -575,6 +603,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
 			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
 	pmu->global_ctrl_rsvd = counter_rsvd;
+	if (perf_capabilities & PERF_CAP_PERF_METRICS)
+		pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
 
 	/*
 	 * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
@@ -631,6 +661,9 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu)
 
 static void intel_pmu_reset(struct kvm_vcpu *vcpu)
 {
+	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+	pmu->perf_metrics = 0;
 	intel_pmu_release_guest_lbr_event(vcpu);
 }
 
@@ -801,6 +834,9 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u64 global_status, toggle;
 
+	if (kvm_vcpu_has_perf_metrics(vcpu))
+		wrmsrq(MSR_PERF_METRICS, pmu->perf_metrics);
+
 	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
 	toggle = pmu->global_status ^ global_status;
 	if (global_status & toggle)
@@ -829,10 +865,23 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
 	 */
 	if (pmu->fixed_ctr_ctrl_hw)
 		wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
+
+	if (kvm_vcpu_has_perf_metrics(vcpu)) {
+		pmu->perf_metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);
+		/*
+		 * The SDM requires restoring fixed counter 3 before
+		 * PERF_METRICS. However, this path writes 0 to PERF_METRICS
+		 * before fixed counter 3. For this all-zero case, the
+		 * resulting hardware state is therefore the same regardless
+		 * of write order.
+		 */
+		if (pmu->perf_metrics)
+			wrmsrq(MSR_PERF_METRICS, 0);
+	}
 }
 
 struct kvm_pmu_ops intel_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = intel_emulate_rdpmc,
 	.msr_idx_to_pmc = intel_msr_idx_to_pmc,
 	.is_valid_msr = intel_is_valid_msr,
 	.get_msr = intel_pmu_get_msr,
diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
index 5d9357640aa18..dd447d8b6fdde 100644
--- a/arch/x86/kvm/vmx/pmu_intel.h
+++ b/arch/x86/kvm/vmx/pmu_intel.h
@@ -4,19 +4,9 @@
 
 #include <linux/kvm_host.h>
 
-#include "cpuid.h"
-
-static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
-{
-	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
-		return 0;
-
-	return vcpu->arch.perf_capabilities;
-}
-
 static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
 {
-	return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
+	return (kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_FW_WRITES) != 0;
 }
 
 bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e3bfe6aca1a0e..8cb9d642277aa 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4277,6 +4277,10 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
 				  MSR_TYPE_RW, intercept);
 	vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,
 				  MSR_TYPE_RW, intercept);
+
+	intercept = !has_mediated_pmu || !kvm_vcpu_has_perf_metrics(vcpu);
+	vmx_set_intercept_for_msr(vcpu, MSR_PERF_METRICS,
+				  MSR_TYPE_RW, intercept);
 }
 
 static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
@@ -8023,14 +8027,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 static __init u64 vmx_get_perf_capabilities(void)
 {
 	u64 perf_cap = PERF_CAP_FW_WRITES;
-	u64 host_perf_cap = 0;
 
 	if (!enable_pmu)
 		return 0;
 
-	if (boot_cpu_has(X86_FEATURE_PDCM))
-		rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);
-
 	if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) &&
 	    !enable_mediated_pmu) {
 		x86_perf_get_lbr(&vmx_lbr_caps);
@@ -8043,11 +8043,11 @@ static __init u64 vmx_get_perf_capabilities(void)
 		if (!vmx_lbr_caps.has_callstack)
 			memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps));
 		else if (vmx_lbr_caps.nr)
-			perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT;
+			perf_cap |= kvm_host.perf_capabilities & PERF_CAP_LBR_FMT;
 	}
 
 	if (vmx_pebs_supported()) {
-		perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK;
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PEBS_MASK;
 
 		/*
 		 * Disallow adaptive PEBS as it is functionally broken, can be
@@ -8072,6 +8072,9 @@ static __init u64 vmx_get_perf_capabilities(void)
 		perf_cap &= ~PERF_CAP_PEBS_BASELINE;
 	}
 
+	if (enable_mediated_pmu)
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+
 	return perf_cap;
 }
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d94b59140c452..d349224d27340 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7083,6 +7083,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
 	if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
 		rdmsrq(MSR_IA32_ARCH_CAPABILITIES, kvm_host.arch_capabilities);
 
+	if (boot_cpu_has(X86_FEATURE_PDCM))
+		rdmsrq_safe(MSR_IA32_PERF_CAPABILITIES,
+			    &kvm_host.perf_capabilities);
+
 	WARN_ON_ONCE(kvm_nr_uret_msrs);
 
 	r = ops->hardware_setup();
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index eff29645719bc..e7745e2cd543a 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h
index 608ed83d7c6a6..6c19503e0bb7d 100644
--- a/tools/testing/selftests/kvm/include/x86/pmu.h
+++ b/tools/testing/selftests/kvm/include/x86/pmu.h
@@ -52,6 +52,9 @@
 /* Fixed PMC controls, Intel only. */
 #define FIXED_PMC_GLOBAL_CTRL_ENABLE(_idx)	BIT_ULL((32 + (_idx)))
 
+/* PERF_METRICS enable, Intel only. */
+#define PERF_METRICS_GLOBAL_CTRL_ENABLE		BIT_ULL(48)
+
 #define FIXED_PMC_KERNEL			BIT_ULL(0)
 #define FIXED_PMC_USER				BIT_ULL(1)
 #define FIXED_PMC_ANYTHREAD			BIT_ULL(2)
diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
index c3e784e16348c..5abcf21d86124 100644
--- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2023, Tencent, Inc.
  */
 #include <x86intrin.h>
+#include <linux/bitfield.h>
 
 #include "pmu.h"
 #include "processor.h"
@@ -254,17 +255,20 @@ do {										\
 	);									\
 } while (0)
 
-#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+#define GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP)				\
 do {										\
-	wrmsr(_pmc_msr, 0);							\
-										\
 	if (this_cpu_has(X86_FEATURE_CLFLUSHOPT))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflushopt %[m]", FEP);	\
 	else if (this_cpu_has(X86_FEATURE_CLFLUSH))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflush  %[m]", FEP);	\
 	else									\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "nop", FEP);		\
-										\
+} while (0)
+
+#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+do {										\
+	wrmsr(_pmc_msr, 0);							\
+	GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP);				\
 	guest_assert_event_count(_idx, _pmc, _pmc_msr);				\
 } while (0)
 
@@ -412,7 +416,7 @@ static void test_arch_events(u8 pmu_version, u64 perf_capabilities)
  * other than PMCs in the future.
  */
 #define MAX_NR_GP_COUNTERS	8
-#define MAX_NR_FIXED_COUNTERS	3
+#define MAX_NR_FIXED_COUNTERS	4
 
 #define GUEST_ASSERT_PMC_MSR_ACCESS(insn, msr, expect_gp, vector)		\
 __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
@@ -653,8 +657,81 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
 	pmu_vm_free(vm, vcpus);
 }
 
+static void __guest_test_perf_metrics(void)
+{
+	int retiring, bad_spec, fe_bound, be_bound, sum;
+	u64 global_ctrl, metrics;
+
+	if ((guest_get_pmu_version() < 2) ||	/* Does guest have GLOBAL_CTRL? */
+	    !this_cpu_has(X86_FEATURE_PDCM) ||
+	    !(rdmsr(MSR_IA32_PERF_CAPABILITIES) & PERF_CAP_PERF_METRICS))
+		return;
+
+	wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+	wrmsr(MSR_CORE_PERF_FIXED_CTR3, 0);
+	wrmsr(MSR_PERF_METRICS, 0);
+
+	/* Enable fixed ctr3 (TOPDOWN.SLOTS) and PERF_METRICS. */
+	wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(3, FIXED_PMC_KERNEL));
+	global_ctrl = FIXED_PMC_GLOBAL_CTRL_ENABLE(3) |
+		      PERF_METRICS_GLOBAL_CTRL_ENABLE;
+
+	GUEST_RUN_PAYLOAD(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl, "");
+
+	/* Check test results. */
+	metrics = rdmsr(MSR_PERF_METRICS);
+	retiring = FIELD_GET(GENMASK_ULL(7, 0), metrics);
+	bad_spec = FIELD_GET(GENMASK_ULL(15, 8), metrics);
+	fe_bound = FIELD_GET(GENMASK_ULL(23, 16), metrics);
+	be_bound = FIELD_GET(GENMASK_ULL(31, 24), metrics);
+
+	/*
+	 * Be conservative: the measured payload definitely retires work, so
+	 * Retiring should be non-zero.
+	 */
+	GUEST_ASSERT_NE(metrics, 0);
+	GUEST_ASSERT_NE(retiring, 0);
+
+	/*
+	 * Each level-1 Topdown metric is an integer fraction of 0xff.
+	 * A +/-3 error margin is chosen for a loose sanity check.
+	 */
+	sum = retiring + bad_spec + fe_bound + be_bound;
+	GUEST_ASSERT(sum >= 0xfc && sum <= 0x102);
+
+	/* Sanity check after PERF_METRICS disabled. */
+	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
+	GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
+	wrmsr(MSR_PERF_METRICS, 0xdeaddead);
+
+	guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
+}
+
+static void guest_test_perf_metrics(void)
+{
+	__guest_test_perf_metrics();
+	GUEST_DONE();
+}
+
+static void test_perf_metrics(u8 pmu_version, u64 perf_capabilities)
+{
+	struct kvm_vcpu **vcpus;
+	struct kvm_vm *vm;
+
+	pr_info("Testing Perf Metrics, PMU version %u, perf_caps = %lx\n",
+		pmu_version, perf_capabilities);
+
+	vm = pmu_vm_create_with_vcpus(1, guest_test_perf_metrics,
+				      pmu_version, perf_capabilities, &vcpus);
+
+	run_vcpu(vcpus[0]);
+
+	pmu_vm_free(vm, vcpus);
+}
+
 static void test_intel_counters(void)
 {
+	u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
 	u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
 	unsigned int i;
 	u8 v;
@@ -662,6 +739,7 @@ static void test_intel_counters(void)
 	const u64 perf_caps[] = {
 		0,
 		PMU_CAP_FW_WRITES,
+		PERF_CAP_PERF_METRICS,
 	};
 
 	/*
@@ -699,9 +777,14 @@ static void test_intel_counters(void)
 			if (!kvm_has_perf_caps && perf_caps[i])
 				continue;
 
+			/* Ignore unsupported features. */
+			if (perf_caps[i] & ~advertised_perf_caps)
+				continue;
+
 			test_arch_events(v, perf_caps[i]);
 			test_gp_counters(v, perf_caps[i]);
 			test_fixed_counters(v, perf_caps[i]);
+			test_perf_metrics(v, perf_caps[i]);
 		}
 	}
 }

3/2 2026/08/17 19:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14508
							output: 24
							thoughts: 844
						

Thoughts:
**KVM PMU Emulation Patch Analysis for Fuzzing**

Okay, so I've got a patch here that's making changes to KVM's x86 Performance Monitoring Unit emulation. It's adding support for the `PERF_METRICS` MSR and switching to `emulate_rdpmc` instead of the old `rdpmc_ecx_to_pmc` method. This looks like pretty core KVM code, touching files like `pmu.c`, `pmu_intel.c`, `pmu.c`, and `vmx.c`, which tells me this is definitely in the hypervisor, so it's reachable from a virtualized environment. The prompt says to avoid code paths that aren't reachable, but KVM is *designed* to be hit by `/dev/kvm`, so this is definitely worth looking at. Fuzzing via syzkaller with `/dev/kvm` will absolutely exercise this code.

Let's dive into the specifics of the changes. It looks like `kvm_pmu_rdpmc` is now calling `kvm_pmu_call(emulate_rdpmc)` instead of going directly to `rdpmc_ecx_to_pmc`. In the Intel-specific code (`pmu_intel.c`), `intel_emulate_rdpmc` is now handling `INTEL_RDPMC_METRICS`, and we have new handlers for `MSR_PERF_METRICS` in `intel_pmu_set_msr`, `intel_pmu_get_msr`, and modifications to `intel_mediated_pmu_load` and `intel_mediated_pmu_put`.  There's also a reset in `intel_pmu_reset` for clearing the `pmu->perf_metrics` field.

Hold on, I see that the `MSR_PERF_METRICS` is `0x38d`. This patch defines `PERF_CAP_PERF_METRICS` in the code, rather than just adding it to some existing mask.

Now, I need to identify the most promising functions for fuzzing. I'm seeing a few key functions that have changed or are new: `intel_emulate_rdpmc`, `amd_emulate_rdpmc`, `kvm_pmu_rdpmc`, `intel_pmu_set_msr`, `intel_pmu_get_msr`, `intel_mediated_pmu_load`, and `intel_mediated_pmu_put`.

Wait, the prompt says to prefer non-static, non-inlined API entrypoint functions. I see that `intel_emulate_rdpmc` is static, so that's probably not the best choice. Let's see... `kvm_pmu_rdpmc` looks like a good candidate, but it's calling static functions so, it's not ideal. Going higher up the stack, `kvm_pmu_set_msr` and `kvm_pmu_get_msr` are non-static and they call the modified static `intel_pmu_set_msr` and `intel_pmu_get_msr`, so maybe those are the way to go. I should check `arch/x86/kvm/pmu.c` and search for where `kvm_pmu_set_msr` is defined. That seems like the right path forward, given the prompt.



4/2 2026/08/17 19:12 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_set_msr"
}

Results:
{
  "Output": "arch/x86/kvm/msrs.c=1442=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1755-\t\tif (kvm_pmu_is_valid_msr(vcpu, msr))\narch/x86/kvm/msrs.c:1756:\t\t\treturn kvm_pmu_set_msr(vcpu, msr_info);\narch/x86/kvm/msrs.c-1757-\n--\narch/x86/kvm/msrs.c-1840-\t\tif (kvm_pmu_is_valid_msr(vcpu, msr))\narch/x86/kvm/msrs.c:1841:\t\t\treturn kvm_pmu_set_msr(vcpu, msr_info);\narch/x86/kvm/msrs.c-1842-\n--\narch/x86/kvm/pmu.c=865=int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/pmu.c-890-\narch/x86/kvm/pmu.c:891:int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\narch/x86/kvm/pmu.c-892-{\n--\narch/x86/kvm/pmu.h=291=int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);\narch/x86/kvm/pmu.h:292:int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);\narch/x86/kvm/pmu.h-293-void kvm_pmu_refresh(struct kvm_vcpu *vcpu);\n"
}

5/2 2026/08/17 19:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16706
							output: 34
							thoughts: 155
						

Thoughts:
**My Analysis of Potential KVM PMU Modification Points**

Alright, let's break this down. From what I'm seeing, `kvm_pmu_set_msr`, `kvm_pmu_get_msr`, and `kvm_pmu_rdpmc` are definitely the functions that stand out as the most likely areas where the relevant changes are implemented. Those are the prime suspects, right off the bat.

Now, I need to dig deeper. I'm going to quickly examine `kvm_init_pmu_capability` because if there's been any modification to it, that's important to understand. Let's see if the capability setup has changed. I also need to verify what conditions trigger the performance monitoring interceptions, so I will check `kvm_need_perf_global_ctrl_intercept` and `kvm_need_rdpmc_intercept` to figure out under what circumstances those functions come into play.

Finally, I need to confirm whether any specific configurations are required to activate the new functionality. KVM's usually enabled with `CONFIG_KVM` and then one of either `CONFIG_KVM_INTEL` or `CONFIG_KVM_AMD`, depending on the host architecture. So, to ensure I haven't missed anything, I'll go directly to the source and examine the `arch/x86/kvm/Kconfig` file. That's the best way to get a clear picture of what's enabled.



6/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/x86/kvm/Kconfig"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-51-\narch/x86/kvm/Kconfig:52:config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-68-\narch/x86/kvm/Kconfig:69:config KVM_WERROR\narch/x86/kvm/Kconfig-70-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-82-\narch/x86/kvm/Kconfig:83:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-95-\narch/x86/kvm/Kconfig:96:config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-106-\narch/x86/kvm/Kconfig:107:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108-        bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-133-\narch/x86/kvm/Kconfig:134:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-135-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-145-\narch/x86/kvm/Kconfig:146:config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-155-\narch/x86/kvm/Kconfig:156:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-157-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-172-\narch/x86/kvm/Kconfig:173:config KVM_IOAPIC\narch/x86/kvm/Kconfig-174-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-182-\narch/x86/kvm/Kconfig:183:config KVM_SMM\narch/x86/kvm/Kconfig-184-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-193-\narch/x86/kvm/Kconfig:194:config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-207-\narch/x86/kvm/Kconfig:208:config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-216-\narch/x86/kvm/Kconfig:217:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-229-\narch/x86/kvm/Kconfig:230:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-231-\tbool\narch/x86/kvm/Kconfig-232-\narch/x86/kvm/Kconfig:233:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\n"
}

7/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18434
							output: 43
							thoughts: 580
						

Thoughts:
**My Analysis of KVM PMU Emulation**

Okay, so I'm looking at this patch and immediately, my mind jumps to the configuration options: `KVM`, `KVM_INTEL`, and `KVM_AMD`. I'll keep those in mind, but first, I need to understand the intent of the changes.

The prompt mentions code paths that are unreachable in virtualized environments like GCE or QEMU, even with software emulation. However, that doesn't fully apply here. KVM is specifically designed to be reachable and fuzzed in virtualized environments, thanks to nested virtualization, and Syzkaller does a lot of work in KVM.

Let's dig into the specific functions touched by this patch. I see: `kvm_pmu_rdpmc`, `kvm_pmu_set_msr`, `kvm_pmu_get_msr`, and then some static functions: `intel_pmu_set_msr`, `intel_pmu_get_msr`, `intel_emulate_rdpmc`, and `amd_emulate_rdpmc`. My initial focus should be on `kvm_pmu_rdpmc`, `kvm_pmu_set_msr`, and `kvm_pmu_get_msr`.

Now I need to confirm something crucial: are these functions exported or are they static? I can see that `kvm_pmu_rdpmc` is in `arch/x86/kvm/pmu.c` and is not static, which is good. Let's make sure the others are too. Yes, `kvm_pmu_set_msr` and `kvm_pmu_get_msr` are also non-static.

Just to be thorough, I should check for other non-static functions within that scope. I'm noting `kvm_need_perf_global_ctrl_intercept`, `kvm_need_rdpmc_intercept`, and `kvm_init_pmu_capability`. While those are relevant, `kvm_pmu_rdpmc`, `kvm_pmu_set_msr`, and `kvm_pmu_get_msr` are the primary entry points for the PMU emulation logic, and likely where the changes related to `PERF_METRICS` and `emulate_rdpmc` will be focused.

Alright, time to dive deeper into `kvm_pmu_rdpmc` in `arch/x86/kvm/pmu.c`.



8/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "int kvm_pmu_rdpmc",
  "PathPrefix": "arch/x86/kvm/pmu.c"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=734=bool is_vmware_backdoor_pmc(u32 pmc_idx)\n--\narch/x86/kvm/pmu.c-744-\narch/x86/kvm/pmu.c:745:static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-746-{\n--\narch/x86/kvm/pmu.c-767-\narch/x86/kvm/pmu.c:768:int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-769-{\n"
}

9/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19855
							output: 33
							thoughts: 129
						

Thoughts:
**My KVM PMU Config Check**

Okay, so I'm looking at this code and confirming that `kvm_pmu_rdpmc` is definitely declared as non-static. And yep, I see that `kvm_pmu_set_msr` and `kvm_pmu_get_msr` are also not static, which is what I suspected.

Now, I need to make sure I've got all my bases covered with the configuration. Let me quickly check for the obvious KVM configs: `CONFIG_KVM`, `CONFIG_KVM_INTEL`, and `CONFIG_KVM_AMD` – just to be thorough.

Ah, right! I can't forget about the performance monitoring aspect. The PMU hooks into `PERF_EVENTS`, of course. Time to dive into `arch/x86/kvm/Kconfig` to refresh my memory and verify all the dependencies that KVM itself imposes. I want to make sure I haven't missed anything crucial for a fully functional PMU implementation.



10/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "depends on",
  "PathPrefix": "arch/x86/kvm/Kconfig"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=52=config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\narch/x86/kvm/Kconfig:54:\tdepends on X86_LOCAL_APIC\narch/x86/kvm/Kconfig-55-\thelp\n--\narch/x86/kvm/Kconfig=69=config KVM_WERROR\n--\narch/x86/kvm/Kconfig-76-\t# the kernel-wide WERROR=y.\narch/x86/kvm/Kconfig:77:\tdepends on KVM_X86 \u0026\u0026 ((EXPERT \u0026\u0026 !KASAN) || WERROR)\narch/x86/kvm/Kconfig-78-\thelp\n--\narch/x86/kvm/Kconfig=83=config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\narch/x86/kvm/Kconfig:85:\tdepends on EXPERT\narch/x86/kvm/Kconfig:86:\tdepends on KVM_X86 \u0026\u0026 X86_64\narch/x86/kvm/Kconfig-87-\tselect KVM_GENERIC_MEMORY_ATTRIBUTES\n--\narch/x86/kvm/Kconfig=96=config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\narch/x86/kvm/Kconfig:98:\tdepends on KVM \u0026\u0026 IA32_FEAT_CTL\narch/x86/kvm/Kconfig-99-\tselect X86_FRED if X86_64\n--\narch/x86/kvm/Kconfig=107=config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108-        bool \"Check that guests do not receive #VE exceptions\"\narch/x86/kvm/Kconfig:109:        depends on KVM_INTEL \u0026\u0026 EXPERT\narch/x86/kvm/Kconfig-110-        help\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\narch/x86/kvm/Kconfig-123-\tbool \"Software Guard eXtensions (SGX) Virtualization\"\narch/x86/kvm/Kconfig:124:\tdepends on X86_SGX \u0026\u0026 KVM_INTEL\narch/x86/kvm/Kconfig-125-\thelp\n--\narch/x86/kvm/Kconfig=134=config KVM_INTEL_TDX\n--\narch/x86/kvm/Kconfig-136-\tdefault y\narch/x86/kvm/Kconfig:137:\tdepends on INTEL_TDX_HOST\narch/x86/kvm/Kconfig-138-\tselect KVM_GENERIC_MEMORY_ATTRIBUTES\n--\narch/x86/kvm/Kconfig=146=config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\narch/x86/kvm/Kconfig:148:\tdepends on KVM \u0026\u0026 (CPU_SUP_AMD || CPU_SUP_HYGON)\narch/x86/kvm/Kconfig-149-\thelp\n--\narch/x86/kvm/Kconfig=156=config KVM_AMD_SEV\n--\narch/x86/kvm/Kconfig-158-\tdefault y\narch/x86/kvm/Kconfig:159:\tdepends on KVM_AMD \u0026\u0026 X86_64\narch/x86/kvm/Kconfig:160:\tdepends on CRYPTO_DEV_SP_PSP \u0026\u0026 !(KVM_AMD=y \u0026\u0026 CRYPTO_DEV_CCP_DD=m)\narch/x86/kvm/Kconfig-161-\tselect ARCH_HAS_CC_PLATFORM\n--\narch/x86/kvm/Kconfig=173=config KVM_IOAPIC\n--\narch/x86/kvm/Kconfig-175-\tdefault y\narch/x86/kvm/Kconfig:176:\tdepends on KVM_X86\narch/x86/kvm/Kconfig-177-\thelp\n--\narch/x86/kvm/Kconfig=183=config KVM_SMM\n--\narch/x86/kvm/Kconfig-185-\tdefault y\narch/x86/kvm/Kconfig:186:\tdepends on KVM_X86\narch/x86/kvm/Kconfig-187-\thelp\n--\narch/x86/kvm/Kconfig=194=config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\narch/x86/kvm/Kconfig:196:\tdepends on KVM_X86\narch/x86/kvm/Kconfig-197-\tdefault y\n--\narch/x86/kvm/Kconfig=208=config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\narch/x86/kvm/Kconfig:210:\tdepends on KVM_X86\narch/x86/kvm/Kconfig-211-\thelp\n--\narch/x86/kvm/Kconfig=217=config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\narch/x86/kvm/Kconfig:219:\tdepends on DEBUG_KERNEL\narch/x86/kvm/Kconfig:220:\tdepends on KVM_X86\narch/x86/kvm/Kconfig:221:\tdepends on EXPERT\narch/x86/kvm/Kconfig-222-\thelp\n--\narch/x86/kvm/Kconfig=233=config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\narch/x86/kvm/Kconfig:235:\tdepends on KVM_X86\narch/x86/kvm/Kconfig-236-\trange 1024 4096\n"
}

11/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21949
							output: 34
							thoughts: 74
						

Thoughts:
Okay, here's my take on those considerations.

**Kernel Virtualization Module Selection**

Right, so when I'm dealing with the kernel modules related to virtualization, the core ones are obviously `KVM`, `KVM_INTEL`, and `KVM_AMD`. These are the foundational components for running virtual machines. Therefore, I will include `KVM`, `KVM_INTEL`, and `KVM_AMD`.

Now, I need to consider whether `PERF_EVENTS` is necessary. My understanding is that the KVM Performance Monitoring Unit (PMU) emulation has a dependency on `PERF_EVENTS`, right? So I need to ensure that is set.



12/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/x86/kvm/Kconfig"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-51-\narch/x86/kvm/Kconfig:52:config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-68-\narch/x86/kvm/Kconfig:69:config KVM_WERROR\narch/x86/kvm/Kconfig-70-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-82-\narch/x86/kvm/Kconfig:83:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-95-\narch/x86/kvm/Kconfig:96:config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-106-\narch/x86/kvm/Kconfig:107:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108-        bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-133-\narch/x86/kvm/Kconfig:134:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-135-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-145-\narch/x86/kvm/Kconfig:146:config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-155-\narch/x86/kvm/Kconfig:156:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-157-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-172-\narch/x86/kvm/Kconfig:173:config KVM_IOAPIC\narch/x86/kvm/Kconfig-174-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-182-\narch/x86/kvm/Kconfig:183:config KVM_SMM\narch/x86/kvm/Kconfig-184-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-193-\narch/x86/kvm/Kconfig:194:config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-207-\narch/x86/kvm/Kconfig:208:config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-216-\narch/x86/kvm/Kconfig:217:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-229-\narch/x86/kvm/Kconfig:230:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-231-\tbool\narch/x86/kvm/Kconfig-232-\narch/x86/kvm/Kconfig:233:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\n"
}

13/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23419
							output: 36
							thoughts: 33
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Investigating KVM and Performance Events Configuration**

Okay, so I'm trying to figure out how `PERF_EVENTS` gets enabled in the KVM (Kernel-based Virtual Machine) context. The question is, where is that selection happening? My initial thought is it's probably being configured in `arch/x86/kvm/Kconfig`. It makes sense for KVM-specific configurations to live there, since KVM is architecture dependent (x86, in this case). Time to go check that Kconfig file and confirm this assumption. Let's dig in and see what's what.



14/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "select PERF_EVENTS",
  "PathPrefix": "arch/x86/kvm/Kconfig"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=20=config KVM_X86\n--\narch/x86/kvm/Kconfig-36-\tselect SCHED_INFO\narch/x86/kvm/Kconfig:37:\tselect PERF_EVENTS\narch/x86/kvm/Kconfig-38-\tselect GUEST_PERF_EVENTS\n"
}

15/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23742
							output: 46
							thoughts: 354
						

Thoughts:
**My Analysis of the KVM Performance Monitoring Unit Patch**

Okay, so `PERF_EVENTS` is enabled via `KVM_X86`, that much is clear. This means `KVM`, `KVM_INTEL`, and `KVM_AMD` are also enabled, so we're good to go there.

Right, let's break down these changes. The patch touches:

*   `arch/x86/kvm/pmu.c`
*   `arch/x86/kvm/vmx/pmu_intel.c`
*   `arch/x86/kvm/svm/pmu.c`
*   `arch/x86/kvm/vmx/vmx.c`
*   `arch/x86/kvm/x86.c`
*   `arch/x86/kvm/msrs.c`

Ah, I see. It's adding support for `PERF_METRICS` MSRs and `emulate_rdpmc`. Definitely ripe for fuzzing. The key symbols I need to focus on are `kvm_pmu_rdpmc`, `kvm_pmu_set_msr`, and `kvm_pmu_get_msr`.

Let's do a quick scan for any other non-static functions... Right, I see a few more: `kvm_need_perf_global_ctrl_intercept`, `kvm_need_rdpmc_intercept`, and `kvm_init_pmu_capability`.

But ultimately, I still think `kvm_pmu_rdpmc`, `kvm_pmu_set_msr`, and `kvm_pmu_get_msr` are the prime targets for fuzzing. Their interaction with the MSRs is the most interesting part. Now, I need to check if `kvm_pmu_rdpmc` is exported... I'll check that now.



16/2 2026/08/17 19:13 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "EXPORT_SYMBOL.*kvm_pmu_rdpmc",
  "PathPrefix": "arch/x86/kvm/pmu.c"
}

Results:
{
  "error": "no matches"
}

17/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24598
							output: 38
							thoughts: 71
						

Thoughts:
Here's my take on those thoughts:

**Analyzing `kvm_pmu_rdpmc` Usage**

Okay, so I'm looking at this `kvm_pmu_rdpmc` function. It's not exported, which is immediately interesting. I need to understand its scope and usage. The fact that it's *non-static* implies that it's accessible within the compilation unit, which offers some clues about its design intent.

Then, the pieces start falling into place. It's used within the context of KVM emulation, specifically related to the `rdpmc` instruction. Knowing that, and looking at the call stack, I can guess how it's being used for this emulation. I know that the function is called from either `arch/x86/kvm/emulate.c` or `arch/x86/kvm/x86.c`. That narrows down the context further and gives me the exact code to look at in more detail. This tells me about its place in the broader KVM architecture, specifically in handling the performance monitoring counter reads in a virtualized environment.



18/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_rdpmc",
  "PathPrefix": "arch/x86/kvm/"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=734=bool is_vmware_backdoor_pmc(u32 pmc_idx)\n--\narch/x86/kvm/pmu.c-744-\narch/x86/kvm/pmu.c:745:static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-746-{\n--\narch/x86/kvm/pmu.c-767-\narch/x86/kvm/pmu.c:768:int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-769-{\n--\narch/x86/kvm/pmu.c-775-\tif (is_vmware_backdoor_pmc(idx))\narch/x86/kvm/pmu.c:776:\t\treturn kvm_pmu_rdpmc_vmware(vcpu, idx, data);\narch/x86/kvm/pmu.c-777-\n--\narch/x86/kvm/pmu.h=287=void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);\narch/x86/kvm/pmu.h:288:int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);\narch/x86/kvm/pmu.h-289-int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);\n--\narch/x86/kvm/x86.c=789=int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/x86.c-793-\narch/x86/kvm/x86.c:794:\tif (kvm_pmu_rdpmc(vcpu, pmc, \u0026data)) {\narch/x86/kvm/x86.c-795-\t\tkvm_inject_gp(vcpu, 0);\n--\narch/x86/kvm/x86.c=5688=static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,\n--\narch/x86/kvm/x86.c-5690-{\narch/x86/kvm/x86.c:5691:\treturn kvm_pmu_rdpmc(emul_to_vcpu(ctxt), pmc, pdata);\narch/x86/kvm/x86.c-5692-}\n"
}

19/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25700
							output: 39
							thoughts: 79
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**PMU Symbol Analysis**

Alright, so `kvm_pmu_rdpmc` pops up in `kvm_emulate_rdpmc` and `emulator_read_pmc`, which is exactly what I was hoping to see. That confirms it, it's definitely a viable target for deeper investigation - a solid focus symbol. Good. Now, to broaden the scope a bit, I should shift my attention to `kvm_pmu_set_msr` and `kvm_pmu_get_msr`. Let's see what those are doing and if they tie in nicely.



20/2 2026/08/17 19:13 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_set_msr",
  "PathPrefix": "arch/x86/kvm/"
}

Results:
{
  "Output": "arch/x86/kvm/msrs.c=1442=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1755-\t\tif (kvm_pmu_is_valid_msr(vcpu, msr))\narch/x86/kvm/msrs.c:1756:\t\t\treturn kvm_pmu_set_msr(vcpu, msr_info);\narch/x86/kvm/msrs.c-1757-\n--\narch/x86/kvm/msrs.c-1840-\t\tif (kvm_pmu_is_valid_msr(vcpu, msr))\narch/x86/kvm/msrs.c:1841:\t\t\treturn kvm_pmu_set_msr(vcpu, msr_info);\narch/x86/kvm/msrs.c-1842-\n--\narch/x86/kvm/pmu.c=865=int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/pmu.c-890-\narch/x86/kvm/pmu.c:891:int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\narch/x86/kvm/pmu.c-892-{\n--\narch/x86/kvm/pmu.h=291=int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);\narch/x86/kvm/pmu.h:292:int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);\narch/x86/kvm/pmu.h-293-void kvm_pmu_refresh(struct kvm_vcpu *vcpu);\n"
}

21/2 2026/08/17 19:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26531
							output: 179
							thoughts: 1021
						

Thoughts:
**Focus Areas for KVM x86 PMU Emulation Fuzzing**

Okay, so I've been reviewing the code and need to identify the key areas to target for fuzzing.  I've got a good handle on where the changes introduced by the `PERF_METRICS` MSR support land.  Essentially, this patch modifies the x86 PMU emulation in KVM to handle the `PERF_METRICS` MSR and introduces `emulate_rdpmc`.  These modifications are reachable from guest code via `rdpmc` instructions and MSR accesses. Thus, my fuzzing effort should really concentrate on the PMU MSR accessors and the `rdpmc` emulation entry points.

Looking at the code, the calls to these MSR functions are performed through `kvm_set_msr_common` and `kvm_get_msr_common`.

Therefore, the focus symbols I've identified are:

*   `kvm_pmu_rdpmc`
*   `kvm_pmu_set_msr`
*   `kvm_pmu_get_msr`

As for configurations, I think the important ones are:

*   `KVM`
*   `KVM_INTEL`
*   `KVM_AMD`

I double-checked to see if I was missing anything, but `X86_LOCAL_APIC` is selected by `KVM` and `PERF_EVENTS` is selected by `KVM_X86`, so I should be covered.

Let me take a quick look at `kvm_pmu_rdpmc` in `arch/x86/kvm/pmu.c`. Yep, it's exactly what I expected, with the `emulate_rdpmc` call at the end, so I'm happy with that.

Now, checking `kvm_pmu_set_msr`. Okay, I see that this is a wrapper around a call to `set_msr`. Now, the actual implementations are `intel_pmu_set_msr` and `amd_pmu_set_msr`. Since `intel_pmu_set_msr` is static, I can't use it directly as a focus symbol. The prompt specifies to prefer non-static, non-inlined API entrypoint functions over internal static helper functions. I *could* use `kvm_set_msr_common` but it's a huge function, which might throw the distribution off. Despite the fact that `intel_pmu_set_msr` is static, because it's assigned to a function pointer (`intel_pmu_ops.set_msr`) it's very likely not inlined. So, I will stick with the original plan and use `kvm_pmu_set_msr`, as it is a suitable non-static API entry point.

Okay, I'm confident with these choices and ready to set the results.



22/2 2026/08/17 19:13 tool set-results
0m Args:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_pmu_rdpmc",
    "kvm_pmu_set_msr",
    "kvm_pmu_get_msr"
  ],
  "Reasoning": "The patch modifies KVM's x86 PMU emulation to support the PERF_METRICS MSR and replaces the rdpmc_ecx_to_pmc callback with emulate_rdpmc. These changes affect the core KVM PMU logic and are reachable from guest code via rdpmc instructions and MSR accesses. Fuzzing should focus on the PMU MSR accessors and rdpmc emulation entry points.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_pmu_rdpmc",
    "kvm_pmu_set_msr",
    "kvm_pmu_get_msr"
  ],
  "Reasoning": "The patch modifies KVM's x86 PMU emulation to support the PERF_METRICS MSR and replaces the rdpmc_ecx_to_pmc callback with emulate_rdpmc. These changes affect the core KVM PMU logic and are reachable from guest code via rdpmc instructions and MSR accesses. Fuzzing should focus on the PMU MSR accessors and rdpmc emulation entry points.",
  "WorthFuzzing": true
}

23/1 2026/08/17 19:13 action If
3m Args:
{
  "WorthFuzzing": true
}

Results:
null

24/2 2026/08/17 19:13 agent kmsan-evaluator
3m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces support for Intel Topdown Perf Metrics in KVM. It adds a `perf_metrics` field to `struct kvm_pmu` and `perf_capabilities` to `struct kvm_host_values`. `struct kvm_pmu` is embedded in the vCPU structure, which is zero-allocated during creation, and `perf_metrics` is explicitly zeroed in `intel_pmu_reset()`. `kvm_host` is a global variable, so `perf_capabilities` is zero-initialized and safely populated via `rdmsrq_safe()`.\n\nThe patch also increases `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4, expanding the `fixed_counters` array in `struct kvm_pmu`. This array is zero-allocated and safely initialized in `intel_pmu_refresh()`.\n\nAdditionally, the patch refactors `rdpmc_ecx_to_pmc` into `emulate_rdpmc`. The new `emulate_rdpmc` implementations (`intel_emulate_rdpmc`, `amd_emulate_rdpmc`) correctly populate the `*data` pointer on success (returning 0) and leave it untouched on failure (returning 1), matching the previous behavior. The caller (`kvm_emulate_rdpmc`) only reads `data` upon success.\n\nThere are no uninitialized memory reads, info-leaks to user space, or complex uninitialized data structures introduced. Any potential out-of-bounds accesses or memory corruption bugs would be caught by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 66c1e60858ff274eea4c8f7af453b5f206016660
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 17 19:12:44 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h
index 4a223c2793e3f..4b50ed058aed3 100644
--- a/arch/x86/include/asm/kvm-x86-pmu-ops.h
+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h
@@ -13,7 +13,7 @@
  * KVM_X86_PMU_OP_OPTIONAL() can be used for those functions that can have
  * a NULL definition.
  */
-KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
+KVM_X86_PMU_OP(emulate_rdpmc)
 KVM_X86_PMU_OP(msr_idx_to_pmc)
 KVM_X86_PMU_OP_OPTIONAL(check_rdpmc_early)
 KVM_X86_PMU_OP(is_valid_msr)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 283847619ff88..7166efdf79cef 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -294,6 +294,7 @@ struct kvm_host_values {
 	u64 xss;
 	u64 s_cet;
 	u64 arch_capabilities;
+	u64 perf_capabilities;
 };
 extern struct kvm_host_values kvm_host;
 
@@ -562,7 +563,7 @@ struct kvm_pmc {
 #define KVM_MAX_NR_GP_COUNTERS		KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \
 						KVM_MAX_NR_AMD_GP_COUNTERS)
 
-#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	3
+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS	4
 #define KVM_MAX_NR_AMD_FIXED_COUNTERS	0
 #define KVM_MAX_NR_FIXED_COUNTERS	KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \
 						KVM_MAX_NR_AMD_FIXED_COUNTERS)
@@ -582,6 +583,7 @@ struct kvm_pmu {
 	u64 global_status_rsvd;
 	u64 reserved_bits;
 	u64 raw_event_mask;
+	u64 perf_metrics;
 	struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS];
 	struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS];
 
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e9271..fdcaeb6c8352a 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d9..2b2cc6bf3ce71 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -217,9 +217,9 @@ static const u32 msrs_to_save_base[] = {
 
 static const u32 msrs_to_save_pmu[] = {
 	MSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,
-	MSR_ARCH_PERFMON_FIXED_CTR0 + 2,
+	MSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,
 	MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_STATUS,
-	MSR_CORE_PERF_GLOBAL_CTRL,
+	MSR_CORE_PERF_GLOBAL_CTRL, MSR_PERF_METRICS,
 	MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG,
 
 	/* This part of MSRs should match KVM_MAX_NR_INTEL_GP_COUNTERS. */
@@ -2608,6 +2608,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)
 		     intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2))
 			return;
 		break;
+	case MSR_PERF_METRICS:
+		if (!(kvm_caps.supported_perf_cap & PERF_CAP_PERF_METRICS))
+			return;
+		break;
 	case MSR_ARCH_PERFMON_PERFCTR0 ...
 	     MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1:
 		if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >=
@@ -2671,7 +2675,7 @@ void kvm_init_msr_lists(void)
 {
 	unsigned i;
 
-	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 3,
+	BUILD_BUG_ON_MSG(KVM_MAX_NR_FIXED_COUNTERS != 4,
 			 "Please update the fixed PMCs in msrs_to_save_pmu[]");
 
 	num_msrs_to_save = 0;
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index a7d60c8785cd4..dab0bcabaa8fd 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -19,6 +19,7 @@
 #include <linux/moduleparam.h>
 #include <asm/perf_event.h>
 #include <asm/cpu_device_id.h>
+#include <asm/cpuid/api.h>
 #include "x86.h"
 #include "cpuid.h"
 #include "lapic.h"
@@ -99,7 +100,8 @@ static const struct x86_cpu_id vmx_pebs_pdist_cpu[] = {
  *        all perf counters (both gp and fixed). The mapping relationship
  *        between pmc and perf counters is as the following:
  *        * Intel: [0 .. KVM_MAX_NR_INTEL_GP_COUNTERS-1] <=> gp counters
- *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX + 2] <=> fixed
+ *                 [KVM_FIXED_PMC_BASE_IDX .. KVM_FIXED_PMC_BASE_IDX +
+ *                  KVM_MAX_NR_INTEL_FIXED_COUNTERS - 1] <=> fixed
  *        * AMD:   [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H
  *          and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] <=> gp counters
  */
@@ -134,6 +136,8 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 {
 	bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;
 	int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS;
+	union cpuid10_edx edx;
+	u32 eax, ebx, ecx;
 
 	/*
 	 * Hybrid PMUs don't play nice with virtualization without careful
@@ -181,6 +185,19 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
 	kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed,
 					     KVM_MAX_NR_FIXED_COUNTERS);
 
+	/*
+	 * Currently, KVM doesn't support non-contiguous fixed counters; make
+	 * sure only contiguous ones are retained in kvm_pmu_cap.
+	 */
+	if (kvm_host_pmu.version >= 5) {
+		cpuid(0xa, &eax, &ebx, &ecx, &edx.full);
+		if (kvm_pmu_cap.num_counters_fixed > edx.split.num_counters_fixed)
+			kvm_pmu_cap.num_counters_fixed = edx.split.num_counters_fixed;
+	}
+
+	if (!enable_mediated_pmu && kvm_pmu_cap.num_counters_fixed > 3)
+		kvm_pmu_cap.num_counters_fixed = 3;
+
 	kvm_pmu_eventsel.INSTRUCTIONS_RETIRED =
 		perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS);
 	kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED =
@@ -751,8 +768,6 @@ static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *pmc;
-	u64 mask = ~0ull;
 
 	if (!pmu->version)
 		return 1;
@@ -760,17 +775,12 @@ 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;
 
-	*data = pmc_read_counter(pmc) & mask;
-	return 0;
+	return kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data);
 }
 
 static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
@@ -789,9 +799,16 @@ static bool kvm_need_any_pmc_intercept(struct kvm_vcpu *vcpu)
 	       pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed;
 }
 
+static bool kvm_need_perf_metrics_intercept(struct kvm_vcpu *vcpu)
+{
+	return (kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS) &&
+		!kvm_vcpu_has_perf_metrics(vcpu);
+}
+
 bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu)
 {
 	return kvm_need_any_pmc_intercept(vcpu) ||
+	       kvm_need_perf_metrics_intercept(vcpu) ||
 	       !kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu));
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_need_perf_global_ctrl_intercept);
@@ -808,6 +825,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);
 }
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 090c9bbb74f48..eb6bbc658f5b4 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -24,8 +24,8 @@
 #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED
 
 struct kvm_pmu_ops {
-	struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
-		unsigned int idx, u64 *mask);
+	int (*emulate_rdpmc)(struct kvm_vcpu *vcpu, unsigned int idx,
+			     u64 *data);
 	struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, u32 msr);
 	int (*check_rdpmc_early)(struct kvm_vcpu *vcpu, unsigned int idx);
 	bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
@@ -271,6 +271,19 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu)
 				  X86_PMC_IDX_MAX);
 }
 
+static inline u64 kvm_vcpu_get_perf_caps(struct kvm_vcpu *vcpu)
+{
+	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
+		return 0;
+
+	return vcpu->arch.perf_capabilities;
+}
+
+static inline bool kvm_vcpu_has_perf_metrics(struct kvm_vcpu *vcpu)
+{
+	return kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PERF_METRICS;
+}
+
 void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
 int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
 int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index c18286545a7ac..0517fd4bbcd7a 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -84,10 +84,15 @@ static int amd_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx)
 }
 
 /* idx is the ECX register of RDPMC instruction */
-static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-	unsigned int idx, u64 *mask)
+static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)
 {
-	return amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+	struct kvm_pmc *pmc = amd_pmu_get_pmc(vcpu_to_pmu(vcpu), idx);
+
+	if (!pmc)
+		return 1;
+
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
@@ -302,7 +307,7 @@ static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc)
 }
 
 struct kvm_pmu_ops amd_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = amd_emulate_rdpmc,
 	.msr_idx_to_pmc = amd_msr_idx_to_pmc,
 	.check_rdpmc_early = amd_check_rdpmc_early,
 	.is_valid_msr = amd_is_valid_msr,
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 7ed79894d11d2..e81b8a5423c87 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -717,6 +717,8 @@ static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu,
 	nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL);
 	nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS);
 	nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL);
+
+	nested_vmx_merge_msr_bitmaps_rw(MSR_PERF_METRICS);
 }
 
 /*
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index bfa8612fb4508..3ca61e4b0ba68 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -31,6 +31,7 @@
  */
 #define INTEL_RDPMC_GP		0
 #define INTEL_RDPMC_FIXED	INTEL_PMC_FIXED_RDPMC_BASE
+#define INTEL_RDPMC_METRICS	INTEL_PMC_FIXED_RDPMC_METRICS
 
 #define INTEL_RDPMC_TYPE_MASK	GENMASK(31, 16)
 #define INTEL_RDPMC_INDEX_MASK	GENMASK(15, 0)
@@ -85,14 +86,13 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
 	}
 }
 
-static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
-					    unsigned int idx, u64 *mask)
+static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,
+			       u64 *data)
 {
 	unsigned int type = idx & INTEL_RDPMC_TYPE_MASK;
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
-	struct kvm_pmc *counters;
+	struct kvm_pmc *counters, *pmc;
 	unsigned int num_counters;
-	u64 bitmask;
 
 	/*
 	 * The encoding of ECX for RDPMC is different for architectural versus
@@ -105,7 +105,9 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	 * as KVM doesn't support such PMUs.
 	 */
 	if (WARN_ON_ONCE(!pmu->version))
-		return NULL;
+		return 1;
+
+	idx &= INTEL_RDPMC_INDEX_MASK;
 
 	/*
 	 * General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
@@ -119,23 +121,34 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
 	case INTEL_RDPMC_FIXED:
 		counters = pmu->fixed_counters;
 		num_counters = pmu->nr_arch_fixed_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
 		break;
 	case INTEL_RDPMC_GP:
 		counters = pmu->gp_counters;
 		num_counters = pmu->nr_arch_gp_counters;
-		bitmask = pmu->counter_bitmask[KVM_PMC_GP];
 		break;
+	case INTEL_RDPMC_METRICS:
+		if (!kvm_vcpu_has_perf_metrics(vcpu))
+			return 1;
+
+		/*
+		 * The index in ECX[15:0] is implementation specific, but no
+		 * platform currently supports a non-zero index.
+		 */
+		if (idx)
+			return 1;
+
+		*data = pmu->perf_metrics;
+		return 0;
 	default:
-		return NULL;
+		return 1;
 	}
 
-	idx &= INTEL_RDPMC_INDEX_MASK;
 	if (idx >= num_counters)
-		return NULL;
+		return 1;
 
-	*mask &= bitmask;
-	return &counters[array_index_nospec(idx, num_counters)];
+	pmc = &counters[array_index_nospec(idx, num_counters)];
+	*data = pmc_read_counter(pmc);
+	return 0;
 }
 
 static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
@@ -189,14 +202,16 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
 	switch (msr) {
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		return kvm_pmu_has_perf_global_ctrl(pmu);
+	case MSR_PERF_METRICS:
+		return kvm_vcpu_has_perf_metrics(vcpu);
 	case MSR_IA32_PEBS_ENABLE:
-		ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
+		ret = kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_PEBS_FORMAT;
 		break;
 	case MSR_IA32_DS_AREA:
 		ret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);
 		break;
 	case MSR_PEBS_DATA_CFG:
-		perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+		perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 		ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) &&
 			((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3);
 		break;
@@ -346,6 +361,9 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	case MSR_CORE_PERF_FIXED_CTR_CTRL:
 		msr_info->data = pmu->fixed_ctr_ctrl;
 		break;
+	case MSR_PERF_METRICS:
+		msr_info->data = pmu->perf_metrics;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		msr_info->data = pmu->pebs_enable;
 		break;
@@ -395,6 +413,14 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		if (pmu->fixed_ctr_ctrl != data)
 			reprogram_fixed_counters(pmu, data);
 		break;
+	case MSR_PERF_METRICS:
+		/*
+		 * Bits [63:32] are ignored on hardware that supports only
+		 * level-1 metrics, but may be valid on hardware that supports
+		 * level-2 metrics. Preserve guest writes verbatim.
+		 */
+		pmu->perf_metrics = data;
+		break;
 	case MSR_IA32_PEBS_ENABLE:
 		if (data & pmu->pebs_enable_rsvd)
 			return 1;
@@ -465,11 +491,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
  * different perf_event is already utilizing the requested counter, but the end
  * result is the same (ignoring the fact that using a general purpose counter
  * will likely exacerbate counter contention).
- *
- * Forcibly inlined to allow asserting on @index at build time, and there should
- * never be more than one user.
  */
-static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
+static u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 {
 	const enum perf_hw_id fixed_pmc_perf_ids[] = {
 		[0] = PERF_COUNT_HW_INSTRUCTIONS,
@@ -478,8 +501,13 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
 	};
 	u64 eventsel;
 
-	BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);
-	BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS);
+	/*
+	 * Fixed counters 3 and above don't have a corresponding generic
+	 * hardware perf event, and KVM does not intend to emulate them on
+	 * non-mediated vPMU.
+	 */
+	if (index >= ARRAY_SIZE(fixed_pmc_perf_ids))
+		return 0;
 
 	/*
 	 * Yell if perf reports support for a fixed counter but perf doesn't
@@ -549,7 +577,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 		pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
 	}
 
-	perf_capabilities = vcpu_get_perf_capabilities(vcpu);
+	perf_capabilities = kvm_vcpu_get_perf_caps(vcpu);
 	if (intel_pmu_lbr_is_compatible(vcpu) &&
 	    (perf_capabilities & PERF_CAP_LBR_FMT))
 		memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps));
@@ -575,6 +603,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 	counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
 			 ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
 	pmu->global_ctrl_rsvd = counter_rsvd;
+	if (perf_capabilities & PERF_CAP_PERF_METRICS)
+		pmu->global_ctrl_rsvd &= ~GLOBAL_CTRL_EN_PERF_METRICS;
 
 	/*
 	 * GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
@@ -631,6 +661,9 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu)
 
 static void intel_pmu_reset(struct kvm_vcpu *vcpu)
 {
+	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+	pmu->perf_metrics = 0;
 	intel_pmu_release_guest_lbr_event(vcpu);
 }
 
@@ -801,6 +834,9 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u64 global_status, toggle;
 
+	if (kvm_vcpu_has_perf_metrics(vcpu))
+		wrmsrq(MSR_PERF_METRICS, pmu->perf_metrics);
+
 	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
 	toggle = pmu->global_status ^ global_status;
 	if (global_status & toggle)
@@ -829,10 +865,23 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
 	 */
 	if (pmu->fixed_ctr_ctrl_hw)
 		wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
+
+	if (kvm_vcpu_has_perf_metrics(vcpu)) {
+		pmu->perf_metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);
+		/*
+		 * The SDM requires restoring fixed counter 3 before
+		 * PERF_METRICS. However, this path writes 0 to PERF_METRICS
+		 * before fixed counter 3. For this all-zero case, the
+		 * resulting hardware state is therefore the same regardless
+		 * of write order.
+		 */
+		if (pmu->perf_metrics)
+			wrmsrq(MSR_PERF_METRICS, 0);
+	}
 }
 
 struct kvm_pmu_ops intel_pmu_ops __initdata = {
-	.rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
+	.emulate_rdpmc = intel_emulate_rdpmc,
 	.msr_idx_to_pmc = intel_msr_idx_to_pmc,
 	.is_valid_msr = intel_is_valid_msr,
 	.get_msr = intel_pmu_get_msr,
diff --git a/arch/x86/kvm/vmx/pmu_intel.h b/arch/x86/kvm/vmx/pmu_intel.h
index 5d9357640aa18..dd447d8b6fdde 100644
--- a/arch/x86/kvm/vmx/pmu_intel.h
+++ b/arch/x86/kvm/vmx/pmu_intel.h
@@ -4,19 +4,9 @@
 
 #include <linux/kvm_host.h>
 
-#include "cpuid.h"
-
-static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu)
-{
-	if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))
-		return 0;
-
-	return vcpu->arch.perf_capabilities;
-}
-
 static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu)
 {
-	return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0;
+	return (kvm_vcpu_get_perf_caps(vcpu) & PERF_CAP_FW_WRITES) != 0;
 }
 
 bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e3bfe6aca1a0e..8cb9d642277aa 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4277,6 +4277,10 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
 				  MSR_TYPE_RW, intercept);
 	vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL,
 				  MSR_TYPE_RW, intercept);
+
+	intercept = !has_mediated_pmu || !kvm_vcpu_has_perf_metrics(vcpu);
+	vmx_set_intercept_for_msr(vcpu, MSR_PERF_METRICS,
+				  MSR_TYPE_RW, intercept);
 }
 
 static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
@@ -8023,14 +8027,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 static __init u64 vmx_get_perf_capabilities(void)
 {
 	u64 perf_cap = PERF_CAP_FW_WRITES;
-	u64 host_perf_cap = 0;
 
 	if (!enable_pmu)
 		return 0;
 
-	if (boot_cpu_has(X86_FEATURE_PDCM))
-		rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);
-
 	if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) &&
 	    !enable_mediated_pmu) {
 		x86_perf_get_lbr(&vmx_lbr_caps);
@@ -8043,11 +8043,11 @@ static __init u64 vmx_get_perf_capabilities(void)
 		if (!vmx_lbr_caps.has_callstack)
 			memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps));
 		else if (vmx_lbr_caps.nr)
-			perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT;
+			perf_cap |= kvm_host.perf_capabilities & PERF_CAP_LBR_FMT;
 	}
 
 	if (vmx_pebs_supported()) {
-		perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK;
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PEBS_MASK;
 
 		/*
 		 * Disallow adaptive PEBS as it is functionally broken, can be
@@ -8072,6 +8072,9 @@ static __init u64 vmx_get_perf_capabilities(void)
 		perf_cap &= ~PERF_CAP_PEBS_BASELINE;
 	}
 
+	if (enable_mediated_pmu)
+		perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS;
+
 	return perf_cap;
 }
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d94b59140c452..d349224d27340 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7083,6 +7083,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
 	if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
 		rdmsrq(MSR_IA32_ARCH_CAPABILITIES, kvm_host.arch_capabilities);
 
+	if (boot_cpu_has(X86_FEATURE_PDCM))
+		rdmsrq_safe(MSR_IA32_PERF_CAPABILITIES,
+			    &kvm_host.perf_capabilities);
+
 	WARN_ON_ONCE(kvm_nr_uret_msrs);
 
 	r = ops->hardware_setup();
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index eff29645719bc..e7745e2cd543a 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -331,6 +331,7 @@
 #define PERF_CAP_PEBS_FORMAT		0xf00
 #define PERF_CAP_FW_WRITES		BIT_ULL(13)
 #define PERF_CAP_PEBS_BASELINE		BIT_ULL(14)
+#define PERF_CAP_PERF_METRICS		BIT_ULL(15)
 #define PERF_CAP_PEBS_TIMING_INFO	BIT_ULL(17)
 #define PERF_CAP_PEBS_MASK		(PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \
 					 PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h
index 608ed83d7c6a6..6c19503e0bb7d 100644
--- a/tools/testing/selftests/kvm/include/x86/pmu.h
+++ b/tools/testing/selftests/kvm/include/x86/pmu.h
@@ -52,6 +52,9 @@
 /* Fixed PMC controls, Intel only. */
 #define FIXED_PMC_GLOBAL_CTRL_ENABLE(_idx)	BIT_ULL((32 + (_idx)))
 
+/* PERF_METRICS enable, Intel only. */
+#define PERF_METRICS_GLOBAL_CTRL_ENABLE		BIT_ULL(48)
+
 #define FIXED_PMC_KERNEL			BIT_ULL(0)
 #define FIXED_PMC_USER				BIT_ULL(1)
 #define FIXED_PMC_ANYTHREAD			BIT_ULL(2)
diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
index c3e784e16348c..5abcf21d86124 100644
--- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2023, Tencent, Inc.
  */
 #include <x86intrin.h>
+#include <linux/bitfield.h>
 
 #include "pmu.h"
 #include "processor.h"
@@ -254,17 +255,20 @@ do {										\
 	);									\
 } while (0)
 
-#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+#define GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP)				\
 do {										\
-	wrmsr(_pmc_msr, 0);							\
-										\
 	if (this_cpu_has(X86_FEATURE_CLFLUSHOPT))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflushopt %[m]", FEP);	\
 	else if (this_cpu_has(X86_FEATURE_CLFLUSH))				\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "clflush  %[m]", FEP);	\
 	else									\
 		GUEST_MEASURE_EVENT(_ctrl_msr, _value, "nop", FEP);		\
-										\
+} while (0)
+
+#define GUEST_TEST_EVENT(_idx, _pmc, _pmc_msr, _ctrl_msr, _value, FEP)		\
+do {										\
+	wrmsr(_pmc_msr, 0);							\
+	GUEST_RUN_PAYLOAD(_ctrl_msr, _value, FEP);				\
 	guest_assert_event_count(_idx, _pmc, _pmc_msr);				\
 } while (0)
 
@@ -412,7 +416,7 @@ static void test_arch_events(u8 pmu_version, u64 perf_capabilities)
  * other than PMCs in the future.
  */
 #define MAX_NR_GP_COUNTERS	8
-#define MAX_NR_FIXED_COUNTERS	3
+#define MAX_NR_FIXED_COUNTERS	4
 
 #define GUEST_ASSERT_PMC_MSR_ACCESS(insn, msr, expect_gp, vector)		\
 __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
@@ -653,8 +657,81 @@ static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities)
 	pmu_vm_free(vm, vcpus);
 }
 
+static void __guest_test_perf_metrics(void)
+{
+	int retiring, bad_spec, fe_bound, be_bound, sum;
+	u64 global_ctrl, metrics;
+
+	if ((guest_get_pmu_version() < 2) ||	/* Does guest have GLOBAL_CTRL? */
+	    !this_cpu_has(X86_FEATURE_PDCM) ||
+	    !(rdmsr(MSR_IA32_PERF_CAPABILITIES) & PERF_CAP_PERF_METRICS))
+		return;
+
+	wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+	wrmsr(MSR_CORE_PERF_FIXED_CTR3, 0);
+	wrmsr(MSR_PERF_METRICS, 0);
+
+	/* Enable fixed ctr3 (TOPDOWN.SLOTS) and PERF_METRICS. */
+	wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(3, FIXED_PMC_KERNEL));
+	global_ctrl = FIXED_PMC_GLOBAL_CTRL_ENABLE(3) |
+		      PERF_METRICS_GLOBAL_CTRL_ENABLE;
+
+	GUEST_RUN_PAYLOAD(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl, "");
+
+	/* Check test results. */
+	metrics = rdmsr(MSR_PERF_METRICS);
+	retiring = FIELD_GET(GENMASK_ULL(7, 0), metrics);
+	bad_spec = FIELD_GET(GENMASK_ULL(15, 8), metrics);
+	fe_bound = FIELD_GET(GENMASK_ULL(23, 16), metrics);
+	be_bound = FIELD_GET(GENMASK_ULL(31, 24), metrics);
+
+	/*
+	 * Be conservative: the measured payload definitely retires work, so
+	 * Retiring should be non-zero.
+	 */
+	GUEST_ASSERT_NE(metrics, 0);
+	GUEST_ASSERT_NE(retiring, 0);
+
+	/*
+	 * Each level-1 Topdown metric is an integer fraction of 0xff.
+	 * A +/-3 error margin is chosen for a loose sanity check.
+	 */
+	sum = retiring + bad_spec + fe_bound + be_bound;
+	GUEST_ASSERT(sum >= 0xfc && sum <= 0x102);
+
+	/* Sanity check after PERF_METRICS disabled. */
+	__asm__ __volatile__("loop ." : "+c"((int){NUM_LOOPS}));
+	GUEST_ASSERT_EQ(rdmsr(MSR_PERF_METRICS), metrics);
+	wrmsr(MSR_PERF_METRICS, 0xdeaddead);
+
+	guest_test_rdpmc(INTEL_RDPMC_METRICS, true, 0xdeaddead);
+}
+
+static void guest_test_perf_metrics(void)
+{
+	__guest_test_perf_metrics();
+	GUEST_DONE();
+}
+
+static void test_perf_metrics(u8 pmu_version, u64 perf_capabilities)
+{
+	struct kvm_vcpu **vcpus;
+	struct kvm_vm *vm;
+
+	pr_info("Testing Perf Metrics, PMU version %u, perf_caps = %lx\n",
+		pmu_version, perf_capabilities);
+
+	vm = pmu_vm_create_with_vcpus(1, guest_test_perf_metrics,
+				      pmu_version, perf_capabilities, &vcpus);
+
+	run_vcpu(vcpus[0]);
+
+	pmu_vm_free(vm, vcpus);
+}
+
 static void test_intel_counters(void)
 {
+	u64 advertised_perf_caps = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
 	u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION);
 	unsigned int i;
 	u8 v;
@@ -662,6 +739,7 @@ static void test_intel_counters(void)
 	const u64 perf_caps[] = {
 		0,
 		PMU_CAP_FW_WRITES,
+		PERF_CAP_PERF_METRICS,
 	};
 
 	/*
@@ -699,9 +777,14 @@ static void test_intel_counters(void)
 			if (!kvm_has_perf_caps && perf_caps[i])
 				continue;
 
+			/* Ignore unsupported features. */
+			if (perf_caps[i] & ~advertised_perf_caps)
+				continue;
+
 			test_arch_events(v, perf_caps[i]);
 			test_gp_counters(v, perf_caps[i]);
 			test_fixed_counters(v, perf_caps[i]);
+			test_perf_metrics(v, perf_caps[i]);
 		}
 	}
 }

25/3 2026/08/17 19:13 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14505
							output: 23
							thoughts: 3047
						

Thoughts:
**Analysis of Intel Topdown Perf Metrics Support in KVM Patch**

Alright, let's break down this patch. My initial thought is to identify potential uninitialized memory reads or information leaks. This patch introduces support for Intel Topdown Perf Metrics within KVM, touching a significant number of files across the architecture, which means a thorough review is critical.

The primary concern is the addition of the `perf_metrics` field to `struct kvm_pmu` and `perf_capabilities` to `struct kvm_host_values`.  `struct kvm_pmu` resides within the `struct kvm_vcpu_arch`, which itself is part of `struct kvm_vcpu`. Since `kvm_vcpu` is allocated with `kmem_cache_zalloc`, I'm comfortable that `perf_metrics` is initialized to zero by default. The `intel_pmu_reset` function explicitly reinforces this by setting `pmu->perf_metrics = 0;`. Likewise, `kvm_host.perf_capabilities` is initialized using `rdmsrq_safe`. So, that initial concern seems to be addressed.

Next, I need to check how `perf_metrics` is handled with regards to user space interaction. MSRs are read/written via `KVM_GET_MSRS` and `KVM_SET_MSRS`. The `intel_pmu_get_msr` function handles `MSR_PERF_METRICS`, returning `pmu->perf_metrics`. While the field *could* have been uninitialized, the zero-initialization ensures this is safe.

Then, there's `intel_emulate_rdpmc`. This function is responsible for emulating the `RDPMC` instruction and potentially reading/writing `pmu->perf_metrics` depending on the index `idx`. If `idx` is `INTEL_RDPMC_METRICS`, and we have the feature supported, then `*data = pmu->perf_metrics;` is performed. This is also safe, as `pmu->perf_metrics` is zero-initialized and updated correctly during normal operation. The related function for AMD also appears safe.

The modification of `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4 is next. This increases the size of the `fixed_counters` array within `struct kvm_pmu`. I need to ensure that loops and other operations which used to assume 3 fixed counters are updated. Looks like the `msrs_to_save_pmu` array in `msrs.c` is updated, and the `BUILD_BUG_ON` checks are also updated to ensure consistency. `intel_get_fixed_pmc_eventsel` also takes this into account, returning 0 safely for the new index 3. The indexing logic within `intel_emulate_rdpmc` with respect to the fixed counters is also handled safely.

Now the core of the implementation is scrutinized to find potential leaks. It looks like `pmu->perf_metrics` can be updated either through the MSR write (`intel_pmu_set_msr`) or via context switching in the mediated PMU implementation (`intel_mediated_pmu_put`, `intel_mediated_pmu_load`).  In the mediated PMU case the hardware itself is read with `rdpmc`, so the state is managed transparently with the hardware registers. Zeroing out `perf_metrics` upon VCPU creation and in `intel_pmu_reset`, combined with updates from hardware reads and MSR writes, makes it safe.

Finally, I checked `kvm_pmu_rdpmc` and ensure its safe and returns 0 when appropriate.

In summary, this patch looks very well-designed from a memory safety perspective. The careful initialization, explicit zeroing, and robust handling of the fixed counter array size change give me confidence that the Intel Topdown Perf Metrics support is added without introducing any information leaks or uninitialized memory reads.



26/3 2026/08/17 19:14 tool grepper
0m Args:
{
  "Expression": "kvm_emulate_rdpmc"
}

Results:
{
  "Output": "arch/x86/kvm/svm/svm.c=3342=static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {\n--\narch/x86/kvm/svm/svm.c-3378-\t[SVM_EXIT_VINTR]\t\t\t= interrupt_window_interception,\narch/x86/kvm/svm/svm.c:3379:\t[SVM_EXIT_RDPMC]\t\t\t= kvm_emulate_rdpmc,\narch/x86/kvm/svm/svm.c-3380-\t[SVM_EXIT_CPUID]\t\t\t= kvm_emulate_cpuid,\n--\narch/x86/kvm/vmx/vmx.c=6331=static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {\n--\narch/x86/kvm/vmx/vmx.c-6345-\t[EXIT_REASON_INVLPG]\t\t      = handle_invlpg,\narch/x86/kvm/vmx/vmx.c:6346:\t[EXIT_REASON_RDPMC]                   = kvm_emulate_rdpmc,\narch/x86/kvm/vmx/vmx.c-6347-\t[EXIT_REASON_VMCALL]                  = kvm_emulate_hypercall,\n--\narch/x86/kvm/x86.c=746=void kvm_invalidate_pcid(struct kvm_vcpu *vcpu, unsigned long pcid)\n--\narch/x86/kvm/x86.c-788-\narch/x86/kvm/x86.c:789:int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu)\narch/x86/kvm/x86.c-790-{\n--\narch/x86/kvm/x86.c-802-}\narch/x86/kvm/x86.c:803:EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_rdpmc);\narch/x86/kvm/x86.c-804-\n--\narch/x86/kvm/x86.h=495=int kvm_emulate_xsetbv(struct kvm_vcpu *vcpu);\narch/x86/kvm/x86.h:496:int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu);\narch/x86/kvm/x86.h-497-\n"
}

27/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19120
							output: 42
							thoughts: 32
						

Thoughts:
Here's my take on those thoughts, assuming I'm deep in the weeds of KVM/x86 emulation:

**Analyzing the KVM RDPMC Emulation**

Okay, so I need to dig into the guts of how KVM handles the `RDPMC` instruction on x86. Let's start with `kvm_emulate_rdpmc`, which I'm assuming is the entry point for this emulation. I know that `arch/x86/kvm/x86.c` is the likely place to find this, so that's where I'll be looking. I'm hoping to understand how it interacts with the performance monitoring unit (PMU) within the guest, how it handles the different PMU registers, and any potential security implications that might arise. This emulation is crucial for running guests correctly and safely, so any subtle bugs here are a big deal. I'll be particularly interested in whether it uses the host's PMU, emulates it, or uses a combination of techniques, and how it deals with the access rights of the guest to the various PMU counters. Let's see what we've got.



28/3 2026/08/17 19:14 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/x86.c",
  "FirstLine": 780,
  "LineCount": 30
}

Results:
{
  "Contents": " 780:\t\t\treturn;\n 781:\t\n 782:\t\tfor (i = 0; i \u003c KVM_MMU_NUM_PREV_ROOTS; i++)\n 783:\t\t\tif (kvm_get_pcid(vcpu, mmu-\u003eprev_roots[i].pgd) == pcid)\n 784:\t\t\t\troots_to_free |= KVM_MMU_ROOT_PREVIOUS(i);\n 785:\t\n 786:\t\tkvm_mmu_free_roots(vcpu-\u003ekvm, mmu, roots_to_free);\n 787:\t}\n 788:\t\n 789:\tint kvm_emulate_rdpmc(struct kvm_vcpu *vcpu)\n 790:\t{\n 791:\t\tu32 pmc = kvm_ecx_read(vcpu);\n 792:\t\tu64 data;\n 793:\t\n 794:\t\tif (kvm_pmu_rdpmc(vcpu, pmc, \u0026data)) {\n 795:\t\t\tkvm_inject_gp(vcpu, 0);\n 796:\t\t\treturn 1;\n 797:\t\t}\n 798:\t\n 799:\t\tkvm_eax_write(vcpu, data);\n 800:\t\tkvm_edx_write(vcpu, data \u003e\u003e 32);\n 801:\t\treturn kvm_skip_emulated_instruction(vcpu);\n 802:\t}\n 803:\tEXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_rdpmc);\n 804:\t\n 805:\tint kvm_emulate_as_nop(struct kvm_vcpu *vcpu)\n 806:\t{\n 807:\t\treturn kvm_skip_emulated_instruction(vcpu);\n 808:\t}\n 809:\t\n"
}

29/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19953
							output: 19
							thoughts: 96
						

Thoughts:
**Analyzing the KVM PMU Read Path**

Okay, so I'm looking at this `kvm_pmu_rdpmc` function and how it interacts with the return value. If it returns `1`, we're triggering a General Protection (GP) fault, which is expected behavior, and it also returns `1`. The `data` variable isn't initialized, but thankfully, it's also not used in that particular branch. That's good, no undefined behavior there.

The interesting scenario is when `kvm_pmu_rdpmc` returns `0`. In that case, we are writing data to eax and edx. This is the normal read path, which implies that it's the expected read path that returns a value. Since no other writes are made to `data`, I am confident in saying that the process is safe, and that no uninitialized data is being used.

Now, let's dive into `em_rdpmc` within `arch/x86/kvm/emulate.c`. Time to see how that function is being implemented.



30/3 2026/08/17 19:14 tool grepper
0m Args:
{
  "Expression": "em_rdpmc"
}

Results:
{
  "Output": "arch/x86/kvm/emulate.c=3208=static int em_rdtsc(struct x86_emulate_ctxt *ctxt)\n--\narch/x86/kvm/emulate.c-3217-\narch/x86/kvm/emulate.c:3218:static int em_rdpmc(struct x86_emulate_ctxt *ctxt)\narch/x86/kvm/emulate.c-3219-{\n--\narch/x86/kvm/emulate.c=4377=static const struct opcode twobyte_table[256] = {\n--\narch/x86/kvm/emulate.c-4408-\tII(ImplicitOps | Priv, em_rdmsr, rdmsr),\narch/x86/kvm/emulate.c:4409:\tIIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc),\narch/x86/kvm/emulate.c-4410-\tI(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack, em_sysenter),\n"
}

31/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20599
							output: 42
							thoughts: 11
						

Thoughts:
Okay, let's dive into this `em_rdpmc` code.

**Diving into `em_rdpmc`**

Alright, let's see what's going on here. I need to understand this `em_rdpmc` function; it's probably critical to something I'm working on. The name strongly suggests it's related to the `rdpmc` instruction, likely the one that reads performance monitor counters. My immediate questions are, what architecture are we dealing with? Is this kernel-level code, user-level code, or maybe a hypervisor component? Knowing the context is crucial to understanding the assumptions being made. I'll need to look for any preprocessor directives or other hints about the target platform. I'm also anticipating details about how the function handles potential errors or security issues since access to performance counters is often restricted. I'll be looking for any context saving or restoring too since the function likely operates in a privileged mode. I'm also going to check whether the `rdpmc` instruction is used directly, or is abstracted behind some other mechanism. Let's start digging.



32/3 2026/08/17 19:14 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/emulate.c",
  "FirstLine": 3218,
  "LineCount": 20
}

Results:
{
  "Contents": "3218:\tstatic int em_rdpmc(struct x86_emulate_ctxt *ctxt)\n3219:\t{\n3220:\t\tu64 pmc;\n3221:\t\n3222:\t\tif (ctxt-\u003eops-\u003eread_pmc(ctxt, reg_read(ctxt, VCPU_REGS_RCX), \u0026pmc))\n3223:\t\t\treturn emulate_gp(ctxt, 0);\n3224:\t\t*reg_write(ctxt, VCPU_REGS_RAX) = (u32)pmc;\n3225:\t\t*reg_write(ctxt, VCPU_REGS_RDX) = pmc \u003e\u003e 32;\n3226:\t\treturn X86EMUL_CONTINUE;\n3227:\t}\n3228:\t\n3229:\tstatic int em_mov(struct x86_emulate_ctxt *ctxt)\n3230:\t{\n3231:\t\tmemcpy(ctxt-\u003edst.valptr, ctxt-\u003esrc.valptr, sizeof(ctxt-\u003esrc.valptr));\n3232:\t\treturn X86EMUL_CONTINUE;\n3233:\t}\n3234:\t\n3235:\tstatic int em_movbe(struct x86_emulate_ctxt *ctxt)\n3236:\t{\n3237:\t\tu16 tmp;\n"
}

33/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21255
							output: 20
							thoughts: 60
						

Thoughts:
Here's my understanding of the situation, given my expertise:

**My Interpretation of the PMC Read Operation**

Okay, so I'm looking at this bit of code and what jumps out at me is this: a crucial function, `ctxt->ops->read_pmc`, is responsible for reading Performance Monitoring Counter (PMC) values. If this function happens to return a zero value (likely indicating success), the PMC data it retrieved is then placed into registers RAX and RDX. From the context, and my familiarity with the codebase, I can deduce that `read_pmc` is, in all likelihood, an alias to the `emulator_read_pmc` function that's defined within the `arch/x86/kvm/x86.c` file. I'm reasonably certain of this as the naming convention is consistent with KVM's design and internal structure for handling emulation. Basically, it seems like a PMC read operation is being performed and the success/data from that operation is being reported by way of a return value of 0 and through the placement of that data into RAX and RDX. This pattern is very familiar; it's a typical approach in low-level code for efficient data transfer and success reporting.



34/3 2026/08/17 19:14 tool grepper
0m Args:
{
  "Expression": "emulator_read_pmc"
}

Results:
{
  "Output": "arch/x86/kvm/x86.c=5683=static int emulator_check_rdpmc_early(struct x86_emulate_ctxt *ctxt, u32 pmc)\n--\narch/x86/kvm/x86.c-5687-\narch/x86/kvm/x86.c:5688:static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,\narch/x86/kvm/x86.c-5689-\t\t\t     u32 pmc, u64 *pdata)\n--\narch/x86/kvm/x86.c=5814=static const struct x86_emulate_ops emulate_ops = {\n--\narch/x86/kvm/x86.c-5843-\t.check_rdpmc_early   = emulator_check_rdpmc_early,\narch/x86/kvm/x86.c:5844:\t.read_pmc            = emulator_read_pmc,\narch/x86/kvm/x86.c-5845-\t.halt                = emulator_halt,\n"
}

35/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21909
							output: 43
							thoughts: 12
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:

**Investigating `emulator_read_pmc`**

Alright, time to dive in. Let's get right to it – I need to take a look at the `emulator_read_pmc` function. It's the key to understanding how the emulator is interacting with the performance monitoring counters, right? Gotta refresh my memory on the implementation details and how it's structured. I'm especially interested in any potential bottlenecks or areas where performance optimization might be possible, or areas of potential subtle bugs. I want to make sure the data being pulled is correct.



36/3 2026/08/17 19:14 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/x86.c",
  "FirstLine": 5688,
  "LineCount": 10
}

Results:
{
  "Contents": "5688:\tstatic int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,\n5689:\t\t\t\t     u32 pmc, u64 *pdata)\n5690:\t{\n5691:\t\treturn kvm_pmu_rdpmc(emul_to_vcpu(ctxt), pmc, pdata);\n5692:\t}\n5693:\t\n5694:\tstatic void emulator_halt(struct x86_emulate_ctxt *ctxt)\n5695:\t{\n5696:\t\temul_to_vcpu(ctxt)-\u003earch.halt_request = 1;\n5697:\t}\n"
}

37/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22295
							output: 20
							thoughts: 299
						

Thoughts:
**My Analysis of Potential Memory Leakage in the Intel PMU Implementation**

Okay, so this code path directly uses `kvm_pmu_rdpmc`, which means the same concerns about memory safety apply here as they do elsewhere. My immediate question is: where else is `pmu->perf_metrics` actually used? I quickly found it in `intel_pmu_get_msr`. Specifically, it assigns `pmu->perf_metrics` to `msr_info->data`. This data, `msr_info->data`, is then returned to user space through `KVM_GET_MSRS`.

Given this, I have to ensure that `pmu->perf_metrics` is properly initialized before it's exposed. Fortunately, it's initialized to zero in `intel_pmu_reset`, and since `struct kvm_pmu` itself is zero-allocated, I initially thought there's no way for uninitialized memory to leak out.

But wait, I need to confirm that `intel_pmu_reset` is *always* called before `KVM_GET_MSRS`. My first thought is: can a VM configuration happen that would avoid this? I need to trace this back. Let's see... `kvm_vcpu_init` calls `kvm_arch_vcpu_init`, which in turn calls `kvm_pmu_init`. Okay, good. And that `kvm_pmu_init` then calls *both* `kvm_pmu_refresh` and `kvm_pmu_reset`. That's reassuring. So, `intel_pmu_reset` *is* called during vCPU initialization.

I should probably double-check the exact initialization sequence within `intel_pmu_init` to be absolutely certain, but so far, this appears to be safe.



38/3 2026/08/17 19:14 tool grepper
0m Args:
{
  "Expression": "intel_pmu_init"
}

Results:
{
  "Output": "arch/x86/events/core.c=2133=static int __init init_hw_perf_events(void)\n--\narch/x86/events/core.c-2141-\tcase X86_VENDOR_INTEL:\narch/x86/events/core.c:2142:\t\terr = intel_pmu_init();\narch/x86/events/core.c-2143-\t\tbreak;\n--\narch/x86/events/intel/core.c=7667=static const struct { enum hybrid_pmu_type id; char *name; } intel_hybrid_pmu_type_map[] __initconst = {\n--\narch/x86/events/intel/core.c-7672-\narch/x86/events/intel/core.c:7673:static __always_inline int intel_pmu_init_hybrid(enum hybrid_pmu_type pmus)\narch/x86/events/intel/core.c-7674-{\n--\narch/x86/events/intel/core.c=7714=static __always_inline void intel_pmu_ref_cycles_ext(void)\n--\narch/x86/events/intel/core.c-7719-\narch/x86/events/intel/core.c:7720:static __always_inline void intel_pmu_init_glc(struct pmu *pmu)\narch/x86/events/intel/core.c-7721-{\n--\narch/x86/events/intel/core.c-7745-\narch/x86/events/intel/core.c:7746:static __always_inline void intel_pmu_init_glc_hybrid(struct pmu *pmu)\narch/x86/events/intel/core.c-7747-{\narch/x86/events/intel/core.c:7748:\tintel_pmu_init_glc(pmu);\narch/x86/events/intel/core.c-7749-\n--\narch/x86/events/intel/core.c-7754-\narch/x86/events/intel/core.c:7755:static __always_inline void intel_pmu_init_grt(struct pmu *pmu)\narch/x86/events/intel/core.c-7756-{\n--\narch/x86/events/intel/core.c-7775-\narch/x86/events/intel/core.c:7776:static __always_inline void intel_pmu_init_cmt(struct pmu *pmu)\narch/x86/events/intel/core.c-7777-{\narch/x86/events/intel/core.c:7778:\tintel_pmu_init_grt(pmu);\narch/x86/events/intel/core.c-7779-\tmemcpy(hybrid_var(pmu, hw_cache_extra_regs),\n--\narch/x86/events/intel/core.c-7784-\narch/x86/events/intel/core.c:7785:static __always_inline void intel_pmu_init_lnc(struct pmu *pmu)\narch/x86/events/intel/core.c-7786-{\narch/x86/events/intel/core.c:7787:\tintel_pmu_init_glc(pmu);\narch/x86/events/intel/core.c-7788-\thybrid(pmu, event_constraints) = intel_lnc_event_constraints;\n--\narch/x86/events/intel/core.c-7795-\narch/x86/events/intel/core.c:7796:static __always_inline void intel_pmu_init_pnc(struct pmu *pmu)\narch/x86/events/intel/core.c-7797-{\narch/x86/events/intel/core.c:7798:\tintel_pmu_init_glc(pmu);\narch/x86/events/intel/core.c-7799-\tx86_pmu.flags \u0026= ~PMU_FL_HAS_RSP_1;\n--\narch/x86/events/intel/core.c-7810-\narch/x86/events/intel/core.c:7811:static __always_inline void intel_pmu_init_cyc(struct pmu *pmu)\narch/x86/events/intel/core.c-7812-{\narch/x86/events/intel/core.c:7813:\tintel_pmu_init_pnc(pmu);\narch/x86/events/intel/core.c-7814-\tmemcpy(hybrid_var(pmu, hw_cache_extra_regs),\n--\narch/x86/events/intel/core.c-7817-\narch/x86/events/intel/core.c:7818:static __always_inline void intel_pmu_init_skt(struct pmu *pmu)\narch/x86/events/intel/core.c-7819-{\narch/x86/events/intel/core.c:7820:\tintel_pmu_init_cmt(pmu);\narch/x86/events/intel/core.c-7821-\thybrid(pmu, event_constraints) = intel_skt_event_constraints;\n--\narch/x86/events/intel/core.c-7827-/* Hybrid client variant. */\narch/x86/events/intel/core.c:7828:static __always_inline void intel_pmu_init_dkt_hybrid(struct pmu *pmu)\narch/x86/events/intel/core.c-7829-{\narch/x86/events/intel/core.c:7830:\tintel_pmu_init_skt(pmu);\narch/x86/events/intel/core.c-7831-\thybrid(pmu, pebs_constraints) = intel_dkt_pebs_event_constraints;\n--\narch/x86/events/intel/core.c-7838- */\narch/x86/events/intel/core.c:7839:static __always_inline void intel_pmu_init_dkt(struct pmu *pmu)\narch/x86/events/intel/core.c-7840-{\narch/x86/events/intel/core.c:7841:\tintel_pmu_init_dkt_hybrid(pmu);\narch/x86/events/intel/core.c-7842-\tmemcpy(hybrid_var(pmu, hw_cache_extra_regs),\n--\narch/x86/events/intel/core.c-7845-\narch/x86/events/intel/core.c:7846:static __always_inline void intel_pmu_init_arw(struct pmu *pmu)\narch/x86/events/intel/core.c-7847-{\narch/x86/events/intel/core.c:7848:\tintel_pmu_init_grt(pmu);\narch/x86/events/intel/core.c-7849-\tx86_pmu.flags \u0026= ~PMU_FL_HAS_RSP_1;\n--\narch/x86/events/intel/core.c-7858-\narch/x86/events/intel/core.c:7859:__init int intel_pmu_init(void)\narch/x86/events/intel/core.c-7860-{\n--\narch/x86/events/intel/core.c-8154-\tcase INTEL_ATOM_GRACEMONT:\narch/x86/events/intel/core.c:8155:\t\tintel_pmu_init_grt(NULL);\narch/x86/events/intel/core.c-8156-\t\tintel_pmu_pebs_data_source_grt();\n--\narch/x86/events/intel/core.c-8167-\tcase INTEL_ATOM_CRESTMONT_X:\narch/x86/events/intel/core.c:8168:\t\tintel_pmu_init_cmt(NULL);\narch/x86/events/intel/core.c-8169-\t\tintel_pmu_pebs_data_source_cmt();\n--\narch/x86/events/intel/core.c-8179-\tcase INTEL_ATOM_DARKMONT_X:\narch/x86/events/intel/core.c:8180:\t\tintel_pmu_init_dkt(NULL);\narch/x86/events/intel/core.c-8181-\t\tintel_pmu_pebs_data_source_cmt();\n--\narch/x86/events/intel/core.c-8515-\tcase INTEL_DIAMONDRAPIDS_X:\narch/x86/events/intel/core.c:8516:\t\tintel_pmu_init_pnc(NULL);\narch/x86/events/intel/core.c-8517-\t\tx86_pmu.pebs_latency_data = pnc_latency_data;\n--\narch/x86/events/intel/core.c-8523-\tglc_common:\narch/x86/events/intel/core.c:8524:\t\tintel_pmu_init_glc(NULL);\narch/x86/events/intel/core.c-8525-\t\tintel_pmu_pebs_data_source_skl(true);\n--\narch/x86/events/intel/core.c-8548-\t\t */\narch/x86/events/intel/core.c:8549:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8550-\n--\narch/x86/events/intel/core.c-8563-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8564:\t\tintel_pmu_init_glc_hybrid(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8565-\t\tif (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) {\n--\narch/x86/events/intel/core.c-8595-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8596:\t\tintel_pmu_init_grt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8597-\n--\narch/x86/events/intel/core.c-8606-\tcase INTEL_ARROWLAKE_U:\narch/x86/events/intel/core.c:8607:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8608-\n--\narch/x86/events/intel/core.c-8620-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8621:\t\tintel_pmu_init_glc_hybrid(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8622-\t\tpmu-\u003eextra_regs = intel_rwc_extra_regs;\n--\narch/x86/events/intel/core.c-8625-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8626:\t\tintel_pmu_init_cmt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8627-\n--\narch/x86/events/intel/core.c-8637-\narch/x86/events/intel/core.c:8638:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8639-\n--\narch/x86/events/intel/core.c-8641-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8642:\t\tintel_pmu_init_lnc(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8643-\t\t/* Initialize Atom core specific PerfMon capabilities.*/\narch/x86/events/intel/core.c-8644-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8645:\t\tintel_pmu_init_dkt_hybrid(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8646-\n--\narch/x86/events/intel/core.c-8652-\narch/x86/events/intel/core.c:8653:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8654-\n--\narch/x86/events/intel/core.c-8656-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8657:\t\tintel_pmu_init_lnc(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8658-\t\tmemcpy(hybrid_var(\u0026pmu-\u003epmu, hw_cache_extra_regs),\n--\narch/x86/events/intel/core.c-8661-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8662:\t\tintel_pmu_init_skt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8663-\n--\narch/x86/events/intel/core.c-8669-\narch/x86/events/intel/core.c:8670:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8671-\n--\narch/x86/events/intel/core.c-8673-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8674:\t\tintel_pmu_init_lnc(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8675-\t\t/* Initialize Atom core specific PerfMon capabilities.*/\narch/x86/events/intel/core.c-8676-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8677:\t\tintel_pmu_init_skt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8678-\n--\narch/x86/events/intel/core.c-8694-\tcase INTEL_ARROWLAKE_H:\narch/x86/events/intel/core.c:8695:\t\tintel_pmu_init_hybrid(hybrid_big_small_tiny);\narch/x86/events/intel/core.c-8696-\n--\narch/x86/events/intel/core.c-8708-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8709:\t\tintel_pmu_init_lnc(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8710-\t\tmemcpy(hybrid_var(\u0026pmu-\u003epmu, hw_cache_extra_regs),\n--\narch/x86/events/intel/core.c-8714-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8715:\t\tintel_pmu_init_skt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8716-\n--\narch/x86/events/intel/core.c-8718-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_TINY_IDX];\narch/x86/events/intel/core.c:8719:\t\tintel_pmu_init_cmt(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8720-\n--\narch/x86/events/intel/core.c-8729-\t\tname = \"novalake_hybrid\";\narch/x86/events/intel/core.c:8730:\t\tintel_pmu_init_hybrid(hybrid_big_small);\narch/x86/events/intel/core.c-8731-\n--\narch/x86/events/intel/core.c-8743-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_CORE_IDX];\narch/x86/events/intel/core.c:8744:\t\tintel_pmu_init_cyc(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8745-\n--\narch/x86/events/intel/core.c-8747-\t\tpmu = \u0026x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX];\narch/x86/events/intel/core.c:8748:\t\tintel_pmu_init_arw(\u0026pmu-\u003epmu);\narch/x86/events/intel/core.c-8749-\n--\narch/x86/events/perf_event.h=1654=extern void intel_cpuc_finish(struct cpu_hw_events *cpuc);\narch/x86/events/perf_event.h-1655-\narch/x86/events/perf_event.h:1656:int intel_pmu_init(void);\narch/x86/events/perf_event.h-1657-\n--\narch/x86/events/perf_event.h=1885=static inline void reserve_lbr_buffers(void)\n--\narch/x86/events/perf_event.h-1888-\narch/x86/events/perf_event.h:1889:static inline int intel_pmu_init(void)\narch/x86/events/perf_event.h-1890-{\n--\narch/x86/kvm/vmx/pmu_intel.c=529=static void intel_pmu_refresh(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/pmu_intel.c-632-\narch/x86/kvm/vmx/pmu_intel.c:633:static void intel_pmu_init(struct kvm_vcpu *vcpu)\narch/x86/kvm/vmx/pmu_intel.c-634-{\n--\narch/x86/kvm/vmx/pmu_intel.c=883=struct kvm_pmu_ops intel_pmu_ops __initdata = {\n--\narch/x86/kvm/vmx/pmu_intel.c-889-\t.refresh = intel_pmu_refresh,\narch/x86/kvm/vmx/pmu_intel.c:890:\t.init = intel_pmu_init,\narch/x86/kvm/vmx/pmu_intel.c-891-\t.reset = intel_pmu_reset,\n"
}

39/3 2026/08/17 19:14 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28350
							output: 21
							thoughts: 71
						

Thoughts:
Here's my take on the thought process, as if it were my own:

**PMU Initialization Dive**

Okay, so I see `intel_pmu_init` in `arch/x86/kvm/vmx/pmu_intel.c` is being called from `kvm_pmu_init`. That's the expected structure, the Intel-specific init function getting called from the generic one. Right, let's back up a step and refresh my memory on what `kvm_pmu_init` is doing in the first place. I'll need to go look at `arch/x86/kvm/pmu.c` to see how that function sets things up. I want to understand the control flow and initialization sequence. Specifically, what parameters are passed, what are the overall preconditions, and what are the main objectives of that initialization step. Gotta build the mental model correctly.



40/3 2026/08/17 19:14 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_init"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=992=void kvm_pmu_refresh(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/pmu.c-1042-\narch/x86/kvm/pmu.c:1043:void kvm_pmu_init(struct kvm_vcpu *vcpu)\narch/x86/kvm/pmu.c-1044-{\n--\narch/x86/kvm/pmu.h=293=void kvm_pmu_refresh(struct kvm_vcpu *vcpu);\narch/x86/kvm/pmu.h:294:void kvm_pmu_init(struct kvm_vcpu *vcpu);\narch/x86/kvm/pmu.h-295-void kvm_pmu_cleanup(struct kvm_vcpu *vcpu);\n--\narch/x86/kvm/x86.c=9344=int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/x86.c-9401-\t}\narch/x86/kvm/x86.c:9402:\tkvm_pmu_init(vcpu);\narch/x86/kvm/x86.c-9403-\n"
}

41/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29010
							output: 42
							thoughts: 39
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Investigating KVM PMU Initialization**

Alright, so the clue here is that `kvm_pmu_init` is called within `kvm_arch_vcpu_create`. That makes sense; it's the right place to be setting up the Performance Monitoring Unit for a new vCPU, right at instantiation. Let's dig into that `kvm_pmu_init` function itself. I need to understand exactly what it's doing – what resources it's allocating, which data structures it's touching, and crucially, what assumptions it makes about the underlying hardware and the guest environment. I want to see how this initialization interacts with the broader PMU infrastructure within KVM and whether there are any race conditions that I need to be aware of. Let's see if there are any subtle interactions with the kvm_arch_vcpu_create function.



42/3 2026/08/17 19:15 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/pmu.c",
  "FirstLine": 1043,
  "LineCount": 10
}

Results:
{
  "Contents": "1043:\tvoid kvm_pmu_init(struct kvm_vcpu *vcpu)\n1044:\t{\n1045:\t\tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n1046:\t\n1047:\t\tmemset(pmu, 0, sizeof(*pmu));\n1048:\t\tkvm_pmu_call(init)(vcpu);\n1049:\t}\n1050:\t\n1051:\t/* Release perf_events for vPMCs that have been unused for a full time slice.  */\n1052:\tvoid kvm_pmu_cleanup(struct kvm_vcpu *vcpu)\n"
}

43/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29488
							output: 26
							thoughts: 1251
						

Thoughts:
**Analyzing the KVM PMU Implementation**

Okay, so I'm looking at this KVM PMU code. First, I see that `pmu->perf_metrics` is definitely initialized to zero because `memset(pmu, 0, sizeof(*pmu))` is explicitly called in `kvm_pmu_init`. That's good to know.

Now, let's review this patch. It introduces handling for `MSR_PERF_METRICS`.  The guest can write to this MSR, and the code explicitly preserves those writes, even the upper 32 bits, with a comment about potential level-2 metrics support. `intel_pmu_get_msr` reads it. Seems standard enough.

Next, I'm checking `intel_emulate_rdpmc`. I see it includes a new `INTEL_RDPMC_METRICS` case. It checks if the vCPU supports performance metrics, and if the index (ECX[15:0]) is non-zero, it returns an error. If the index is zero, it reads `pmu->perf_metrics` into `*data`.  Safe so far.

Now I'm looking at `amd_emulate_rdpmc`. This also seems fine, since it's just getting the pmc from `amd_pmu_get_pmc`.

Let's review the common function, `kvm_pmu_rdpmc`. It calls a function pointer, `kvm_pmu_call(emulate_rdpmc)(vcpu, idx, data)`. It seems like the common code is safe since it calls one of the emulation functions already reviewed.

Wait a moment, I need to check something... I see a call to `kvm_pmu_rdpmc_vmware` in the main `kvm_pmu_rdpmc` function.  Does *this* function set `*data`? I'd better check.



44/3 2026/08/17 19:15 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_rdpmc_vmware"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=734=bool is_vmware_backdoor_pmc(u32 pmc_idx)\n--\narch/x86/kvm/pmu.c-744-\narch/x86/kvm/pmu.c:745:static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-746-{\n--\narch/x86/kvm/pmu.c=768=int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\n--\narch/x86/kvm/pmu.c-775-\tif (is_vmware_backdoor_pmc(idx))\narch/x86/kvm/pmu.c:776:\t\treturn kvm_pmu_rdpmc_vmware(vcpu, idx, data);\narch/x86/kvm/pmu.c-777-\n"
}

45/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31485
							output: 41
							thoughts: 18
						

Thoughts:
Okay, here's my attempt at summarizing the thought process, as if I were considering the code `kvm_pmu_rdpmc_vmware`, assuming I'm an expert in virtualization:

**Analyzing the `kvm_pmu_rdpmc_vmware` Function**

Alright, let's dive into `kvm_pmu_rdpmc_vmware`. The name itself is pretty telling; it's a KVM (Kernel-based Virtual Machine) function, specifically dealing with PMCs (Performance Monitoring Counters), and specifically for the VMware hypervisor. This immediately signals a few things. First, it suggests VMware emulation or compatibility within the KVM environment. Second, it implies an interest in performance monitoring *within* a VM running on KVM, potentially to understand overhead or optimize workloads. It's likely designed to handle the specific idiosyncrasies of reading PMCs in a virtualized, nested environment.

I need to understand what specific VMware behaviors this function is addressing. Are we talking about differences in how the guest sees the PMCs, or potential conflicts between KVM and VMware's own virtualization layers when accessing hardware performance counters? Are there specific emulation details that need to be addressed? Are there issues with handling VMWare specific MSRs when emulating KVM on top of VMWare? This code is likely a workaround of some kind, a specific patch or integration layer. I'll need to look at the surrounding context and see if there are any specific notes or comments that further describe the issue. There will likely be a specific VMware knowledge gap the original authors were solving for.

My priority will be finding out why it's necessary. Is this a performance optimization? A correctness fix? A security mitigation? Understanding the *why* is key to evaluating the quality of the solution. This is not something I would normally see; KVM has its own mechanisms for PMC access. This is very specific, and probably only triggered under a very particular set of circumstances.

I'll need to examine the function's implementation, pay close attention to the MSR reads and writes. I will need to understand what assumptions it's making about the underlying hardware and the interactions between KVM and the VMware hypervisor. Any conditional logic will be critical, because that could indicate the specific VMware versions or hardware configurations for which this function is relevant. The goal will be to either understand, or find some documentation. If it's a workaround, I will need to be prepared to find out where the bug is.



46/3 2026/08/17 19:15 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/pmu.c",
  "FirstLine": 745,
  "LineCount": 20
}

Results:
{
  "Contents": " 745:\tstatic int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\n 746:\t{\n 747:\t\tu64 ctr_val;\n 748:\t\n 749:\t\tswitch (idx) {\n 750:\t\tcase VMWARE_BACKDOOR_PMC_HOST_TSC:\n 751:\t\t\tctr_val = rdtsc();\n 752:\t\t\tbreak;\n 753:\t\tcase VMWARE_BACKDOOR_PMC_REAL_TIME:\n 754:\t\t\tctr_val = ktime_get_boottime_ns();\n 755:\t\t\tbreak;\n 756:\t\tcase VMWARE_BACKDOOR_PMC_APPARENT_TIME:\n 757:\t\t\tctr_val = ktime_get_boottime_ns() +\n 758:\t\t\t\tvcpu-\u003ekvm-\u003earch.kvmclock_offset;\n 759:\t\t\tbreak;\n 760:\t\tdefault:\n 761:\t\t\treturn 1;\n 762:\t\t}\n 763:\t\n 764:\t\t*data = ctr_val;\n"
}

47/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32387
							output: 20
							thoughts: 675
						

Thoughts:
**Analyzing the RDPMC Emulation Change**

Okay, so I'm looking at this recent change to the RDPMC emulation and trying to understand the implications, specifically concerning the handling of the counter masks. Initially, the code seems straightforward: it sets `*data` and returns 0, indicating success, or returns 1, meaning `*data` remains untouched. That seemed safe enough.

But now, I'm concerned about the `mask` variable, especially its use within `intel_rdpmc_ecx_to_pmc`. Looking at that code, I'm reminded that the older `kvm_pmu_rdpmc` function previously did something like `*data = pmc_read_counter(pmc) & mask;`. This old approach included the mask during the read.

However, the new `intel_emulate_rdpmc` implementation just does `*data = pmc_read_counter(pmc);` - that `& mask` is gone, completely. So I need to figure out where that `mask` came from initially.

I see now that, in the original `kvm_pmu_rdpmc` code, `mask` was initialized to `~0ull`. But then `intel_rdpmc_ecx_to_pmc` was called which, depending on whether it's a fixed or general-purpose counter, would set `bitmask` to either `pmu->counter_bitmask[KVM_PMC_FIXED]` or `pmu->counter_bitmask[KVM_PMC_GP]`. Crucially, `*mask &= bitmask;` was applied in `intel_rdpmc_ecx_to_pmc`, *before* the read.

So, in the old code, `mask` would be initialized to all 1's then AND'ed with a specific bitmask. Now, that's not happening and the code is just reading the counter value and writing it to `*data`. My concern now is: are these bitmasks still being applied?

The only place the bitmask might be applied would be *within* `pmc_read_counter`. I need to check that function to verify this is indeed the case.



48/3 2026/08/17 19:15 tool grepper
0m Args:
{
  "Expression": "pmc_read_counter"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=406=static void pmc_stop_counter(struct kvm_pmc *pmc)\n--\narch/x86/kvm/pmu.c-408-\tif (pmc-\u003eperf_event) {\narch/x86/kvm/pmu.c:409:\t\tpmc-\u003ecounter = pmc_read_counter(pmc);\narch/x86/kvm/pmu.c-410-\t\tpmc_release_perf_event(pmc);\n--\narch/x86/kvm/pmu.c=424=void pmc_write_counter(struct kvm_pmc *pmc, u64 val)\n--\narch/x86/kvm/pmu.c-436-\t * reset it to '0'.  Note, this very sneakily offsets the accumulated\narch/x86/kvm/pmu.c:437:\t * emulated count too, by using pmc_read_counter()!\narch/x86/kvm/pmu.c-438-\t */\narch/x86/kvm/pmu.c-439-\tpmc-\u003eemulated_counter = 0;\narch/x86/kvm/pmu.c:440:\tpmc-\u003ecounter += val - pmc_read_counter(pmc);\narch/x86/kvm/pmu.c-441-\tpmc-\u003ecounter \u0026= pmc_bitmask(pmc);\n--\narch/x86/kvm/pmu.h=123=static inline u64 pmc_bitmask(struct kvm_pmc *pmc)\n--\narch/x86/kvm/pmu.h-129-\narch/x86/kvm/pmu.h:130:static inline u64 pmc_read_counter(struct kvm_pmc *pmc)\narch/x86/kvm/pmu.h-131-{\n--\narch/x86/kvm/svm/pmu.c=87=static int amd_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx, u64 *data)\n--\narch/x86/kvm/svm/pmu.c-93-\narch/x86/kvm/svm/pmu.c:94:\t*data = pmc_read_counter(pmc);\narch/x86/kvm/svm/pmu.c-95-\treturn 0;\n--\narch/x86/kvm/svm/pmu.c=133=static int amd_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/svm/pmu.c-141-\tif (pmc) {\narch/x86/kvm/svm/pmu.c:142:\t\tmsr_info-\u003edata = pmc_read_counter(pmc);\narch/x86/kvm/svm/pmu.c-143-\t\treturn 0;\n--\narch/x86/kvm/vmx/pmu_intel.c=89=static int intel_emulate_rdpmc(struct kvm_vcpu *vcpu, unsigned int idx,\n--\narch/x86/kvm/vmx/pmu_intel.c-149-\tpmc = \u0026counters[array_index_nospec(idx, num_counters)];\narch/x86/kvm/vmx/pmu_intel.c:150:\t*data = pmc_read_counter(pmc);\narch/x86/kvm/vmx/pmu_intel.c-151-\treturn 0;\n--\narch/x86/kvm/vmx/pmu_intel.c=354=static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/vmx/pmu_intel.c-378-\t\t    (pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) {\narch/x86/kvm/vmx/pmu_intel.c:379:\t\t\tu64 val = pmc_read_counter(pmc);\narch/x86/kvm/vmx/pmu_intel.c-380-\t\t\tmsr_info-\u003edata =\n--\narch/x86/kvm/vmx/pmu_intel.c-383-\t\t} else if ((pmc = get_fixed_pmc(pmu, msr))) {\narch/x86/kvm/vmx/pmu_intel.c:384:\t\t\tu64 val = pmc_read_counter(pmc);\narch/x86/kvm/vmx/pmu_intel.c-385-\t\t\tmsr_info-\u003edata =\n--\ndrivers/platform/mellanox/mlxbf-pmc.c=1455=static int mlxbf_pmc_read_crspace_counter(unsigned int blk_num, u32 cnt_num, u64 *result)\n--\ndrivers/platform/mellanox/mlxbf-pmc.c-1471-/* Method to read the counter value */\ndrivers/platform/mellanox/mlxbf-pmc.c:1472:static int mlxbf_pmc_read_counter(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)\ndrivers/platform/mellanox/mlxbf-pmc.c-1473-{\n--\ndrivers/platform/mellanox/mlxbf-pmc.c=1663=static ssize_t mlxbf_pmc_counter_show(struct device *dev,\n--\ndrivers/platform/mellanox/mlxbf-pmc.c-1680-\t    (pmc-\u003eblock[blk_num].type == MLXBF_PMC_TYPE_CRSPACE)) {\ndrivers/platform/mellanox/mlxbf-pmc.c:1681:\t\tif (mlxbf_pmc_read_counter(blk_num, cnt_num, is_l3, \u0026value))\ndrivers/platform/mellanox/mlxbf-pmc.c-1682-\t\t\treturn -EINVAL;\n"
}

49/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35180
							output: 41
							thoughts: 12
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Initial Checkpoint: Examining `pmc_read_counter`**

Alright, so the first thing I'm doing is zeroing in on `pmc_read_counter`. It's a key variable, and I need to understand its current state.  Given my expertise, this likely represents a performance monitoring counter, probably related to something fundamental like memory access, cache hits/misses, instruction cycles, or something else core to the current task. I'm assuming that understanding its current value is the critical first step. The specific context will, of course, guide my analysis further. The type and size are not yet defined, as it could be a simple integer counter, a bitfield, or something more complex. However, my immediate objective is to know the *current* state of `pmc_read_counter`.



50/3 2026/08/17 19:15 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/pmu.h",
  "FirstLine": 130,
  "LineCount": 15
}

Results:
{
  "Contents": " 130:\tstatic inline u64 pmc_read_counter(struct kvm_pmc *pmc)\n 131:\t{\n 132:\t\tu64 counter, enabled, running;\n 133:\t\n 134:\t\tif (kvm_vcpu_has_mediated_pmu(pmc-\u003evcpu))\n 135:\t\t\treturn pmc-\u003ecounter \u0026 pmc_bitmask(pmc);\n 136:\t\n 137:\t\tcounter = pmc-\u003ecounter + pmc-\u003eemulated_counter;\n 138:\t\n 139:\t\tif (pmc-\u003eperf_event \u0026\u0026 !pmc-\u003eis_paused)\n 140:\t\t\tcounter += perf_event_read_value(pmc-\u003eperf_event,\n 141:\t\t\t\t\t\t\t \u0026enabled, \u0026running);\n 142:\t\t/* FIXME: Scaling needed? */\n 143:\t\treturn counter \u0026 pmc_bitmask(pmc);\n 144:\t}\n"
}

51/3 2026/08/17 19:15 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35693
							output: 23
							thoughts: 3789
						

Thoughts:
**Analysis of RDPMC Emulation and PMU Capabilities**

Alright, let's break down this code, point by point. It looks like the primary focus here is streamlining the `RDPMC` emulation path, particularly concerning performance monitoring units (PMUs) and the introduction of performance metrics capabilities, so I will start there.

Initially, I see that the code was redundantly applying the bitmask twice. The old code did `pmc_read_counter(pmc) & mask`, but `pmc_read_counter` already applies `pmc_bitmask(pmc)`, so the masking was happening twice. The update is just `*data = pmc_read_counter(pmc);`, which is the correct and cleaner approach.

Now I am going to see about any potential uninitialized memory reads. I confirmed that `pmc->counter` and `pmc->emulated_counter` are initialized to 0 when `struct kvm_pmu` is allocated. Further, `pmc_bitmask(pmc)` is derived from the hardware, so that is safe. So far so good.

Next, I looked at the handling of `kvm_host.perf_capabilities`. This is initialized in `kvm_x86_vendor_init` by reading from the MSR `IA32_PERF_CAPABILITIES`. If the read fails, which can happen, it remains zero-initialized. This is good because it provides a safe default.

`vmx_get_perf_capabilities` reads this value, and merges it with other relevant capabilities. The code now correctly uses the pre-existing `kvm_host.perf_capabilities` rather than a local variable. I verified this is the same as the prior behavior.

I dove into `kvm_init_pmu_capability`, which probes the PMU capabilities and sets various configuration parameters. I am reviewing the checks on fixed counters. This is also safe, and ensures the correct number of fixed counters are used.

Then, I looked at `intel_mediated_pmu_put` and `intel_mediated_pmu_load`. I noted how these functions interact with the hardware registers to write and read the `perf_metrics` value. This seems like a reasonable way to handle the interaction with the hardware.

`kvm_need_perf_metrics_intercept`, `vmx_recalc_pmu_msr_intercepts`, and `nested_vmx_merge_pmu_msr_bitmaps` seem to handle the intercept and merging of the MSRs related to the performance metrics. Again, this looks safe.

I inspected `kvm_vcpu_get_perf_caps` and `kvm_vcpu_has_perf_metrics`. These are straightforward and safe reads of capability flags. I then verified that `kvm_probe_msr_to_save` correctly handles the `PERF_METRICS` MSR.

Finally, I checked `msrs_to_save_pmu` and verified the correct MSRs are being saved and restored, including `MSR_PERF_METRICS`.

In a final round of review, I confirmed that no uninitialized memory risks were introduced. All the new fields are properly initialized. In particular, both `perf_metrics` and `perf_capabilities` were specifically checked.

Finally, I verified that the return behavior of `kvm_pmu_rdpmc`, and its emulated calls, have the same behavior for returning errors, so no regressions should have occurred. The code still works the same.



52/3 2026/08/17 19:16 tool grepper
0m Args:
{
  "Expression": "kvm_pmu_rdpmc"
}

Results:
{
  "Output": "arch/x86/kvm/pmu.c=734=bool is_vmware_backdoor_pmc(u32 pmc_idx)\n--\narch/x86/kvm/pmu.c-744-\narch/x86/kvm/pmu.c:745:static int kvm_pmu_rdpmc_vmware(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-746-{\n--\narch/x86/kvm/pmu.c-767-\narch/x86/kvm/pmu.c:768:int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)\narch/x86/kvm/pmu.c-769-{\n--\narch/x86/kvm/pmu.c-775-\tif (is_vmware_backdoor_pmc(idx))\narch/x86/kvm/pmu.c:776:\t\treturn kvm_pmu_rdpmc_vmware(vcpu, idx, data);\narch/x86/kvm/pmu.c-777-\n--\narch/x86/kvm/pmu.h=287=void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);\narch/x86/kvm/pmu.h:288:int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);\narch/x86/kvm/pmu.h-289-int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx);\n--\narch/x86/kvm/x86.c=789=int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/x86.c-793-\narch/x86/kvm/x86.c:794:\tif (kvm_pmu_rdpmc(vcpu, pmc, \u0026data)) {\narch/x86/kvm/x86.c-795-\t\tkvm_inject_gp(vcpu, 0);\n--\narch/x86/kvm/x86.c=5688=static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,\n--\narch/x86/kvm/x86.c-5690-{\narch/x86/kvm/x86.c:5691:\treturn kvm_pmu_rdpmc(emul_to_vcpu(ctxt), pmc, pdata);\narch/x86/kvm/x86.c-5692-}\n"
}

53/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 41019
							output: 28
							thoughts: 1306
						

Thoughts:
**Analysis of Potential Issues Related to Fixed Counters Increase**

Okay, no immediate external concerns. Let's delve into this code change related to the increased number of fixed performance counters. My primary concern is the potential for uninitialized memory reads or out-of-bounds access. I need to be thorough.

First, I examine the `MSR_PERF_METRICS` handling in both `intel_pmu_get_msr` and `intel_pmu_set_msr`. The `get` function simply returns the value, initialized to zero, and the `set` function overwrites it with new data. Safe.

Next, I check `kvm_host.perf_capabilities`, which is obtained via `rdmsrq_safe`. If this fails, the variable is initialized to zero. Safe.

Now, the core of the change: increasing `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4. I see the implications: `fixed_counters` array size is increased in `struct kvm_pmu`. This array is zero-allocated, which is good. The `intel_pmu_refresh` loop iterates up to `pmu->nr_arch_fixed_counters`. Safe. I want to double-check `kvm_init_pmu_capability`, this ensures safety via the min function. This is also safe.

Next, I check `intel_get_fixed_pmc_eventsel` and `intel_emulate_rdpmc`. They now handle index 3 correctly and don't introduce out-of-bounds reads. Safe.

Now I look at `msrs_to_save_pmu`. The addition of `MSR_ARCH_PERFMON_FIXED_CTR3` is expected and fine, since it now saves four fixed counters instead of three. Safe.

Finally, I investigate `kvm_probe_msr_to_save`, which filters the MSR ranges. It correctly handles the extended range up to `MSR_ARCH_PERFMON_FIXED_CTR0 + KVM_MAX_NR_FIXED_COUNTERS - 1`, which means it is safe.

However, I note a potential inconsistency in the code:
In `intel_pmu_get_msr`:

```c
	case MSR_ARCH_PERFMON_FIXED_CTR0 ... MSR_ARCH_PERFMON_FIXED_CTR0 + 2:
```
This is a problem! It's supposed to deal with four counters, not three. This means it only covers indices 0, 1, and 2, but not 3, the added fixed counter. I'll need to check the code that deals with reading and writing these MSRs. This requires further scrutiny of `intel_pmu_get_msr`.



54/3 2026/08/17 19:16 tool grepper
0m Args:
{
  "Expression": "MSR_ARCH_PERFMON_FIXED_CTR0"
}

Results:
{
  "Output": "arch/x86/events/intel/core.c=6571=static __initconst const struct x86_pmu core_pmu = {\n--\narch/x86/events/intel/core.c-6581-\t.perfctr\t\t= MSR_ARCH_PERFMON_PERFCTR0,\narch/x86/events/intel/core.c:6582:\t.fixedctr\t\t= MSR_ARCH_PERFMON_FIXED_CTR0,\narch/x86/events/intel/core.c-6583-\t.event_map\t\t= intel_pmu_event_map,\n--\narch/x86/events/intel/core.c=6620=static __initconst const struct x86_pmu intel_pmu = {\n--\narch/x86/events/intel/core.c-6635-\t.perfctr\t\t= MSR_ARCH_PERFMON_PERFCTR0,\narch/x86/events/intel/core.c:6636:\t.fixedctr\t\t= MSR_ARCH_PERFMON_FIXED_CTR0,\narch/x86/events/intel/core.c-6637-\t.event_map\t\t= intel_pmu_event_map,\n--\narch/x86/include/asm/perf_event.h=301=struct x86_pmu_capability {\n--\narch/x86/include/asm/perf_event.h-342-/* Instr_Retired.Any: */\narch/x86/include/asm/perf_event.h:343:#define MSR_ARCH_PERFMON_FIXED_CTR0\t0x309\narch/x86/include/asm/perf_event.h-344-#define INTEL_PMC_IDX_FIXED_INSTRUCTIONS\t(INTEL_PMC_IDX_FIXED + 0)\n--\narch/x86/kvm/msrs.c=218=static const u32 msrs_to_save_pmu[] = {\narch/x86/kvm/msrs.c:219:\tMSR_ARCH_PERFMON_FIXED_CTR0, MSR_ARCH_PERFMON_FIXED_CTR1,\narch/x86/kvm/msrs.c-220-\tMSR_ARCH_PERFMON_FIXED_CTR2, MSR_ARCH_PERFMON_FIXED_CTR3,\n--\narch/x86/kvm/msrs.c=2563=static void kvm_probe_msr_to_save(u32 msr_index)\n--\narch/x86/kvm/msrs.c-2626-\t\tbreak;\narch/x86/kvm/msrs.c:2627:\tcase MSR_ARCH_PERFMON_FIXED_CTR0 ...\narch/x86/kvm/msrs.c:2628:\t     MSR_ARCH_PERFMON_FIXED_CTR0 + KVM_MAX_NR_FIXED_COUNTERS - 1:\narch/x86/kvm/msrs.c:2629:\t\tif (msr_index - MSR_ARCH_PERFMON_FIXED_CTR0 \u003e=\narch/x86/kvm/msrs.c-2630-\t\t    kvm_pmu_cap.num_counters_fixed)\n"
}

55/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 43830
							output: 23
							thoughts: 36
						

Thoughts:
Here's my summary, as if I were thinking these thoughts:

**Diving into the Intel PMU MSR Retrieval**

Okay, so I need to refresh my memory on how KVM interacts with the Intel Performance Monitoring Unit (PMU) for those MSR reads. Let's start by digging into the `intel_pmu_get_msr` function, and that's likely to be in the `arch/x86/kvm/vmx/pmu_intel.c` file. Gotta get a solid handle on the path for those performance counter MSR values. I'm assuming that it is the most likely file to handle the retrieval of these values. I'll need to double check any intricacies with the VM and VMX specifically.



56/3 2026/08/17 19:16 tool grepper
0m Args:
{
  "Expression": "intel_pmu_get_msr"
}

Results:
{
  "Output": "arch/x86/kvm/vmx/pmu_intel.c=315=static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/vmx/pmu_intel.c-353-\narch/x86/kvm/vmx/pmu_intel.c:354:static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\narch/x86/kvm/vmx/pmu_intel.c-355-{\n--\narch/x86/kvm/vmx/pmu_intel.c=883=struct kvm_pmu_ops intel_pmu_ops __initdata = {\n--\narch/x86/kvm/vmx/pmu_intel.c-886-\t.is_valid_msr = intel_is_valid_msr,\narch/x86/kvm/vmx/pmu_intel.c:887:\t.get_msr = intel_pmu_get_msr,\narch/x86/kvm/vmx/pmu_intel.c-888-\t.set_msr = intel_pmu_set_msr,\n"
}

57/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 44393
							output: 46
							thoughts: 15
						

Thoughts:
Okay, let's dive into `intel_pmu_get_msr`.

**My Initial Assessment of `intel_pmu_get_msr`**

Alright, I'm taking a look at this function, `intel_pmu_get_msr`. My initial thought is, based on the naming, this is likely part of the Intel Performance Monitoring Unit (PMU) infrastructure. It suggests we're probably fetching Model Specific Registers (MSRs). Given my expertise, I already know these MSRs are crucial for low-level performance profiling, event counting, and hardware debugging on Intel processors.

I'm expecting this function to handle the nuances of accessing these registers. That could include things like: ensuring correct privilege levels, properly handling potential errors during the read operation, managing the MSR address based on the specific CPU model, maybe dealing with specific quirks or errata in the hardware. There might also be locking involved to manage concurrency, particularly if multiple processes are trying to access the PMU simultaneously. I will be looking closely for the handling of CPUID and MSR_IA32_PERF_CTL to see if it is used. I am expecting any error handling to be efficient.

I'm curious to see how robust the error checking is. Are we just returning an error code, or is there more involved logging or maybe even error recovery, such as retrying the read or signaling a fatal hardware issue? Understanding the error handling is essential for reliability in a performance monitoring context. I am also expecting to see if there is any abstraction from the platform, as this makes the code easier to maintain.



58/3 2026/08/17 19:16 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/vmx/pmu_intel.c",
  "FirstLine": 354,
  "LineCount": 40
}

Results:
{
  "Contents": " 354:\tstatic int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n 355:\t{\n 356:\t\tstruct kvm_pmu *pmu = vcpu_to_pmu(vcpu);\n 357:\t\tstruct kvm_pmc *pmc;\n 358:\t\tu32 msr = msr_info-\u003eindex;\n 359:\t\n 360:\t\tswitch (msr) {\n 361:\t\tcase MSR_CORE_PERF_FIXED_CTR_CTRL:\n 362:\t\t\tmsr_info-\u003edata = pmu-\u003efixed_ctr_ctrl;\n 363:\t\t\tbreak;\n 364:\t\tcase MSR_PERF_METRICS:\n 365:\t\t\tmsr_info-\u003edata = pmu-\u003eperf_metrics;\n 366:\t\t\tbreak;\n 367:\t\tcase MSR_IA32_PEBS_ENABLE:\n 368:\t\t\tmsr_info-\u003edata = pmu-\u003epebs_enable;\n 369:\t\t\tbreak;\n 370:\t\tcase MSR_IA32_DS_AREA:\n 371:\t\t\tmsr_info-\u003edata = pmu-\u003eds_area;\n 372:\t\t\tbreak;\n 373:\t\tcase MSR_PEBS_DATA_CFG:\n 374:\t\t\tmsr_info-\u003edata = pmu-\u003epebs_data_cfg;\n 375:\t\t\tbreak;\n 376:\t\tdefault:\n 377:\t\t\tif ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) ||\n 378:\t\t\t    (pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) {\n 379:\t\t\t\tu64 val = pmc_read_counter(pmc);\n 380:\t\t\t\tmsr_info-\u003edata =\n 381:\t\t\t\t\tval \u0026 pmu-\u003ecounter_bitmask[KVM_PMC_GP];\n 382:\t\t\t\tbreak;\n 383:\t\t\t} else if ((pmc = get_fixed_pmc(pmu, msr))) {\n 384:\t\t\t\tu64 val = pmc_read_counter(pmc);\n 385:\t\t\t\tmsr_info-\u003edata =\n 386:\t\t\t\t\tval \u0026 pmu-\u003ecounter_bitmask[KVM_PMC_FIXED];\n 387:\t\t\t\tbreak;\n 388:\t\t\t} else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {\n 389:\t\t\t\tmsr_info-\u003edata = pmc-\u003eeventsel;\n 390:\t\t\t\tbreak;\n 391:\t\t\t} else if (intel_pmu_handle_lbr_msrs_access(vcpu, msr_info, true)) {\n 392:\t\t\t\tbreak;\n 393:\t\t\t}\n"
}

59/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 45566
							output: 20
							thoughts: 71
						

Thoughts:
**Analyzing the Fixed Performance Counters**

Okay, so I'm looking at how `get_fixed_pmc` handles the fixed performance counters, specifically from `MSR_ARCH_PERFMON_FIXED_CTR0` all the way up to `MSR_ARCH_PERFMON_FIXED_CTR0 + KVM_MAX_NR_FIXED_COUNTERS - 1`. This function is critical for proper interaction with the hardware's performance monitoring capabilities. I need to make sure this is implemented correctly, handling the full range that the hardware supports. Let me dive deeper into the `get_fixed_pmc` function and see exactly how it's dealing with these MSRs. It’s important to see how the system is managing these architectural performance monitoring registers.



60/3 2026/08/17 19:16 tool grepper
0m Args:
{
  "Expression": "get_fixed_pmc"
}

Results:
{
  "Output": "arch/riscv/kvm/vcpu_pmu.c=118=static u64 kvm_pmu_get_perf_event_config(unsigned long eidx, uint64_t evt_data)\n--\narch/riscv/kvm/vcpu_pmu.c-148-\narch/riscv/kvm/vcpu_pmu.c:149:static int kvm_pmu_get_fixed_pmc_index(unsigned long eidx)\narch/riscv/kvm/vcpu_pmu.c-150-{\n--\narch/riscv/kvm/vcpu_pmu.c=194=static int pmu_get_pmc_index(struct kvm_pmu *pmu, unsigned long eidx,\n--\narch/riscv/kvm/vcpu_pmu.c-199-\t/* Fixed counters need to be have fixed mapping as they have different width */\narch/riscv/kvm/vcpu_pmu.c:200:\tret = kvm_pmu_get_fixed_pmc_index(eidx);\narch/riscv/kvm/vcpu_pmu.c-201-\tif (ret \u003e= 0)\n--\narch/x86/kvm/pmu.h=168=static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,\n--\narch/x86/kvm/pmu.h-181-/* returns fixed PMC with the specified MSR */\narch/x86/kvm/pmu.h:182:static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)\narch/x86/kvm/pmu.h-183-{\n--\narch/x86/kvm/vmx/pmu_intel.c=59=static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)\n--\narch/x86/kvm/vmx/pmu_intel.c-81-\narch/x86/kvm/vmx/pmu_intel.c:82:\t\tpmc = get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + i);\narch/x86/kvm/vmx/pmu_intel.c-83-\n--\narch/x86/kvm/vmx/pmu_intel.c=196=static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)\n--\narch/x86/kvm/vmx/pmu_intel.c-220-\t\t\tget_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0) ||\narch/x86/kvm/vmx/pmu_intel.c:221:\t\t\tget_fixed_pmc(pmu, msr) || get_fw_gp_pmc(pmu, msr) ||\narch/x86/kvm/vmx/pmu_intel.c-222-\t\t\tintel_pmu_is_valid_lbr_msr(vcpu, msr);\n--\narch/x86/kvm/vmx/pmu_intel.c=229=static struct kvm_pmc *intel_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)\n--\narch/x86/kvm/vmx/pmu_intel.c-233-\narch/x86/kvm/vmx/pmu_intel.c:234:\tpmc = get_fixed_pmc(pmu, msr);\narch/x86/kvm/vmx/pmu_intel.c-235-\tpmc = pmc ? pmc : get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0);\n--\narch/x86/kvm/vmx/pmu_intel.c=354=static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/vmx/pmu_intel.c-382-\t\t\tbreak;\narch/x86/kvm/vmx/pmu_intel.c:383:\t\t} else if ((pmc = get_fixed_pmc(pmu, msr))) {\narch/x86/kvm/vmx/pmu_intel.c-384-\t\t\tu64 val = pmc_read_counter(pmc);\n--\narch/x86/kvm/vmx/pmu_intel.c=400=static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/vmx/pmu_intel.c-457-\t\t\tbreak;\narch/x86/kvm/vmx/pmu_intel.c:458:\t\t} else if ((pmc = get_fixed_pmc(pmu, msr))) {\narch/x86/kvm/vmx/pmu_intel.c-459-\t\t\tpmc_write_counter(pmc, data);\n--\narch/x86/kvm/vmx/pmu_intel.c-494- */\narch/x86/kvm/vmx/pmu_intel.c:495:static u64 intel_get_fixed_pmc_eventsel(unsigned int index)\narch/x86/kvm/vmx/pmu_intel.c-496-{\n--\narch/x86/kvm/vmx/pmu_intel.c=633=static void intel_pmu_init(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/pmu_intel.c-653-\t\tpmu-\u003efixed_counters[i].current_config = 0;\narch/x86/kvm/vmx/pmu_intel.c:654:\t\tpmu-\u003efixed_counters[i].eventsel = intel_get_fixed_pmc_eventsel(i);\narch/x86/kvm/vmx/pmu_intel.c-655-\t}\n"
}

61/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 47438
							output: 41
							thoughts: 12
						

Thoughts:
Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:

**Analyzing the Call to `get_fixed_pmc`**

Right, let's get into this. My initial instinct is to scrutinize the call to `get_fixed_pmc`. It's a critical function, so I'm immediately running through possible scenarios.

What is the context? What exactly does `get_fixed_pmc` mean in this setting? I need to understand its inputs, the expected outputs, and most importantly, any potential side effects. Is this a simple retrieval or something that could modify data, or cause a critical race condition?

Is there any documentation or comments on it? Before I even consider its implementation, understanding the function's contract and intended behavior is paramount. I'm thinking about the wider system, and how the PMC (whatever that is!) fits in with other modules. I need to get the "big picture" of how it should integrate and how it is expected to function.

So, first thing's first. It's time to dig into `get_fixed_pmc` itself.



62/3 2026/08/17 19:16 tool read-file
0m Args:
{
  "File": "arch/x86/kvm/pmu.h",
  "FirstLine": 182,
  "LineCount": 15
}

Results:
{
  "Contents": " 182:\tstatic inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)\n 183:\t{\n 184:\t\tint base = MSR_CORE_PERF_FIXED_CTR0;\n 185:\t\n 186:\t\tif (msr \u003e= base \u0026\u0026 msr \u003c base + pmu-\u003enr_arch_fixed_counters) {\n 187:\t\t\tu32 index = array_index_nospec(msr - base,\n 188:\t\t\t\t\t\t       pmu-\u003enr_arch_fixed_counters);\n 189:\t\n 190:\t\t\treturn \u0026pmu-\u003efixed_counters[index];\n 191:\t\t}\n 192:\t\n 193:\t\treturn NULL;\n 194:\t}\n 195:\t\n 196:\tstatic inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc)\n"
}

63/3 2026/08/17 19:16 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 47999
							output: 387
							thoughts: 765
						

Thoughts:
**KMSAN Reasoning for Intel Topdown Perf Metrics Patch**

Okay, let's break this down. My initial concern was about uninitialized memory reads, that's always the first thing I look for. The `get_fixed_pmc` function seems to correctly handle the fixed performance counters, specifically from `MSR_CORE_PERF_FIXED_CTR0` up to however many arch-fixed counters are supported. Wait, I should double check the MSR addresses… yep, `MSR_ARCH_PERFMON_FIXED_CTR0` and `MSR_CORE_PERF_FIXED_CTR0` are the same, `0x309`. So `get_fixed_pmc` is good.

Now, about uninitialized memory...The patch introduces `perf_metrics`, which is initialized to zero, that's good practice. It also bumps `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4, expanding the `fixed_counters` array. Since this is zero-allocated, it's safe. It's also worth noting that the change to `emulate_rdpmc` seems safe, as it correctly sets the output `data` only on success (0) and matches the old behavior regarding failure (1). That's critical, I don't want any garbage data being exposed. Additionally, the new `perf_capabilities` member in `kvm_host` is also initialized to zero if `rdmsrq_safe` fails.

So, digging deeper, there are no obvious uninitialized memory risks, no information leaks, or complex data structures being sent to user space without proper initialization that I can see. This patch focuses on adding support for Intel Topdown Performance Metrics. It adds `perf_metrics` and `perf_capabilities`. The `kvm_pmu` struct is within the `kvm_vcpu_arch` struct, and is allocated with zero during vCPU creation. Plus, `perf_metrics` is explicitly reset to 0 in `intel_pmu_reset`. `kvm_host` is a global, so `perf_capabilities` gets zeroed by default and updated through `rdmsrq_safe`. The `fixed_counters` array is also safely zero-allocated and updated as necessary. The refactor of `rdpmc_ecx_to_pmc` to `emulate_rdpmc` is safe as the data is only written on success.

Standard checks like KASAN and other bug detectors are enough. I don't think we need a dedicated KMSAN fuzzing session for this. Any potential out-of-bounds or use-after-free bugs are extremely likely to be caught by existing tools. I can sign off on this.



64/3 2026/08/17 19:17 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces support for Intel Topdown Perf Metrics in KVM. It adds a `perf_metrics` field to `struct kvm_pmu` and `perf_capabilities` to `struct kvm_host_values`. `struct kvm_pmu` is embedded in the vCPU structure, which is zero-allocated during creation, and `perf_metrics` is explicitly zeroed in `intel_pmu_reset()`. `kvm_host` is a global variable, so `perf_capabilities` is zero-initialized and safely populated via `rdmsrq_safe()`.\n\nThe patch also increases `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4, expanding the `fixed_counters` array in `struct kvm_pmu`. This array is zero-allocated and safely initialized in `intel_pmu_refresh()`.\n\nAdditionally, the patch refactors `rdpmc_ecx_to_pmc` into `emulate_rdpmc`. The new `emulate_rdpmc` implementations (`intel_emulate_rdpmc`, `amd_emulate_rdpmc`) correctly populate the `*data` pointer on success (returning 0) and leave it untouched on failure (returning 1), matching the previous behavior. The caller (`kvm_emulate_rdpmc`) only reads `data` upon success.\n\nThere are no uninitialized memory reads, info-leaks to user space, or complex uninitialized data structures introduced. Any potential out-of-bounds accesses or memory corruption bugs would be caught by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch introduces support for Intel Topdown Perf Metrics in KVM. It adds a `perf_metrics` field to `struct kvm_pmu` and `perf_capabilities` to `struct kvm_host_values`. `struct kvm_pmu` is embedded in the vCPU structure, which is zero-allocated during creation, and `perf_metrics` is explicitly zeroed in `intel_pmu_reset()`. `kvm_host` is a global variable, so `perf_capabilities` is zero-initialized and safely populated via `rdmsrq_safe()`.\n\nThe patch also increases `KVM_MAX_NR_INTEL_FIXED_COUNTERS` from 3 to 4, expanding the `fixed_counters` array in `struct kvm_pmu`. This array is zero-allocated and safely initialized in `intel_pmu_refresh()`.\n\nAdditionally, the patch refactors `rdpmc_ecx_to_pmc` into `emulate_rdpmc`. The new `emulate_rdpmc` implementations (`intel_emulate_rdpmc`, `amd_emulate_rdpmc`) correctly populate the `*data` pointer on success (returning 0) and leave it untouched on failure (returning 1), matching the previous behavior. The caller (`kvm_emulate_rdpmc`) only reads `data` upon success.\n\nThere are no uninitialized memory reads, info-leaks to user space, or complex uninitialized data structures introduced. Any potential out-of-bounds accesses or memory corruption bugs would be caught by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)