val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTEX);
                val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_DF2);
                val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_PFAR);
+               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MPAM_frac);
                break;
        case SYS_ID_AA64PFR2_EL1:
                /* We only expose FPMR */
 
        val &= ~ID_AA64PFR0_EL1_AMU_MASK;
 
+       /*
+        * MPAM is disabled by default as KVM also needs a set of PARTID to
+        * program the MPAMVPMx_EL2 PARTID remapping registers with. But some
+        * older kernels let the guest see the ID bit.
+        */
+       val &= ~ID_AA64PFR0_EL1_MPAM_MASK;
+
        return val;
 }
 
 }
 
 static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
-                              const struct sys_reg_desc *rd, u64 val)
+                              const struct sys_reg_desc *rd, u64 user_val)
 {
-       return set_id_reg(vcpu, rd, val);
+       u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+       u64 mpam_mask = ID_AA64PFR0_EL1_MPAM_MASK;
+
+       /*
+        * Commit 011e5f5bf529f ("arm64/cpufeature: Add remaining feature bits
+        * in ID_AA64PFR0 register") exposed the MPAM field of AA64PFR0_EL1 to
+        * guests, but didn't add trap handling. KVM doesn't support MPAM and
+        * always returns an UNDEF for these registers. The guest must see 0
+        * for this field.
+        *
+        * But KVM must also accept values from user-space that were provided
+        * by KVM. On CPUs that support MPAM, permit user-space to write
+        * the sanitizied value to ID_AA64PFR0_EL1.MPAM, but ignore this field.
+        */
+       if ((hw_val & mpam_mask) == (user_val & mpam_mask))
+               user_val &= ~ID_AA64PFR0_EL1_MPAM_MASK;
+
+       return set_id_reg(vcpu, rd, user_val);
+}
+
+static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
+                              const struct sys_reg_desc *rd, u64 user_val)
+{
+       u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1);
+       u64 mpam_mask = ID_AA64PFR1_EL1_MPAM_frac_MASK;
+
+       /* See set_id_aa64pfr0_el1 for comment about MPAM */
+       if ((hw_val & mpam_mask) == (user_val & mpam_mask))
+               user_val &= ~ID_AA64PFR1_EL1_MPAM_frac_MASK;
+
+       return set_id_reg(vcpu, rd, user_val);
 }
 
 /*
                      ID_AA64PFR0_EL1_RAS |
                      ID_AA64PFR0_EL1_AdvSIMD |
                      ID_AA64PFR0_EL1_FP)),
-       ID_WRITABLE(ID_AA64PFR1_EL1, ~(ID_AA64PFR1_EL1_PFAR |
+       ID_FILTERED(ID_AA64PFR1_EL1, id_aa64pfr1_el1,
+                                    ~(ID_AA64PFR1_EL1_PFAR |
                                       ID_AA64PFR1_EL1_DF2 |
                                       ID_AA64PFR1_EL1_MTEX |
                                       ID_AA64PFR1_EL1_THE |