return 1;
 }
 
+static bool emulate_sys_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params);
+
+/**
+ * kvm_emulate_cp15_id_reg() - Handles an MRC trap on a guest CP15 access where
+ *                            CRn=0, which corresponds to the AArch32 feature
+ *                            registers.
+ * @vcpu: the vCPU pointer
+ * @params: the system register access parameters.
+ *
+ * Our cp15 system register tables do not enumerate the AArch32 feature
+ * registers. Conveniently, our AArch64 table does, and the AArch32 system
+ * register encoding can be trivially remapped into the AArch64 for the feature
+ * registers: Append op0=3, leaving op1, CRn, CRm, and op2 the same.
+ *
+ * According to DDI0487G.b G7.3.1, paragraph "Behavior of VMSAv8-32 32-bit
+ * System registers with (coproc=0b1111, CRn==c0)", read accesses from this
+ * range are either UNKNOWN or RES0. Rerouting remains architectural as we
+ * treat undefined registers in this range as RAZ.
+ */
+static int kvm_emulate_cp15_id_reg(struct kvm_vcpu *vcpu,
+                                  struct sys_reg_params *params)
+{
+       int Rt = kvm_vcpu_sys_get_rt(vcpu);
+
+       /* Treat impossible writes to RO registers as UNDEFINED */
+       if (params->is_write) {
+               unhandled_cp_access(vcpu, params);
+               return 1;
+       }
+
+       params->Op0 = 3;
+
+       /*
+        * All registers where CRm > 3 are known to be UNKNOWN/RAZ from AArch32.
+        * Avoid conflicting with future expansion of AArch64 feature registers
+        * and simply treat them as RAZ here.
+        */
+       if (params->CRm > 3)
+               params->regval = 0;
+       else if (!emulate_sys_reg(vcpu, params))
+               return 1;
+
+       vcpu_set_reg(vcpu, Rt, params->regval);
+       return 1;
+}
+
 /**
  * kvm_handle_cp_32 -- handles a mrc/mcr trap on a guest CP14/CP15 access
  * @vcpu: The VCPU pointer
  * @run:  The kvm_run struct
  */
 static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
+                           struct sys_reg_params *params,
                            const struct sys_reg_desc *global,
                            size_t nr_global)
 {
-       struct sys_reg_params params;
-       u32 esr = kvm_vcpu_get_esr(vcpu);
        int Rt  = kvm_vcpu_sys_get_rt(vcpu);
 
-       params.CRm = (esr >> 1) & 0xf;
-       params.regval = vcpu_get_reg(vcpu, Rt);
-       params.is_write = ((esr & 1) == 0);
-       params.CRn = (esr >> 10) & 0xf;
-       params.Op0 = 0;
-       params.Op1 = (esr >> 14) & 0x7;
-       params.Op2 = (esr >> 17) & 0x7;
+       params->regval = vcpu_get_reg(vcpu, Rt);
 
-       if (emulate_cp(vcpu, ¶ms, global, nr_global)) {
-               if (!params.is_write)
-                       vcpu_set_reg(vcpu, Rt, params.regval);
+       if (emulate_cp(vcpu, params, global, nr_global)) {
+               if (!params->is_write)
+                       vcpu_set_reg(vcpu, Rt, params->regval);
                return 1;
        }
 
-       unhandled_cp_access(vcpu, ¶ms);
+       unhandled_cp_access(vcpu, params);
        return 1;
 }
 
 
 int kvm_handle_cp15_32(struct kvm_vcpu *vcpu)
 {
-       return kvm_handle_cp_32(vcpu, cp15_regs, ARRAY_SIZE(cp15_regs));
+       struct sys_reg_params params;
+
+       params = esr_cp1x_32_to_params(kvm_vcpu_get_esr(vcpu));
+
+       /*
+        * Certain AArch32 ID registers are handled by rerouting to the AArch64
+        * system register table. Registers in the ID range where CRm=0 are
+        * excluded from this scheme as they do not trivially map into AArch64
+        * system register encodings.
+        */
+       if (params.Op1 == 0 && params.CRn == 0 && params.CRm)
+               return kvm_emulate_cp15_id_reg(vcpu, ¶ms);
+
+       return kvm_handle_cp_32(vcpu, ¶ms, cp15_regs, ARRAY_SIZE(cp15_regs));
 }
 
 int kvm_handle_cp14_64(struct kvm_vcpu *vcpu)
 
 int kvm_handle_cp14_32(struct kvm_vcpu *vcpu)
 {
-       return kvm_handle_cp_32(vcpu, cp14_regs, ARRAY_SIZE(cp14_regs));
+       struct sys_reg_params params;
+
+       params = esr_cp1x_32_to_params(kvm_vcpu_get_esr(vcpu));
+
+       return kvm_handle_cp_32(vcpu, ¶ms, cp14_regs, ARRAY_SIZE(cp14_regs));
 }
 
 static bool is_imp_def_sys_reg(struct sys_reg_params *params)