}
 }
 
+static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
+static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
+
 /* Read a sanitised cpufeature ID register by sys_reg_desc */
 static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
                                       const struct sys_reg_desc *r)
        val = read_sanitised_ftr_reg(id);
 
        switch (id) {
+       case SYS_ID_AA64DFR0_EL1:
+               val = sanitise_id_aa64dfr0_el1(vcpu, val);
+               break;
+       case SYS_ID_AA64PFR0_EL1:
+               val = sanitise_id_aa64pfr0_el1(vcpu, val);
+               break;
        case SYS_ID_AA64PFR1_EL1:
                if (!kvm_has_mte(vcpu->kvm))
                        val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
        return REG_HIDDEN;
 }
 
-static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-                                         const struct sys_reg_desc *rd)
+static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
 {
-       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
-
        if (!vcpu_has_sve(vcpu))
                val &= ~ID_AA64PFR0_EL1_SVE_MASK;
 
        (val);                                                                 \
 })
 
-static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
-                                         const struct sys_reg_desc *rd)
+static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
 {
-       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-
        val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8);
 
        /*
        return set_id_reg(vcpu, rd, val);
 }
 
+static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
+                              const struct sys_reg_desc *rd, u64 val)
+{
+       return set_id_reg(vcpu, rd, val);
+}
+
 /*
  * cpufeature ID register user accessors
  *
        .val = mask,                            \
 }
 
+/* sys_reg_desc initialiser for cpufeature ID registers that need filtering */
+#define ID_FILTERED(sysreg, name, mask) {      \
+       ID_DESC(sysreg),                                \
+       .set_user = set_##name,                         \
+       .visibility = id_visibility,                    \
+       .reset = kvm_read_sanitised_id_reg,             \
+       .val = (mask),                                  \
+}
+
 /*
  * sys_reg_desc initialiser for architecturally unallocated cpufeature ID
  * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
 
        /* AArch64 ID registers */
        /* CRm=4 */
-       { SYS_DESC(SYS_ID_AA64PFR0_EL1),
-         .access = access_id_reg,
-         .get_user = get_id_reg,
-         .set_user = set_id_reg,
-         .reset = read_sanitised_id_aa64pfr0_el1,
-         .val = ~(ID_AA64PFR0_EL1_AMU |
-                  ID_AA64PFR0_EL1_MPAM |
-                  ID_AA64PFR0_EL1_SVE |
-                  ID_AA64PFR0_EL1_RAS |
-                  ID_AA64PFR0_EL1_AdvSIMD |
-                  ID_AA64PFR0_EL1_FP), },
+       ID_FILTERED(ID_AA64PFR0_EL1, id_aa64pfr0_el1,
+                   ~(ID_AA64PFR0_EL1_AMU |
+                     ID_AA64PFR0_EL1_MPAM |
+                     ID_AA64PFR0_EL1_SVE |
+                     ID_AA64PFR0_EL1_RAS |
+                     ID_AA64PFR0_EL1_AdvSIMD |
+                     ID_AA64PFR0_EL1_FP)),
        ID_WRITABLE(ID_AA64PFR1_EL1, ~(ID_AA64PFR1_EL1_PFAR |
                                       ID_AA64PFR1_EL1_DF2 |
                                       ID_AA64PFR1_EL1_MTEX |
        ID_WRITABLE(ID_AA64FPFR0_EL1, ~ID_AA64FPFR0_EL1_RES0),
 
        /* CRm=5 */
-       { SYS_DESC(SYS_ID_AA64DFR0_EL1),
-         .access = access_id_reg,
-         .get_user = get_id_reg,
-         .set_user = set_id_aa64dfr0_el1,
-         .reset = read_sanitised_id_aa64dfr0_el1,
        /*
         * Prior to FEAT_Debugv8.9, the architecture defines context-aware
         * breakpoints (CTX_CMPs) as the highest numbered breakpoints (BRPs).
         * See DDI0487K.a, section D2.8.3 Breakpoint types and linking
         * of breakpoints for more details.
         */
-         .val = ID_AA64DFR0_EL1_DoubleLock_MASK |
-                ID_AA64DFR0_EL1_WRPs_MASK |
-                ID_AA64DFR0_EL1_PMUVer_MASK |
-                ID_AA64DFR0_EL1_DebugVer_MASK, },
+       ID_FILTERED(ID_AA64DFR0_EL1, id_aa64dfr0_el1,
+                   ID_AA64DFR0_EL1_DoubleLock_MASK |
+                   ID_AA64DFR0_EL1_WRPs_MASK |
+                   ID_AA64DFR0_EL1_PMUVer_MASK |
+                   ID_AA64DFR0_EL1_DebugVer_MASK),
        ID_SANITISED(ID_AA64DFR1_EL1),
        ID_UNALLOCATED(5,2),
        ID_UNALLOCATED(5,3),