diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h
index 8f266baa26..0272e61c21 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -192,6 +192,8 @@ struct ARMCPUClass {
     OnOffAuto has_vfp;
     /* CPU has Neon */
     OnOffAuto has_neon;
+    /* CPU has PMU (Performance Monitor Unit) */
+    OnOffAuto has_pmu;
 
     /* CPU has memory protection unit */
     bool has_mpu;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 1b181ecde4..dac72045d1 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -886,9 +886,6 @@ struct ArchCPU {
     /* Current power state, access guarded by BQL */
     ARMPSCIState power_state;
 
-    /* CPU has PMU (Performance Monitor Unit) */
-    bool has_pmu;
-
     /* PMSAv7 MPU number of supported regions */
     uint32_t pmsav7_dregion;
     /* v8M SAU number of supported regions */
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index d426e24c53..a958e071c1 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -177,14 +177,6 @@ void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp);
  */
 bool kvm_arm_aarch32_supported(void);
 
-/**
- * kvm_arm_pmu_supported:
- *
- * Returns: true if KVM can enable the PMU
- * and false otherwise.
- */
-bool kvm_arm_pmu_supported(void);
-
 /**
  * kvm_arm_sve_supported:
  *
@@ -229,11 +221,6 @@ static inline bool kvm_arm_aarch32_supported(void)
     return false;
 }
 
-static inline bool kvm_arm_pmu_supported(void)
-{
-    return false;
-}
-
 static inline bool kvm_arm_sve_supported(void)
 {
     return false;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c1cabe2413..38f89559ed 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -605,7 +605,6 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
     MachineState *ms = MACHINE(vms);
 
     if (!arm_feature(&armcpu->env, ARM_FEATURE_PMU)) {
-        assert(!object_property_get_bool(OBJECT(armcpu), "pmu", NULL));
         return;
     }
 
@@ -1951,9 +1950,11 @@ static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem)
     int max_cpus = MACHINE(vms)->smp.max_cpus;
     bool aarch64, pmu, steal_time;
     CPUState *cpu;
+    ObjectClass *cpu_class;
 
+    cpu_class = object_get_class(OBJECT(first_cpu));
     aarch64 = object_property_get_bool(OBJECT(first_cpu), "aarch64", NULL);
-    pmu = object_property_get_bool(OBJECT(first_cpu), "pmu", NULL);
+    pmu = class_property_get_bool(cpu_class, "pmu", NULL);
     steal_time = object_property_get_bool(OBJECT(first_cpu),
                                           "kvm-steal-time", NULL);
 
@@ -2043,6 +2044,9 @@ static void machvirt_init(MachineState *machine)
     if (!vms->virt) {
         class_property_set_bool(cpu_class, "has_el2", false, &error_abort);
     }
+    if (vmc->no_pmu) {
+        class_property_set_bool(cpu_class, "pmu", false, &error_abort);
+    }
 
     /*
      * In accelerated mode, the memory map is computed earlier in kvm_type()
@@ -2185,10 +2189,6 @@ static void machvirt_init(MachineState *machine)
             object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL);
         }
 
-        if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) {
-            object_property_set_bool(cpuobj, "pmu", false, NULL);
-        }
-
         if (vmc->no_tcg_lpa2 && object_property_find(cpuobj, "lpa2")) {
             object_property_set_bool(cpuobj, "lpa2", false, NULL);
         }
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index b984735793..c287b0bc89 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1279,29 +1279,6 @@ static void arm_cpu_initfn(Object *obj)
 static Property arm_cpu_reset_cbar_property =
             DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0);
 
-static bool arm_get_pmu(Object *obj, Error **errp)
-{
-    ARMCPU *cpu = ARM_CPU(obj);
-
-    return cpu->has_pmu;
-}
-
-static void arm_set_pmu(Object *obj, bool value, Error **errp)
-{
-    ARMCPU *cpu = ARM_CPU(obj);
-
-    if (value) {
-        if (kvm_enabled() && !kvm_arm_pmu_supported()) {
-            error_setg(errp, "'pmu' feature not supported by KVM on this host");
-            return;
-        }
-        set_feature(&cpu->env, ARM_FEATURE_PMU);
-    } else {
-        unset_feature(&cpu->env, ARM_FEATURE_PMU);
-    }
-    cpu->has_pmu = value;
-}
-
 unsigned int gt_cntfrq_period_ns(ARMCPU *cpu)
 {
     ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu);
@@ -1357,11 +1334,6 @@ static void arm_cpu_post_init(Object *obj)
     }
 #endif
 
-    if (arm_feature(&cpu->env, ARM_FEATURE_PMU)) {
-        cpu->has_pmu = true;
-        object_property_add_bool(obj, "pmu", arm_get_pmu, arm_set_pmu);
-    }
-
     if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) {
         object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau,
                                  qdev_prop_allow_set_link_before_realize,
@@ -1586,9 +1558,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
-    if (!cpu->has_pmu) {
-        unset_feature(env, ARM_FEATURE_PMU);
-    }
     if (arm_feature(env, ARM_FEATURE_PMU)) {
         pmu_init(cpu);
 
@@ -2163,6 +2132,13 @@ static void arm_cpu_leaf_class_init(ObjectClass *oc, void *data)
                            arm_class_prop_set_auto_ofs,
                            (void *)(uintptr_t)offsetof(ARMCPUClass, has_el3));
     }
+
+    if (arm_class_feature(acc, ARM_FEATURE_PMU)) {
+        class_property_add(oc, "pmu", "bool", NULL,
+                           arm_class_prop_get_auto_ofs,
+                           arm_class_prop_set_auto_ofs,
+                           (void *)(uintptr_t)offsetof(ARMCPUClass, has_pmu));
+    }
 #endif /* !CONFIG_USER_ONLY */
 
     /*
@@ -2279,6 +2255,25 @@ static bool arm_cpu_class_late_init(ObjectClass *oc, Error **errp)
     default:
         g_assert_not_reached();
     }
+
+    switch (acc->has_pmu) {
+    case ON_OFF_AUTO_AUTO:
+        acc->has_pmu = (arm_class_feature(acc, ARM_FEATURE_PMU)
+                        ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF);
+        break;
+    case ON_OFF_AUTO_OFF:
+        unset_class_feature(acc, ARM_FEATURE_PMU);
+        break;
+    case ON_OFF_AUTO_ON:
+        if (!arm_class_feature(acc, ARM_FEATURE_PMU)) {
+            error_setg(errp, "'pmu' feature not supported by %s on this host",
+                       current_accel_name());
+            return false;
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
 #endif /* !CONFIG_USER_ONLY */
 
     if (!arm_class_feature(acc, ARM_FEATURE_M)) {
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 85971df07c..0ae435addd 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -239,7 +239,13 @@ void kvm_arm_add_vcpu_properties(Object *obj)
                                     "Set off to disable KVM steal time.");
 }
 
