| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/23 21:33 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arm_pmu_v3_init",
"kvm_arm_pmu_v3_set_pmu",
"kvm_arm_pmu_v3_set_attr",
"set_pmcr",
"access_pmmir",
"set_pmmir",
"get_pmmir",
"kvm_pmu_get_pmceid"
],
"Reasoning": "The patch introduces a new KVM vCPU feature `KVM_ARM_VCPU_PMU_V3_STRICT` which enforces strict PMUv3 UAPI behavior. It modifies KVM ARM64 PMU emulation logic, including PMU initialization, setting PMU attributes, and handling PMU system registers (PMMIR, PMCR). This is reachable core kernel logic that can be triggered by userspace via KVM ioctls and guest execution, making it worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/23 21:33 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 9dca80c40fbed275cd60394ef9a670db91bc1276\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 23 21:33:06 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst\nindex a5f9ee92f43e8d..4988c32df4bf81 100644\n--- a/Documentation/virt/kvm/api.rst\n+++ b/Documentation/virt/kvm/api.rst\n@@ -3515,6 +3515,17 @@ Possible features:\n \t Depends on KVM_CAP_ARM_PSCI_0_2.\n \t- KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU.\n \t Depends on KVM_CAP_ARM_PMU_V3.\n+\t- KVM_ARM_VCPU_PMU_V3_STRICT: Enable strict PMUv3 UAPI.\n+\t Requires KVM_ARM_VCPU_PMU_V3. Depends on KVM_CAP_ARM_PMU_V3_STRICT.\n+\t When enabled:\n+\n+\t * Userspace must explicitly select a PMU implementation before\n+\t initializing the PMU or configuring a PMU event filter\n+\n+\t * If the PMU implements FEAT_PMUv3p4, PMMIR_EL1.SLOTS provides the\n+\t hardware value of the underlying implementation\n+\n+\t * Writes to PMCR_EL0.N via KVM_SET_ONE_REG are ignored\n \n \t- KVM_ARM_VCPU_PTRAUTH_ADDRESS: Enables Address Pointer authentication\n \t for arm64 only.\ndiff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst\nindex 66e714f2fcfa7f..deb5c51bc00c87 100644\n--- a/Documentation/virt/kvm/devices/vcpu.rst\n+++ b/Documentation/virt/kvm/devices/vcpu.rst\n@@ -53,8 +53,9 @@ Returns:\n \t ======= ======================================================\n \t -EEXIST Interrupt number already used\n \t -ENODEV PMUv3 not supported or GIC not initialized\n-\t -ENXIO PMUv3 not supported, missing VCPU feature or interrupt\n-\t\t number not set (non-GICv5 guests, only)\n+\t -ENXIO PMUv3 not supported, missing VCPU feature, missing\n+ hardware PMU, or interrupt number not set (non-GICv5\n+ guests, only)\n \t -EBUSY PMUv3 already initialized\n \t ======= ======================================================\n \n@@ -62,6 +63,9 @@ Request the initialization of the PMUv3. If using the PMUv3 with an in-kernel\n virtual GIC implementation, this must be done after initializing the in-kernel\n irqchip.\n \n+When the KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature is enabled this must be done\n+after selecting a hardware PMU.\n+\n 1.3 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FILTER\n -----------------------------------------\n \n@@ -108,6 +112,9 @@ hardware event. Filtering event 0x1E (CHAIN) has no effect either, as it\n isn't strictly speaking an event. Filtering the cycle counter is possible\n using event 0x11 (CPU_CYCLES).\n \n+When the KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature is enabled this must be done\n+after selecting a hardware PMU.\n+\n 1.4 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_SET_PMU\n ------------------------------------------\n \ndiff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h\nindex bae2c4f92ef5c0..39f7fc740d079b 100644\n--- a/arch/arm64/include/asm/kvm_host.h\n+++ b/arch/arm64/include/asm/kvm_host.h\n@@ -39,7 +39,7 @@\n \n #define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS\n \n-#define KVM_VCPU_MAX_FEATURES 9\n+#define KVM_VCPU_MAX_FEATURES 10\n #define KVM_VCPU_VALID_FEATURES\t(BIT(KVM_VCPU_MAX_FEATURES) - 1)\n \n #define KVM_REQ_SLEEP \\\n@@ -387,6 +387,9 @@ struct kvm_arch {\n \t/* Maximum number of counters for the guest */\n \tu8 nr_pmu_counters;\n \n+\t/* PMMIR_EL1.SLOTS value exposed to the guest. */\n+\tu8 pmmir_slots;\n+\n \t/* Hypercall features firmware registers' descriptor */\n \tstruct kvm_smccc_features smccc_feat;\n \tstruct maple_tree smccc_filter;\ndiff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h\nindex 1c13bfa2d38aad..019e5e3d892e6d 100644\n--- a/arch/arm64/include/uapi/asm/kvm.h\n+++ b/arch/arm64/include/uapi/asm/kvm.h\n@@ -106,6 +106,7 @@ struct kvm_regs {\n #define KVM_ARM_VCPU_PTRAUTH_GENERIC\t6 /* VCPU uses generic authentication */\n #define KVM_ARM_VCPU_HAS_EL2\t\t7 /* Support nested virtualization */\n #define KVM_ARM_VCPU_HAS_EL2_E2H0\t8 /* Limit NV support to E2H RES0 */\n+#define KVM_ARM_VCPU_PMU_V3_STRICT\t9 /* No default PMU creation */\n \n struct kvm_vcpu_init {\n \t__u32 target;\ndiff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c\nindex 50adfff75be82e..7dbefdd846aa70 100644\n--- a/arch/arm64/kvm/arm.c\n+++ b/arch/arm64/kvm/arm.c\n@@ -452,6 +452,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)\n \t\tr = get_num_wrps();\n \t\tbreak;\n \tcase KVM_CAP_ARM_PMU_V3:\n+\tcase KVM_CAP_ARM_PMU_V3_STRICT:\n \t\tr = kvm_supports_guest_pmuv3();\n \t\tbreak;\n \tcase KVM_CAP_ARM_INJECT_SERROR_ESR:\n@@ -1563,8 +1564,10 @@ static unsigned long system_supported_vcpu_features(void)\n \tif (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))\n \t\tclear_bit(KVM_ARM_VCPU_EL1_32BIT, \u0026features);\n \n-\tif (!kvm_supports_guest_pmuv3())\n+\tif (!kvm_supports_guest_pmuv3()) {\n \t\tclear_bit(KVM_ARM_VCPU_PMU_V3, \u0026features);\n+\t\tclear_bit(KVM_ARM_VCPU_PMU_V3_STRICT, \u0026features);\n+\t}\n \n \tif (!system_supports_sve())\n \t\tclear_bit(KVM_ARM_VCPU_SVE, \u0026features);\n@@ -1605,6 +1608,11 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,\n \t test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, \u0026features))\n \t\treturn -EINVAL;\n \n+\t/* Strict PMUv3 UAPI requires PMUv3. */\n+\tif (test_bit(KVM_ARM_VCPU_PMU_V3_STRICT, \u0026features) \u0026\u0026\n+\t !test_bit(KVM_ARM_VCPU_PMU_V3, \u0026features))\n+\t\treturn -EINVAL;\n+\n \tif (!test_bit(KVM_ARM_VCPU_EL1_32BIT, \u0026features))\n \t\treturn 0;\n \n@@ -1634,10 +1642,13 @@ static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)\n \tint ret = 0;\n \n \t/*\n-\t * When the vCPU has a PMU, but no PMU is set for the guest\n-\t * yet, set the default one.\n+\t * When the vCPU has a PMU, but no PMU is set for the guest yet, set\n+\t * the default one. If KVM_ARM_VCPU_PMU_V3_STRICT is set, no default\n+\t * PMU is created, and userspace must select a PMU via\n+\t * KVM_ARM_VCPU_PMU_V3_SET_PMU.\n \t */\n-\tif (kvm_vcpu_has_pmu(vcpu) \u0026\u0026 !kvm-\u003earch.arm_pmu)\n+\tif (kvm_vcpu_has_pmu(vcpu) \u0026\u0026 !kvm-\u003earch.arm_pmu \u0026\u0026\n+\t !kvm_vcpu_has_pmuv3_strict(vcpu))\n \t\tret = kvm_arm_set_default_pmu(kvm);\n \n \t/* Prepare for nested if required */\ndiff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c\nindex 98305bbfc095ab..5b1af7e2176faf 100644\n--- a/arch/arm64/kvm/pmu-emul.c\n+++ b/arch/arm64/kvm/pmu-emul.c\n@@ -838,9 +838,9 @@ static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)\n \treturn ((u64)hi[pmceid1] \u003c\u003c 32) | lo[pmceid1];\n }\n \n-static u64 compute_pmceid0(struct arm_pmu *pmu)\n+static u64 compute_pmceid0(struct kvm_vcpu *vcpu)\n {\n-\tu64 val = __compute_pmceid(pmu, 0);\n+\tu64 val = __compute_pmceid(vcpu-\u003ekvm-\u003earch.arm_pmu, 0);\n \n \t/* always support SW_INCR */\n \tval |= BIT(ARMV8_PMUV3_PERFCTR_SW_INCR);\n@@ -849,32 +849,33 @@ static u64 compute_pmceid0(struct arm_pmu *pmu)\n \treturn val;\n }\n \n-static u64 compute_pmceid1(struct arm_pmu *pmu)\n+static u64 compute_pmceid1(struct kvm_vcpu *vcpu)\n {\n-\tu64 val = __compute_pmceid(pmu, 1);\n+\tu64 val = __compute_pmceid(vcpu-\u003ekvm-\u003earch.arm_pmu, 1);\n \n \t/*\n-\t * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled\n-\t * as RAZ\n+\t * If KVM_ARM_VCPU_PMU_V3_STRICT is not set, PMMIR_EL1 is\n+\t * unconditionally RAZ, so don't advertise STALL_SLOT* events.\n \t */\n-\tval \u0026= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) |\n-\t\t BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) |\n-\t\t BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32));\n+\tif (!kvm_vcpu_has_pmuv3_strict(vcpu))\n+\t\tval \u0026= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) |\n+\t\t\t BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) |\n+\t\t\t BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32));\n+\n \treturn val;\n }\n \n u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n {\n-\tstruct arm_pmu *cpu_pmu = vcpu-\u003ekvm-\u003earch.arm_pmu;\n \tunsigned long *bmap = vcpu-\u003ekvm-\u003earch.pmu_filter;\n \tu64 val, mask = 0;\n \tint base, i, nr_events;\n \n \tif (!pmceid1) {\n-\t\tval = compute_pmceid0(cpu_pmu);\n+\t\tval = compute_pmceid0(vcpu);\n \t\tbase = 0;\n \t} else {\n-\t\tval = compute_pmceid1(cpu_pmu);\n+\t\tval = compute_pmceid1(vcpu);\n \t\tbase = 32;\n \t}\n \n@@ -938,6 +939,10 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)\n \n static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)\n {\n+\t/* Only possible when using KVM_ARM_VCPU_PMU_V3_STRICT */\n+\tif (!vcpu-\u003ekvm-\u003earch.arm_pmu)\n+\t\treturn -ENXIO;\n+\n \tif (irqchip_in_kernel(vcpu-\u003ekvm)) {\n \t\tint ret;\n \n@@ -1008,6 +1013,14 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)\n {\n \tstruct arm_pmu *arm_pmu = kvm-\u003earch.arm_pmu;\n \n+\t/*\n+\t * Under KVM_ARM_VCPU_PMU_V3_STRICT no PMU exists until userspace sets\n+\t * one, so this can be reached before arm_pmu is set. Report no\n+\t * counters in that case.\n+\t */\n+\tif (!arm_pmu)\n+\t\treturn 0;\n+\n \t/*\n \t * PMUv3 requires that all event counters are capable of counting any\n \t * event, though the same may not be true of non-PMUv3 hardware.\n@@ -1049,7 +1062,8 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)\n }\n \n /**\n- * kvm_arm_set_default_pmu - No PMU set, get the default one.\n+ * kvm_arm_set_default_pmu - No PMU set and KVM_ARM_VCPU_PMU_V3_STRICT not\n+ * set, get the default one.\n * @kvm: The kvm pointer\n *\n * The observant among you will notice that the supported_cpus\n@@ -1092,6 +1106,17 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)\n \n \t\t\tkvm_arm_set_pmu(kvm, arm_pmu);\n \t\t\tcpumask_copy(kvm-\u003earch.supported_cpus, \u0026arm_pmu-\u003esupported_cpus);\n+\n+\t\t\t/*\n+\t\t\t * Since a specific PMU is explicitly selected,\n+\t\t\t * PMMIR_EL1.SLOTS is deterministic to the guest.\n+\t\t\t * If KVM_ARM_VCPU_PMU_V3_STRICT is set, snapshot\n+\t\t\t * the value to allow the guest to read it.\n+\t\t\t */\n+\t\t\tif (kvm_vcpu_has_pmuv3_strict(vcpu))\n+\t\t\t\tkvm-\u003earch.pmmir_slots =\n+\t\t\t\t\tFIELD_GET(ARMV8_PMU_SLOTS,\n+\t\t\t\t\t\t arm_pmu-\u003ereg_pmmir);\n \t\t\tret = 0;\n \t\t\tbreak;\n \t\t}\n@@ -1178,6 +1203,9 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\n \t\tif (kvm_vm_has_ran_once(kvm))\n \t\t\treturn -EBUSY;\n \n+\t\tif (!kvm-\u003earch.arm_pmu)\n+\t\t\treturn -ENXIO;\n+\n \t\tif (!kvm-\u003earch.pmu_filter) {\n \t\t\tkvm-\u003earch.pmu_filter = bitmap_alloc(nr_events, GFP_KERNEL_ACCOUNT);\n \t\t\tif (!kvm-\u003earch.pmu_filter)\ndiff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c\nindex 5d5c579d457902..e93f11b90813b0 100644\n--- a/arch/arm64/kvm/sys_regs.c\n+++ b/arch/arm64/kvm/sys_regs.c\n@@ -1367,6 +1367,64 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n \treturn true;\n }\n \n+static bool access_pmmir(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n+\t\t\t const struct sys_reg_desc *r)\n+{\n+\tif (p-\u003eis_write)\n+\t\treturn write_to_read_only(vcpu, p, r);\n+\n+\t/*\n+\t * If KVM_ARM_VCPU_PMU_V3_STRICT is set and PMU was explicitly\n+\t * selected, the underlying hardware SLOTS value was read into this\n+\t * field. Otherwise, it stays 0. All other PMMIR_EL1 fields are RAZ.\n+\t */\n+\tp-\u003eregval = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu-\u003ekvm-\u003earch.pmmir_slots);\n+\treturn true;\n+}\n+\n+static int get_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\n+\t\t u64 *val)\n+{\n+\t*val = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu-\u003ekvm-\u003earch.pmmir_slots);\n+\treturn 0;\n+}\n+\n+static int set_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\n+\t\t u64 val)\n+{\n+\tstruct kvm *kvm = vcpu-\u003ekvm;\n+\tu8 slots = FIELD_GET(ARMV8_PMU_SLOTS, val);\n+\n+\t/*\n+\t * Only the SLOTS field is exposed (get_pmmir returns just that field),\n+\t * so reject a write that sets any other bit rather than silently\n+\t * masking it.\n+\t */\n+\tif (val \u0026 ~(u64)ARMV8_PMU_SLOTS)\n+\t\treturn -EINVAL;\n+\n+\tguard(mutex)(\u0026kvm-\u003earch.config_lock);\n+\n+\t/*\n+\t * Once the VM has started PMMIR_EL1 is immutable. Reject any write\n+\t * that does not match the current value.\n+\t */\n+\tif (kvm_vm_has_ran_once(kvm))\n+\t\treturn slots == kvm-\u003earch.pmmir_slots ? 0 : -EBUSY;\n+\n+\t/*\n+\t * Only SLOTS = 0 is honored for backwards compatibility with the\n+\t * old RAZ behavior. Reject any non-zero write that does not match\n+\t * the current value.\n+\t */\n+\tif (!slots)\n+\t\tkvm-\u003earch.pmmir_slots = 0;\n+\telse if (slots != kvm-\u003earch.pmmir_slots)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n \t\t\t const struct sys_reg_desc *r)\n {\n@@ -1444,6 +1502,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\n \t */\n \tif (!kvm_vm_has_ran_once(kvm) \u0026\u0026\n \t !vcpu_has_nv(vcpu)\t \u0026\u0026\n+\t !kvm_vcpu_has_pmuv3_strict(vcpu) \u0026\u0026\n \t new_n \u003c= kvm_arm_pmu_get_max_counters(kvm))\n \t\tkvm-\u003earch.nr_pmu_counters = new_n;\n \n@@ -3448,7 +3507,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {\n \t{ PMU_SYS_REG(PMINTENCLR_EL1),\n \t .access = access_pminten, .reg = PMINTENSET_EL1,\n \t .get_user = get_pmreg, .set_user = set_pmreg },\n-\t{ SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },\n+\t{ PMU_SYS_REG(PMMIR_EL1), .access = access_pmmir, .reset = NULL,\n+\t .get_user = get_pmmir, .set_user = set_pmmir },\n \n \t{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },\n \t{ SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1,\n@@ -4593,7 +4653,7 @@ static const struct sys_reg_desc cp15_regs[] = {\n \t{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 4), .access = access_pmceid },\n \t{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 5), .access = access_pmceid },\n \t/* PMMIR */\n-\t{ CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = trap_raz_wi },\n+\t{ CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = access_pmmir },\n \n \t/* PRRR/MAIR0 */\n \t{ AA32(LO), Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, MAIR_EL1 },\ndiff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h\nindex b5e5942204fc69..6b4a118d17ca99 100644\n--- a/include/kvm/arm_pmu.h\n+++ b/include/kvm/arm_pmu.h\n@@ -75,6 +75,9 @@ void kvm_vcpu_pmu_resync_el0(void);\n #define kvm_vcpu_has_pmu(vcpu)\t\t\t\t\t\\\n \t(vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3))\n \n+#define kvm_vcpu_has_pmuv3_strict(vcpu)\t\t\t\t\\\n+\t(vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3_STRICT))\n+\n /*\n * Updates the vcpu's view of the pmu events for this cpu.\n * Must be called before every vcpu run after disabling interrupts, to ensure\n@@ -160,6 +163,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n }\n \n #define kvm_vcpu_has_pmu(vcpu)\t\t({ false; })\n+#define kvm_vcpu_has_pmuv3_strict(vcpu)\t({ false; })\n static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}\n static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}\n static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}\ndiff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h\nindex 419011097fa8e2..9fc8dfdfd65ffa 100644\n--- a/include/uapi/linux/kvm.h\n+++ b/include/uapi/linux/kvm.h\n@@ -997,6 +997,7 @@ struct kvm_enable_cap {\n #define KVM_CAP_S390_KEYOP 247\n #define KVM_CAP_S390_VSIE_ESAMODE 248\n #define KVM_CAP_S390_HPAGE_2G 249\n+#define KVM_CAP_ARM_PMU_V3_STRICT 250\n \n struct kvm_irq_routing_irqchip {\n \t__u32 irqchip;\ndiff --git a/tools/testing/selftests/kvm/arm64/get-reg-list.c b/tools/testing/selftests/kvm/arm64/get-reg-list.c\nindex 0a3a94c4cca16f..cfa99979d57c8e 100644\n--- a/tools/testing/selftests/kvm/arm64/get-reg-list.c\n+++ b/tools/testing/selftests/kvm/arm64/get-reg-list.c\n@@ -532,6 +532,7 @@ static __u64 base_regs[] = {\n static __u64 pmu_regs[] = {\n \tARM64_SYS_REG(3, 0, 9, 14, 1),\t/* PMINTENSET_EL1 */\n \tARM64_SYS_REG(3, 0, 9, 14, 2),\t/* PMINTENCLR_EL1 */\n+\tARM64_SYS_REG(3, 0, 9, 14, 6),\t/* PMMIR_EL1 */\n \tARM64_SYS_REG(3, 3, 9, 12, 0),\t/* PMCR_EL0 */\n \tARM64_SYS_REG(3, 3, 9, 12, 1),\t/* PMCNTENSET_EL0 */\n \tARM64_SYS_REG(3, 3, 9, 12, 2),\t/* PMCNTENCLR_EL0 */\n"
} |
| 2/1 | 2026/07/23 21:33 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arm_pmu_v3_init",
"kvm_arm_pmu_v3_set_pmu",
"kvm_arm_pmu_v3_set_attr",
"set_pmcr",
"access_pmmir",
"set_pmmir",
"get_pmmir",
"kvm_pmu_get_pmceid"
],
"Reasoning": "The patch introduces a new KVM vCPU feature `KVM_ARM_VCPU_PMU_V3_STRICT` which enforces strict PMUv3 UAPI behavior. It modifies KVM ARM64 PMU emulation logic, including PMU initialization, setting PMU attributes, and handling PMU system registers (PMMIR, PMCR). This is reachable core kernel logic that can be triggered by userspace via KVM ioctls and guest execution, making it worth fuzzing.",
"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). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 9dca80c40fbed275cd60394ef9a670db91bc1276
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 23 21:33:06 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index a5f9ee92f43e8d..4988c32df4bf81 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -3515,6 +3515,17 @@ Possible features:
Depends on KVM_CAP_ARM_PSCI_0_2.
- KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU.
Depends on KVM_CAP_ARM_PMU_V3.
+ - KVM_ARM_VCPU_PMU_V3_STRICT: Enable strict PMUv3 UAPI.
+ Requires KVM_ARM_VCPU_PMU_V3. Depends on KVM_CAP_ARM_PMU_V3_STRICT.
+ When enabled:
+
+ * Userspace must explicitly select a PMU implementation before
+ initializing the PMU or configuring a PMU event filter
+
+ * If the PMU implements FEAT_PMUv3p4, PMMIR_EL1.SLOTS provides the
+ hardware value of the underlying implementation
+
+ * Writes to PMCR_EL0.N via KVM_SET_ONE_REG are ignored
- KVM_ARM_VCPU_PTRAUTH_ADDRESS: Enables Address Pointer authentication
for arm64 only.
diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
index 66e714f2fcfa7f..deb5c51bc00c87 100644
--- a/Documentation/virt/kvm/devices/vcpu.rst
+++ b/Documentation/virt/kvm/devices/vcpu.rst
@@ -53,8 +53,9 @@ Returns:
======= ======================================================
-EEXIST Interrupt number already used
-ENODEV PMUv3 not supported or GIC not initialized
- -ENXIO PMUv3 not supported, missing VCPU feature or interrupt
- number not set (non-GICv5 guests, only)
+ -ENXIO PMUv3 not supported, missing VCPU feature, missing
+ hardware PMU, or interrupt number not set (non-GICv5
+ guests, only)
-EBUSY PMUv3 already initialized
======= ======================================================
@@ -62,6 +63,9 @@ Request the initialization of the PMUv3. If using the PMUv3 with an in-kernel
virtual GIC implementation, this must be done after initializing the in-kernel
irqchip.
+When the KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature is enabled this must be done
+after selecting a hardware PMU.
+
1.3 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FILTER
-----------------------------------------
@@ -108,6 +112,9 @@ hardware event. Filtering event 0x1E (CHAIN) has no effect either, as it
isn't strictly speaking an event. Filtering the cycle counter is possible
using event 0x11 (CPU_CYCLES).
+When the KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature is enabled this must be done
+after selecting a hardware PMU.
+
1.4 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_SET_PMU
------------------------------------------
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5c0..39f7fc740d079b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -39,7 +39,7 @@
#define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS
-#define KVM_VCPU_MAX_FEATURES 9
+#define KVM_VCPU_MAX_FEATURES 10
#define KVM_VCPU_VALID_FEATURES (BIT(KVM_VCPU_MAX_FEATURES) - 1)
#define KVM_REQ_SLEEP \
@@ -387,6 +387,9 @@ struct kvm_arch {
/* Maximum number of counters for the guest */
u8 nr_pmu_counters;
+ /* PMMIR_EL1.SLOTS value exposed to the guest. */
+ u8 pmmir_slots;
+
/* Hypercall features firmware registers' descriptor */
struct kvm_smccc_features smccc_feat;
struct maple_tree smccc_filter;
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 1c13bfa2d38aad..019e5e3d892e6d 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -106,6 +106,7 @@ struct kvm_regs {
#define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */
#define KVM_ARM_VCPU_HAS_EL2 7 /* Support nested virtualization */
#define KVM_ARM_VCPU_HAS_EL2_E2H0 8 /* Limit NV support to E2H RES0 */
+#define KVM_ARM_VCPU_PMU_V3_STRICT 9 /* No default PMU creation */
struct kvm_vcpu_init {
__u32 target;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 50adfff75be82e..7dbefdd846aa70 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -452,6 +452,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
r = get_num_wrps();
break;
case KVM_CAP_ARM_PMU_V3:
+ case KVM_CAP_ARM_PMU_V3_STRICT:
r = kvm_supports_guest_pmuv3();
break;
case KVM_CAP_ARM_INJECT_SERROR_ESR:
@@ -1563,8 +1564,10 @@ static unsigned long system_supported_vcpu_features(void)
if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
- if (!kvm_supports_guest_pmuv3())
+ if (!kvm_supports_guest_pmuv3()) {
clear_bit(KVM_ARM_VCPU_PMU_V3, &features);
+ clear_bit(KVM_ARM_VCPU_PMU_V3_STRICT, &features);
+ }
if (!system_supports_sve())
clear_bit(KVM_ARM_VCPU_SVE, &features);
@@ -1605,6 +1608,11 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features))
return -EINVAL;
+ /* Strict PMUv3 UAPI requires PMUv3. */
+ if (test_bit(KVM_ARM_VCPU_PMU_V3_STRICT, &features) &&
+ !test_bit(KVM_ARM_VCPU_PMU_V3, &features))
+ return -EINVAL;
+
if (!test_bit(KVM_ARM_VCPU_EL1_32BIT, &features))
return 0;
@@ -1634,10 +1642,13 @@ static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)
int ret = 0;
/*
- * When the vCPU has a PMU, but no PMU is set for the guest
- * yet, set the default one.
+ * When the vCPU has a PMU, but no PMU is set for the guest yet, set
+ * the default one. If KVM_ARM_VCPU_PMU_V3_STRICT is set, no default
+ * PMU is created, and userspace must select a PMU via
+ * KVM_ARM_VCPU_PMU_V3_SET_PMU.
*/
- if (kvm_vcpu_has_pmu(vcpu) && !kvm->arch.arm_pmu)
+ if (kvm_vcpu_has_pmu(vcpu) && !kvm->arch.arm_pmu &&
+ !kvm_vcpu_has_pmuv3_strict(vcpu))
ret = kvm_arm_set_default_pmu(kvm);
/* Prepare for nested if required */
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 98305bbfc095ab..5b1af7e2176faf 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -838,9 +838,9 @@ static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)
return ((u64)hi[pmceid1] << 32) | lo[pmceid1];
}
-static u64 compute_pmceid0(struct arm_pmu *pmu)
+static u64 compute_pmceid0(struct kvm_vcpu *vcpu)
{
- u64 val = __compute_pmceid(pmu, 0);
+ u64 val = __compute_pmceid(vcpu->kvm->arch.arm_pmu, 0);
/* always support SW_INCR */
val |= BIT(ARMV8_PMUV3_PERFCTR_SW_INCR);
@@ -849,32 +849,33 @@ static u64 compute_pmceid0(struct arm_pmu *pmu)
return val;
}
-static u64 compute_pmceid1(struct arm_pmu *pmu)
+static u64 compute_pmceid1(struct kvm_vcpu *vcpu)
{
- u64 val = __compute_pmceid(pmu, 1);
+ u64 val = __compute_pmceid(vcpu->kvm->arch.arm_pmu, 1);
/*
- * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled
- * as RAZ
+ * If KVM_ARM_VCPU_PMU_V3_STRICT is not set, PMMIR_EL1 is
+ * unconditionally RAZ, so don't advertise STALL_SLOT* events.
*/
- val &= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) |
- BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) |
- BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32));
+ if (!kvm_vcpu_has_pmuv3_strict(vcpu))
+ val &= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) |
+ BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) |
+ BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32));
+
return val;
}
u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
{
- struct arm_pmu *cpu_pmu = vcpu->kvm->arch.arm_pmu;
unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
u64 val, mask = 0;
int base, i, nr_events;
if (!pmceid1) {
- val = compute_pmceid0(cpu_pmu);
+ val = compute_pmceid0(vcpu);
base = 0;
} else {
- val = compute_pmceid1(cpu_pmu);
+ val = compute_pmceid1(vcpu);
base = 32;
}
@@ -938,6 +939,10 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
{
+ /* Only possible when using KVM_ARM_VCPU_PMU_V3_STRICT */
+ if (!vcpu->kvm->arch.arm_pmu)
+ return -ENXIO;
+
if (irqchip_in_kernel(vcpu->kvm)) {
int ret;
@@ -1008,6 +1013,14 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
{
struct arm_pmu *arm_pmu = kvm->arch.arm_pmu;
+ /*
+ * Under KVM_ARM_VCPU_PMU_V3_STRICT no PMU exists until userspace sets
+ * one, so this can be reached before arm_pmu is set. Report no
+ * counters in that case.
+ */
+ if (!arm_pmu)
+ return 0;
+
/*
* PMUv3 requires that all event counters are capable of counting any
* event, though the same may not be true of non-PMUv3 hardware.
@@ -1049,7 +1062,8 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
}
/**
- * kvm_arm_set_default_pmu - No PMU set, get the default one.
+ * kvm_arm_set_default_pmu - No PMU set and KVM_ARM_VCPU_PMU_V3_STRICT not
+ * set, get the default one.
* @kvm: The kvm pointer
*
* The observant among you will notice that the supported_cpus
@@ -1092,6 +1106,17 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
kvm_arm_set_pmu(kvm, arm_pmu);
cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
+
+ /*
+ * Since a specific PMU is explicitly selected,
+ * PMMIR_EL1.SLOTS is deterministic to the guest.
+ * If KVM_ARM_VCPU_PMU_V3_STRICT is set, snapshot
+ * the value to allow the guest to read it.
+ */
+ if (kvm_vcpu_has_pmuv3_strict(vcpu))
+ kvm->arch.pmmir_slots =
+ FIELD_GET(ARMV8_PMU_SLOTS,
+ arm_pmu->reg_pmmir);
ret = 0;
break;
}
@@ -1178,6 +1203,9 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
if (kvm_vm_has_ran_once(kvm))
return -EBUSY;
+ if (!kvm->arch.arm_pmu)
+ return -ENXIO;
+
if (!kvm->arch.pmu_filter) {
kvm->arch.pmu_filter = bitmap_alloc(nr_events, GFP_KERNEL_ACCOUNT);
if (!kvm->arch.pmu_filter)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 5d5c579d457902..e93f11b90813b0 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1367,6 +1367,64 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
return true;
}
+static bool access_pmmir(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ if (p->is_write)
+ return write_to_read_only(vcpu, p, r);
+
+ /*
+ * If KVM_ARM_VCPU_PMU_V3_STRICT is set and PMU was explicitly
+ * selected, the underlying hardware SLOTS value was read into this
+ * field. Otherwise, it stays 0. All other PMMIR_EL1 fields are RAZ.
+ */
+ p->regval = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu->kvm->arch.pmmir_slots);
+ return true;
+}
+
+static int get_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
+ u64 *val)
+{
+ *val = FIELD_PREP(ARMV8_PMU_SLOTS, vcpu->kvm->arch.pmmir_slots);
+ return 0;
+}
+
+static int set_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
+ u64 val)
+{
+ struct kvm *kvm = vcpu->kvm;
+ u8 slots = FIELD_GET(ARMV8_PMU_SLOTS, val);
+
+ /*
+ * Only the SLOTS field is exposed (get_pmmir returns just that field),
+ * so reject a write that sets any other bit rather than silently
+ * masking it.
+ */
+ if (val & ~(u64)ARMV8_PMU_SLOTS)
+ return -EINVAL;
+
+ guard(mutex)(&kvm->arch.config_lock);
+
+ /*
+ * Once the VM has started PMMIR_EL1 is immutable. Reject any write
+ * that does not match the current value.
+ */
+ if (kvm_vm_has_ran_once(kvm))
+ return slots == kvm->arch.pmmir_slots ? 0 : -EBUSY;
+
+ /*
+ * Only SLOTS = 0 is honored for backwards compatibility with the
+ * old RAZ behavior. Reject any non-zero write that does not match
+ * the current value.
+ */
+ if (!slots)
+ kvm->arch.pmmir_slots = 0;
+ else if (slots != kvm->arch.pmmir_slots)
+ return -EINVAL;
+
+ return 0;
+}
+
static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -1444,6 +1502,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
*/
if (!kvm_vm_has_ran_once(kvm) &&
!vcpu_has_nv(vcpu) &&
+ !kvm_vcpu_has_pmuv3_strict(vcpu) &&
new_n <= kvm_arm_pmu_get_max_counters(kvm))
kvm->arch.nr_pmu_counters = new_n;
@@ -3448,7 +3507,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ PMU_SYS_REG(PMINTENCLR_EL1),
.access = access_pminten, .reg = PMINTENSET_EL1,
.get_user = get_pmreg, .set_user = set_pmreg },
- { SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
+ { PMU_SYS_REG(PMMIR_EL1), .access = access_pmmir, .reset = NULL,
+ .get_user = get_pmmir, .set_user = set_pmmir },
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
{ SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1,
@@ -4593,7 +4653,7 @@ static const struct sys_reg_desc cp15_regs[] = {
{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 4), .access = access_pmceid },
{ CP15_PMU_SYS_REG(HI, 0, 9, 14, 5), .access = access_pmceid },
/* PMMIR */
- { CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = trap_raz_wi },
+ { CP15_PMU_SYS_REG(DIRECT, 0, 9, 14, 6), .access = access_pmmir },
/* PRRR/MAIR0 */
{ AA32(LO), Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, MAIR_EL1 },
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index b5e5942204fc69..6b4a118d17ca99 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -75,6 +75,9 @@ void kvm_vcpu_pmu_resync_el0(void);
#define kvm_vcpu_has_pmu(vcpu) \
(vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3))
+#define kvm_vcpu_has_pmuv3_strict(vcpu) \
+ (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3_STRICT))
+
/*
* Updates the vcpu's view of the pmu events for this cpu.
* Must be called before every vcpu run after disabling interrupts, to ensure
@@ -160,6 +163,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
}
#define kvm_vcpu_has_pmu(vcpu) ({ false; })
+#define kvm_vcpu_has_pmuv3_strict(vcpu) ({ false; })
static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}
static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}
static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 419011097fa8e2..9fc8dfdfd65ffa 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -997,6 +997,7 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_S390_HPAGE_2G 249
+#define KVM_CAP_ARM_PMU_V3_STRICT 250
struct kvm_irq_routing_irqchip {
__u32 irqchip;
diff --git a/tools/testing/selftests/kvm/arm64/get-reg-list.c b/tools/testing/selftests/kvm/arm64/get-reg-list.c
index 0a3a94c4cca16f..cfa99979d57c8e 100644
--- a/tools/testing/selftests/kvm/arm64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/arm64/get-reg-list.c
@@ -532,6 +532,7 @@ static __u64 base_regs[] = {
static __u64 pmu_regs[] = {
ARM64_SYS_REG(3, 0, 9, 14, 1), /* PMINTENSET_EL1 */
ARM64_SYS_REG(3, 0, 9, 14, 2), /* PMINTENCLR_EL1 */
+ ARM64_SYS_REG(3, 0, 9, 14, 6), /* PMMIR_EL1 */
ARM64_SYS_REG(3, 3, 9, 12, 0), /* PMCR_EL0 */
ARM64_SYS_REG(3, 3, 9, 12, 1), /* PMCNTENSET_EL0 */
ARM64_SYS_REG(3, 3, 9, 12, 2), /* PMCNTENCLR_EL0 */
|
| 3/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8719 output: 34 thoughts: 342 Thoughts: **My Assessment of the New KVM vCPU Feature** Okay, so we have this patch proposing a new KVM vCPU feature for ARM64, `KVM_ARM_VCPU_PMU_V3_STRICT`. It sounds like it enforces stricter behavior for the PMUv3 UAPI, specifically around how userspace interacts with it. This involves explicit selection of a PMU implementation before initialization or filter configuration, and taking `PMMIR_EL1.SLOTS` into account if FEAT_PMUv3p4 is supported. The patch also ignores writes to `PMCR_EL0.N` via `KVM_SET_ONE_REG`, which seems like a sensible hardening measure. I'm seeing that this touches quite a few key parts of the core KVM ARM64 PMU emulation, namely `kvm_arm_pmu_v3_init`, `kvm_arm_pmu_v3_set_pmu`, `kvm_arm_pmu_v3_set_attr`, `set_pmcr`, `access_pmmir`, `set_pmmir`, and `get_pmmir`. Now, the important question is: is this worth fuzzing? Considering that it's a new KVM feature that can be enabled via `KVM_ENABLE_CAP` or `KVM_ARM_VCPU_INIT`, absolutely, yes. Any new feature is a potential attack surface, and the changes listed are definitely something I want to scrutinize for any potential vulnerabilities. This is specifically for ARM64 KVM, so I'll want to focus on that. Time to look for the relevant CONFIG options. `CONFIG_KVM` is obviously a prerequisite. I'll need to dig into `arch/arm64/kvm/Kconfig` or similar files to see what else might be involved in enabling this feature. |
| 4/2 | 2026/07/23 21:33 | tool | grepper |
0mArgs:{
"Expression": "config KVM",
"PathPrefix": "arch/arm64/kvm/Kconfig"
}Results: {
"Output": "arch/arm64/kvm/Kconfig=18=if VIRTUALIZATION\narch/arm64/kvm/Kconfig-19-\narch/arm64/kvm/Kconfig:20:menuconfig KVM\narch/arm64/kvm/Kconfig-21-\tbool \"Kernel-based Virtual Machine (KVM) support\"\n"
} |
| 5/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9601 output: 37 thoughts: 59 Thoughts: Okay, let's see, the core of this seems to revolve around `CONFIG_KVM`. That's the primary kernel configuration option I'm interested in here. I need to be sure I'm not missing anything crucial. My next step is to cast a wider net and look for any related configurations that might also be relevant. I should check through `arch/arm64/kvm/pmu-emul.c` and `arch/arm64/kvm/sys_regs.c`. This is where the finer details are implemented. Hopefully, that will give me a more complete picture. |
| 6/2 | 2026/07/23 21:33 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_HW_PERF_EVENTS",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/Makefile=16=kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \\\n--\narch/arm64/kvm/Makefile-28-\narch/arm64/kvm/Makefile:29:kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o\narch/arm64/kvm/Makefile-30-kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o\n--\narch/arm64/kvm/hyp/nvhe/switch.c=129=static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/hyp/nvhe/switch.c-145- */\narch/arm64/kvm/hyp/nvhe/switch.c:146:#ifdef CONFIG_HW_PERF_EVENTS\narch/arm64/kvm/hyp/nvhe/switch.c-147-static bool __pmu_switch_to_guest(struct kvm_vcpu *vcpu)\n"
} |
| 7/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10127 output: 33 thoughts: 57 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Investigating the Build Dependency of Performance Monitoring Components** Right, so I see that I need `CONFIG_HW_PERF_EVENTS` to successfully compile `pmu-emul.o` and `pmu.o`. That's not entirely surprising, but I need to get a clear picture here. My next step is definitely to dig into the kernel configuration and figure out how `CONFIG_HW_PERF_EVENTS` is handled. Is this a default option, or is it contingent on some other specific kernel feature or hardware support? Understanding its dependencies is critical. I need to know under what circumstances these performance monitoring components will actually get built. The goal is to either verify this is a standard configuration or isolate the variable dependencies at play, so that I can anticipate how this impacts my own target builds and overall system configuration. Time to dive into the Kconfig! |
| 8/2 | 2026/07/23 21:33 | tool | grepper |
0mArgs:{
"Expression": "config HW_PERF_EVENTS",
"PathPrefix": "arch/arm64/"
}Results: {
"Output": "arch/arm64/Kconfig=1606=config ARCH_SPARSEMEM_ENABLE\n--\narch/arm64/Kconfig-1609-\narch/arm64/Kconfig:1610:config HW_PERF_EVENTS\narch/arm64/Kconfig-1611-\tdef_bool y\n"
} |
| 9/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10510 output: 36 thoughts: 40 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **ARM64 KVM and Boolean Definitions** Alright, so I'm focusing on an `arm64` system, specifically dealing with KVM. My immediate concern is with `def_bool y`, which I'm taking to mean a boolean definition set to "yes" or potentially enabled. This suggests a configuration option is set, likely impacting some aspect of the KVM implementation on this architecture. My next move is to immediately zero in on `arch/arm64/kvm/sys_regs.c`. This file seems the most likely place to find relevant configurations related to system registers within the ARM64 KVM context. I expect to find further definitions, conditional blocks, or macros tied to this boolean. I'm hoping to discover the ramifications of this boolean setting in terms of register access or virtualization behavior. I'll be looking for where this `def_bool y` value is used, how it interacts with other KVM features, and its overall impact on the system's performance and security profile. |
| 10/2 | 2026/07/23 21:33 | tool | grepper |
0mError:no matches Args: {
"Expression": "CONFIG_",
"PathPrefix": "arch/arm64/kvm/sys_regs.c"
}Results: {
"error": "no matches"
} |
| 11/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10832 output: 38 thoughts: 23 Thoughts: Here's my thought process, as requested: **Investigating the Absence of Configuration Directives** Okay, so I'm looking at `sys_regs.c`, and I'm a bit thrown. I expected to find some `CONFIG_` directives in this file. But wait, there aren't any? That's unusual. It warrants a closer look. I need to figure out why this file deviates from the expected pattern. Time to dive deeper and see what's actually going on in here. I need to understand this lack of configuration options in order to fully understand this code base. |
| 12/2 | 2026/07/23 21:33 | tool | grepper |
0mError:no matches Args: {
"Expression": "#ifdef CONFIG_",
"PathPrefix": "arch/arm64/kvm/sys_regs.c"
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11034 output: 123 thoughts: 454 Thoughts: **Analyzing the KVM Patch for Performance Monitoring** Alright, let's dive into this KVM patch. The central element, as expected, is `CONFIG_KVM`. My focus is on understanding the modifications and their impact on performance monitoring. First, I need to identify the key symbols this patch touches. It looks like we're primarily dealing with these functions: `kvm_arm_pmu_v3_init`, `kvm_arm_pmu_v3_set_pmu`, `kvm_arm_pmu_v3_set_attr`, `set_pmcr`, `access_pmmir`, `set_pmmir`, `get_pmmir`, `kvm_pmu_get_pmceid`, `kvm_arm_pmu_get_max_counters`, and `kvm_arm_set_default_pmu`. Okay, a pretty comprehensive list. Now, a quick assessment of accessibility and potential hotspots is in order. It seems `kvm_arm_pmu_v3_init`, `kvm_arm_pmu_v3_set_pmu`, and `kvm_arm_pmu_v3_set_attr` are triggered by ioctls – specifically, `KVM_SET_DEVICE_ATTR`. That's reasonable, implying configuration changes. `set_pmcr`, `access_pmmir`, `set_pmmir`, and `get_pmmir` are linked to either KVM register access (`KVM_SET_ONE_REG`/`KVM_GET_ONE_REG`) or guest traps – the interaction between host and guest. `kvm_pmu_get_pmceid` is clearly handling guest access to the PMCEID register, and `kvm_arm_pmu_get_max_counters` is a helper function, which may be called from other functions. Let's consolidate the core symbols: `kvm_arm_pmu_v3_init`, `kvm_arm_pmu_v3_set_pmu`, `kvm_arm_pmu_v3_set_attr`, `set_pmcr`, `access_pmmir`, `set_pmmir`, `get_pmmir`, and `kvm_pmu_get_pmceid`. These are the functions I want to keep a close eye on. I should verify the names in the source code to be absolutely sure. |
| 14/2 | 2026/07/23 21:33 | tool | grepper |
0mArgs:{
"Expression": "^(static )?(int|bool|u8|u64|void) (kvm_arm_pmu_v3_init|kvm_arm_pmu_v3_set_pmu|kvm_arm_pmu_v3_set_attr|set_pmcr|access_pmmir|set_pmmir|get_pmmir|kvm_pmu_get_pmceid)",
"PathPrefix": "arch/arm64/kvm/"
}Results: {
"Output": "arch/arm64/kvm/pmu-emul.c=852=static u64 compute_pmceid1(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/pmu-emul.c-867-\narch/arm64/kvm/pmu-emul.c:868:u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\narch/arm64/kvm/pmu-emul.c-869-{\n--\narch/arm64/kvm/pmu-emul.c=912=int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/pmu-emul.c-939-\narch/arm64/kvm/pmu-emul.c:940:static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)\narch/arm64/kvm/pmu-emul.c-941-{\n--\narch/arm64/kvm/pmu-emul.c=1077=int kvm_arm_set_default_pmu(struct kvm *kvm)\n--\narch/arm64/kvm/pmu-emul.c-1087-\narch/arm64/kvm/pmu-emul.c:1088:static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)\narch/arm64/kvm/pmu-emul.c-1089-{\n--\narch/arm64/kvm/pmu-emul.c=1129=static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned int n)\n--\narch/arm64/kvm/pmu-emul.c-1142-\narch/arm64/kvm/pmu-emul.c:1143:int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\narch/arm64/kvm/pmu-emul.c-1144-{\n--\narch/arm64/kvm/sys_regs.c=1346=static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\n--\narch/arm64/kvm/sys_regs.c-1369-\narch/arm64/kvm/sys_regs.c:1370:static bool access_pmmir(struct kvm_vcpu *vcpu, struct sys_reg_params *p,\narch/arm64/kvm/sys_regs.c-1371-\t\t\t const struct sys_reg_desc *r)\n--\narch/arm64/kvm/sys_regs.c-1384-\narch/arm64/kvm/sys_regs.c:1385:static int get_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\narch/arm64/kvm/sys_regs.c-1386-\t\t u64 *val)\n--\narch/arm64/kvm/sys_regs.c-1391-\narch/arm64/kvm/sys_regs.c:1392:static int set_pmmir(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\narch/arm64/kvm/sys_regs.c-1393-\t\t u64 val)\n--\narch/arm64/kvm/sys_regs.c=1483=static int get_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\n--\narch/arm64/kvm/sys_regs.c-1489-\narch/arm64/kvm/sys_regs.c:1490:static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\narch/arm64/kvm/sys_regs.c-1491-\t\t u64 val)\n"
} |
| 15/2 | 2026/07/23 21:33 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13251 output: 230 thoughts: 590 Thoughts: **Analysis of KVM ARM64 PMUv3 Emulation Code for Fuzzing** Okay, let's break this down. My initial assessment of these functions is spot on: `kvm_arm_pmu_v3_init`, `kvm_arm_pmu_v3_set_pmu`, `kvm_arm_pmu_v3_set_attr`, `set_pmcr`, `access_pmmir`, `set_pmmir`, `get_pmmir`, and `kvm_pmu_get_pmceid` – these are all excellent starting points for fuzzing. They represent key entry points and core functionality within the PMUv3 emulation. Wait, looking closer at the call graph, `kvm_arm_pmu_v3_set_pmu` being static but called from `kvm_arm_pmu_v3_set_attr` is interesting. And then `set_pmcr` is static too, called from `set_pmreg` - that will be fun to explore. Same goes for `access_pmmir`, `set_pmmir`, and `get_pmmir` - static, but used within the `sys_reg_descs` table. Now, the configuration aspect. It looks like `CONFIG_KVM` is the primary dependency. A quick check of the modified files (`arch/arm64/kvm/arm.c`, `arch/arm64/kvm/pmu-emul.c`, and `arch/arm64/kvm/sys_regs.c`) confirms that `CONFIG_KVM` pulls everything in. And `pmu-emul.c` additionally depends on `CONFIG_HW_PERF_EVENTS`, which is statically enabled for ARM64. So, yes, `CONFIG_KVM` should be sufficient for a fuzzing build. Excellent. Let's review the implications. This patch implements a new `KVM_ARM_VCPU_PMU_V3_STRICT` feature for strict PMUv3 UAPI compliance. That means this is touching critical emulation logic – initialization, attribute setting, and system register handling (PMMIR, PMCR). And the kicker: this logic is directly reachable via KVM ioctls and guest execution. This makes it a prime target for fuzzing. I can absolutely see how fuzzing can reveal interesting bugs here. So, to summarize for fuzzing purposes: **Focus Symbols:** * `kvm_arm_pmu_v3_init` * `kvm_arm_pmu_v3_set_pmu` * `kvm_arm_pmu_v3_set_attr` * `set_pmcr` * `access_pmmir` * `set_pmmir` * `get_pmmir` * `kvm_pmu_get_pmceid` **Configuration:** * `KVM` I'm ready to set up the fuzzing environment. |
| 16/2 | 2026/07/23 21:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arm_pmu_v3_init",
"kvm_arm_pmu_v3_set_pmu",
"kvm_arm_pmu_v3_set_attr",
"set_pmcr",
"access_pmmir",
"set_pmmir",
"get_pmmir",
"kvm_pmu_get_pmceid"
],
"Reasoning": "The patch introduces a new KVM vCPU feature `KVM_ARM_VCPU_PMU_V3_STRICT` which enforces strict PMUv3 UAPI behavior. It modifies KVM ARM64 PMU emulation logic, including PMU initialization, setting PMU attributes, and handling PMU system registers (PMMIR, PMCR). This is reachable core kernel logic that can be triggered by userspace via KVM ioctls and guest execution, making it worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arm_pmu_v3_init",
"kvm_arm_pmu_v3_set_pmu",
"kvm_arm_pmu_v3_set_attr",
"set_pmcr",
"access_pmmir",
"set_pmmir",
"get_pmmir",
"kvm_pmu_get_pmceid"
],
"Reasoning": "The patch introduces a new KVM vCPU feature `KVM_ARM_VCPU_PMU_V3_STRICT` which enforces strict PMUv3 UAPI behavior. It modifies KVM ARM64 PMU emulation logic, including PMU initialization, setting PMU attributes, and handling PMU system registers (PMMIR, PMCR). This is reachable core kernel logic that can be triggered by userspace via KVM ioctls and guest execution, making it worth fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|