-bool kvm_arm_pmu_supported(void)
+/**
+ * kvm_arm_pmu_supported:
+ *
+ * Returns: true if KVM can enable the PMU
+ * and false otherwise.
+ */
+static bool kvm_arm_pmu_supported(void)
 {
     return kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3);
 }
@@ -1463,7 +1469,7 @@ void kvm_arm_pmu_init(CPUState *cs)
         .attr = KVM_ARM_VCPU_PMU_V3_INIT,
     };
 
-    if (!ARM_CPU(cs)->has_pmu) {
+    if (!arm_feature(&ARM_CPU(cs)->env, ARM_FEATURE_PMU)) {
         return;
     }
     if (!kvm_arm_set_device_attr(cs, &attr, "PMU")) {
@@ -1480,7 +1486,7 @@ void kvm_arm_pmu_set_irq(CPUState *cs, int irq)
         .attr = KVM_ARM_VCPU_PMU_V3_IRQ,
     };
 
-    if (!ARM_CPU(cs)->has_pmu) {
+    if (!arm_feature(&ARM_CPU(cs)->env, ARM_FEATURE_PMU)) {
         return;
     }
     if (!kvm_arm_set_device_attr(cs, &attr, "PMU")) {
@@ -1594,6 +1600,9 @@ bool kvm_arm_get_host_cpu_features(ARMCPUClass *acc, Error **errp)
     if (kvm_arm_pmu_supported()) {
         init.features[0] |= 1 << KVM_ARM_VCPU_PMU_V3;
         pmu_supported = true;
+    } else {
+        /* This was optimistically set in aarch64_host_class_init. */
+        unset_class_feature(acc, ARM_FEATURE_PMU);
     }
 
     if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
@@ -1877,7 +1886,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
 {
     int ret;
     ARMCPU *cpu = ARM_CPU(cs);
-    CPUARMState *env = &cpu->env;
     uint64_t psciver;
 
     if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
@@ -1900,13 +1908,9 @@ int kvm_arch_init_vcpu(CPUState *cs)
     if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
         cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT;
     }
-    if (!kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) {
-        cpu->has_pmu = false;
-    }
-    if (cpu->has_pmu) {
+    if (arm_feature(&cpu->env, ARM_FEATURE_PMU)) {
+        assert(kvm_arm_pmu_supported());
         cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3;
-    } else {
-        env->features &= ~(1ULL << ARM_FEATURE_PMU);
     }
     if (cpu_isar_feature(aa64_sve, cpu)) {
         assert(kvm_arm_sve_supported());
