[PATCH v2 1/6] KVM: arm64: Move CPU ID feature registers emulation into a separate file

Reiji Watanabe reijiw at google.com
Thu Feb 23 17:01:44 PST 2023


Hi Jing,

On Sun, Feb 12, 2023 at 1:58 PM Jing Zhang <jingzhangos at google.com> wrote:
>
> Create a new file id_regs.c for CPU ID feature registers emulation code,
> which are moved from sys_regs.c and tweak sys_regs code accordingly.
>
> No functional change intended.
>
> Signed-off-by: Jing Zhang <jingzhangos at google.com>
> ---
>  arch/arm64/kvm/Makefile   |   2 +-
>  arch/arm64/kvm/id_regs.c  | 486 ++++++++++++++++++++++++++++++++++++++
>  arch/arm64/kvm/sys_regs.c | 463 ++----------------------------------
>  arch/arm64/kvm/sys_regs.h |  28 +++
>  4 files changed, 541 insertions(+), 438 deletions(-)
>  create mode 100644 arch/arm64/kvm/id_regs.c
>
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index c0c050e53157..a6a315fcd81e 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -13,7 +13,7 @@ obj-$(CONFIG_KVM) += hyp/
>  kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
>          inject_fault.o va_layout.o handle_exit.o \
>          guest.o debug.o reset.o sys_regs.o stacktrace.o \
> -        vgic-sys-reg-v3.o fpsimd.o pkvm.o \
> +        vgic-sys-reg-v3.o fpsimd.o pkvm.o id_regs.o \
>          arch_timer.o trng.o vmid.o emulate-nested.o nested.o \
>          vgic/vgic.o vgic/vgic-init.o \
>          vgic/vgic-irqfd.o vgic/vgic-v2.o \
> diff --git a/arch/arm64/kvm/id_regs.c b/arch/arm64/kvm/id_regs.c
> new file mode 100644
> index 000000000000..7f30d683de21
> --- /dev/null
> +++ b/arch/arm64/kvm/id_regs.c
> @@ -0,0 +1,486 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 - Google LLC
> + * Author: Jing Zhang <jingzhangos at google.com>
> + *
> + * Moved from arch/arm64/kvm/sys_regs.c
> + * Copyright (C) 2012,2013 - ARM Ltd
> + * Author: Marc Zyngier <marc.zyngier at arm.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/bsearch.h>
> +#include <linux/kvm_host.h>
> +#include <asm/kvm_emulate.h>
> +#include <asm/sysreg.h>
> +#include <asm/cpufeature.h>
> +#include <asm/kvm_nested.h>
> +
> +#include "sys_regs.h"
> +
> +static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> +{
> +       if (kvm_vcpu_has_pmu(vcpu))
> +               return vcpu->kvm->arch.dfr0_pmuver.imp;
> +
> +       return vcpu->kvm->arch.dfr0_pmuver.unimp;
> +}
> +
> +static u8 perfmon_to_pmuver(u8 perfmon)
> +{
> +       switch (perfmon) {
> +       case ID_DFR0_EL1_PerfMon_PMUv3:
> +               return ID_AA64DFR0_EL1_PMUVer_IMP;
> +       case ID_DFR0_EL1_PerfMon_IMPDEF:
> +               return ID_AA64DFR0_EL1_PMUVer_IMP_DEF;
> +       default:
> +               /* Anything ARMv8.1+ and NI have the same value. For now. */
> +               return perfmon;
> +       }
> +}
> +
> +static u8 pmuver_to_perfmon(u8 pmuver)
> +{
> +       switch (pmuver) {
> +       case ID_AA64DFR0_EL1_PMUVer_IMP:
> +               return ID_DFR0_EL1_PerfMon_PMUv3;
> +       case ID_AA64DFR0_EL1_PMUVer_IMP_DEF:
> +               return ID_DFR0_EL1_PerfMon_IMPDEF;
> +       default:
> +               /* Anything ARMv8.1+ and NI have the same value. For now. */
> +               return pmuver;
> +       }
> +}
> +
> +/* Read a sanitised cpufeature ID register by sys_reg_desc */
> +static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r)
> +{
> +       u32 id = reg_to_encoding(r);
> +       u64 val;
> +
> +       if (sysreg_visible_as_raz(vcpu, r))
> +               return 0;
> +
> +       val = read_sanitised_ftr_reg(id);
> +
> +       switch (id) {
> +       case SYS_ID_AA64PFR0_EL1:
> +               if (!vcpu_has_sve(vcpu))
> +                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2),
> +                                 (u64)vcpu->kvm->arch.pfr0_csv2);
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3),
> +                                 (u64)vcpu->kvm->arch.pfr0_csv3);
> +               if (kvm_vgic_global_state.type == VGIC_V3) {
> +                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> +                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> +               }
> +               break;
> +       case SYS_ID_AA64PFR1_EL1:
> +               if (!kvm_has_mte(vcpu->kvm))
> +                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
> +
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
> +               break;
> +       case SYS_ID_AA64ISAR1_EL1:
> +               if (!vcpu_has_ptrauth(vcpu))
> +                       val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_APA) |
> +                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_API) |
> +                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPA) |
> +                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPI));
> +               break;
> +       case SYS_ID_AA64ISAR2_EL1:
> +               if (!vcpu_has_ptrauth(vcpu))
> +                       val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) |
> +                                ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
> +               if (!cpus_have_final_cap(ARM64_HAS_WFXT))
> +                       val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> +               break;
> +       case SYS_ID_AA64DFR0_EL1:
> +               /* Limit debug to ARMv8.0 */
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> +               /* Set PMUver to the required version */
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> +                                 vcpu_pmuver(vcpu));
> +               /* Hide SPE from guests */
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> +               break;
> +       case SYS_ID_DFR0_EL1:
> +               val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> +                                 pmuver_to_perfmon(vcpu_pmuver(vcpu)));
> +               break;
> +       case SYS_ID_AA64MMFR2_EL1:
> +               val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
> +               break;
> +       case SYS_ID_MMFR4_EL1:
> +               val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
> +               break;
> +       }
> +
> +       return val;
> +}
> +
> +/* cpufeature ID register access trap handlers */
> +
> +static bool access_id_reg(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);
> +
> +       p->regval = read_id_reg(vcpu, r);
> +       if (vcpu_has_nv(vcpu))
> +               access_nested_id_reg(vcpu, p, r);
> +
> +       return true;
> +}
> +
> +/*
> + * cpufeature ID register user accessors
> + *
> + * For now, these registers are immutable for userspace, so no values
> + * are stored, and for set_id_reg() we don't allow the effective value
> + * to be changed.
> + */
> +static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> +                     u64 *val)
> +{
> +       *val = read_id_reg(vcpu, rd);
> +       return 0;
> +}
> +
> +static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> +                     u64 val)
> +{
> +       /* This is what we mean by invariant: you can't change it. */
> +       if (val != read_id_reg(vcpu, rd))
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> +                                 const struct sys_reg_desc *r)
> +{
> +       u32 id = reg_to_encoding(r);
> +
> +       switch (id) {
> +       case SYS_ID_AA64ZFR0_EL1:
> +               if (!vcpu_has_sve(vcpu))
> +                       return REG_RAZ;
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
> +                                      const struct sys_reg_desc *r)
> +{
> +       /*
> +        * AArch32 ID registers are UNKNOWN if AArch32 isn't implemented at any
> +        * EL. Promote to RAZ/WI in order to guarantee consistency between
> +        * systems.
> +        */
> +       if (!kvm_supports_32bit_el0())
> +               return REG_RAZ | REG_USER_WI;
> +
> +       return id_visibility(vcpu, r);
> +}
> +
> +static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                              const struct sys_reg_desc *rd,
> +                              u64 val)
> +{
> +       u8 csv2, csv3;
> +
> +       /*
> +        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> +        * it doesn't promise more than what is actually provided (the
> +        * guest could otherwise be covered in ectoplasmic residue).
> +        */
> +       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
> +       if (csv2 > 1 ||
> +           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> +               return -EINVAL;
> +
> +       /* Same thing for CSV3 */
> +       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
> +       if (csv3 > 1 ||
> +           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> +               return -EINVAL;
> +
> +       /* We can only differ with CSV[23], and anything else is an error */
> +       val ^= read_id_reg(vcpu, rd);
> +       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> +                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> +       if (val)
> +               return -EINVAL;
> +
> +       vcpu->kvm->arch.pfr0_csv2 = csv2;
> +       vcpu->kvm->arch.pfr0_csv3 = csv3;
> +
> +       return 0;
> +}
> +
> +static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> +                              const struct sys_reg_desc *rd,
> +                              u64 val)
> +{
> +       u8 pmuver, host_pmuver;
> +       bool valid_pmu;
> +
> +       host_pmuver = kvm_arm_pmu_get_pmuver_limit();
> +
> +       /*
> +        * Allow AA64DFR0_EL1.PMUver to be set from userspace as long
> +        * as it doesn't promise more than what the HW gives us. We
> +        * allow an IMPDEF PMU though, only if no PMU is supported
> +        * (KVM backward compatibility handling).
> +        */
> +       pmuver = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), val);
> +       if ((pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver > host_pmuver))
> +               return -EINVAL;
> +
> +       valid_pmu = (pmuver != 0 && pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> +
> +       /* Make sure view register and PMU support do match */
> +       if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> +               return -EINVAL;
> +
> +       /* We can only differ with PMUver, and anything else is an error */
> +       val ^= read_id_reg(vcpu, rd);
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> +       if (val)
> +               return -EINVAL;
> +
> +       if (valid_pmu)
> +               vcpu->kvm->arch.dfr0_pmuver.imp = pmuver;
> +       else
> +               vcpu->kvm->arch.dfr0_pmuver.unimp = pmuver;
> +
> +       return 0;
> +}
> +
> +static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                          const struct sys_reg_desc *rd,
> +                          u64 val)
> +{
> +       u8 perfmon, host_perfmon;
> +       bool valid_pmu;
> +
> +       host_perfmon = pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
> +
> +       /*
> +        * Allow DFR0_EL1.PerfMon to be set from userspace as long as
> +        * it doesn't promise more than what the HW gives us on the
> +        * AArch64 side (as everything is emulated with that), and
> +        * that this is a PMUv3.
> +        */
> +       perfmon = FIELD_GET(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), val);
> +       if ((perfmon != ID_DFR0_EL1_PerfMon_IMPDEF && perfmon > host_perfmon) ||
> +           (perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3))
> +               return -EINVAL;
> +
> +       valid_pmu = (perfmon != 0 && perfmon != ID_DFR0_EL1_PerfMon_IMPDEF);
> +
> +       /* Make sure view register and PMU support do match */
> +       if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> +               return -EINVAL;
> +
> +       /* We can only differ with PerfMon, and anything else is an error */
> +       val ^= read_id_reg(vcpu, rd);
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       if (val)
> +               return -EINVAL;
> +
> +       if (valid_pmu)
> +               vcpu->kvm->arch.dfr0_pmuver.imp = perfmon_to_pmuver(perfmon);
> +       else
> +               vcpu->kvm->arch.dfr0_pmuver.unimp = perfmon_to_pmuver(perfmon);
> +
> +       return 0;
> +}
> +
> +/* sys_reg_desc initialiser for known cpufeature ID registers */
> +#define ID_SANITISED(name) {                   \
> +       SYS_DESC(SYS_##name),                   \
> +       .access = access_id_reg,                \
> +       .get_user = get_id_reg,                 \
> +       .set_user = set_id_reg,                 \
> +       .visibility = id_visibility,            \
> +}
> +
> +/* sys_reg_desc initialiser for known cpufeature ID registers */
> +#define AA32_ID_SANITISED(name) {              \
> +       SYS_DESC(SYS_##name),                   \
> +       .access = access_id_reg,                \
> +       .get_user = get_id_reg,                 \
> +       .set_user = set_id_reg,                 \
> +       .visibility = aa32_id_visibility,       \
> +}
> +
> +/*
> + * sys_reg_desc initialiser for architecturally unallocated cpufeature ID
> + * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
> + * (1 <= crm < 8, 0 <= Op2 < 8).
> + */
> +#define ID_UNALLOCATED(crm, op2) {                     \
> +       Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),     \
> +       .access = access_id_reg,                        \
> +       .get_user = get_id_reg,                         \
> +       .set_user = set_id_reg,                         \
> +       .visibility = raz_visibility                    \
> +}
> +
> +/*
> + * sys_reg_desc initialiser for known ID registers that we hide from guests.
> + * For now, these are exposed just like unallocated ID regs: they appear
> + * RAZ for the guest.
> + */
> +#define ID_HIDDEN(name) {                      \
> +       SYS_DESC(SYS_##name),                   \
> +       .access = access_id_reg,                \
> +       .get_user = get_id_reg,                 \
> +       .set_user = set_id_reg,                 \
> +       .visibility = raz_visibility,           \
> +}
> +
> +static const struct sys_reg_desc id_reg_descs[] = {
> +       /*
> +        * ID regs: all ID_SANITISED() entries here must have corresponding
> +        * entries in arm64_ftr_regs[].
> +        */
> +
> +       /* AArch64 mappings of the AArch32 ID registers */
> +       /* CRm=1 */
> +       AA32_ID_SANITISED(ID_PFR0_EL1),
> +       AA32_ID_SANITISED(ID_PFR1_EL1),
> +       { SYS_DESC(SYS_ID_DFR0_EL1), .access = access_id_reg,
> +         .get_user = get_id_reg, .set_user = set_id_dfr0_el1,
> +         .visibility = aa32_id_visibility, },
> +       ID_HIDDEN(ID_AFR0_EL1),
> +       AA32_ID_SANITISED(ID_MMFR0_EL1),
> +       AA32_ID_SANITISED(ID_MMFR1_EL1),
> +       AA32_ID_SANITISED(ID_MMFR2_EL1),
> +       AA32_ID_SANITISED(ID_MMFR3_EL1),
> +
> +       /* CRm=2 */
> +       AA32_ID_SANITISED(ID_ISAR0_EL1),
> +       AA32_ID_SANITISED(ID_ISAR1_EL1),
> +       AA32_ID_SANITISED(ID_ISAR2_EL1),
> +       AA32_ID_SANITISED(ID_ISAR3_EL1),
> +       AA32_ID_SANITISED(ID_ISAR4_EL1),
> +       AA32_ID_SANITISED(ID_ISAR5_EL1),
> +       AA32_ID_SANITISED(ID_MMFR4_EL1),
> +       AA32_ID_SANITISED(ID_ISAR6_EL1),
> +
> +       /* CRm=3 */
> +       AA32_ID_SANITISED(MVFR0_EL1),
> +       AA32_ID_SANITISED(MVFR1_EL1),
> +       AA32_ID_SANITISED(MVFR2_EL1),
> +       ID_UNALLOCATED(3, 3),
> +       AA32_ID_SANITISED(ID_PFR2_EL1),
> +       ID_HIDDEN(ID_DFR1_EL1),
> +       AA32_ID_SANITISED(ID_MMFR5_EL1),
> +       ID_UNALLOCATED(3, 7),
> +
> +       /* AArch64 ID registers */
> +       /* CRm=4 */
> +       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> +         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> +       ID_SANITISED(ID_AA64PFR1_EL1),
> +       ID_UNALLOCATED(4, 2),
> +       ID_UNALLOCATED(4, 3),
> +       ID_SANITISED(ID_AA64ZFR0_EL1),
> +       ID_HIDDEN(ID_AA64SMFR0_EL1),
> +       ID_UNALLOCATED(4, 6),
> +       ID_UNALLOCATED(4, 7),
> +
> +       /* CRm=5 */
> +       { SYS_DESC(SYS_ID_AA64DFR0_EL1), .access = access_id_reg,
> +         .get_user = get_id_reg, .set_user = set_id_aa64dfr0_el1, },
> +       ID_SANITISED(ID_AA64DFR1_EL1),
> +       ID_UNALLOCATED(5, 2),
> +       ID_UNALLOCATED(5, 3),
> +       ID_HIDDEN(ID_AA64AFR0_EL1),
> +       ID_HIDDEN(ID_AA64AFR1_EL1),
> +       ID_UNALLOCATED(5, 6),
> +       ID_UNALLOCATED(5, 7),
> +
> +       /* CRm=6 */
> +       ID_SANITISED(ID_AA64ISAR0_EL1),
> +       ID_SANITISED(ID_AA64ISAR1_EL1),
> +       ID_SANITISED(ID_AA64ISAR2_EL1),
> +       ID_UNALLOCATED(6, 3),
> +       ID_UNALLOCATED(6, 4),
> +       ID_UNALLOCATED(6, 5),
> +       ID_UNALLOCATED(6, 6),
> +       ID_UNALLOCATED(6, 7),
> +
> +       /* CRm=7 */
> +       ID_SANITISED(ID_AA64MMFR0_EL1),
> +       ID_SANITISED(ID_AA64MMFR1_EL1),
> +       ID_SANITISED(ID_AA64MMFR2_EL1),
> +       ID_UNALLOCATED(7, 3),
> +       ID_UNALLOCATED(7, 4),
> +       ID_UNALLOCATED(7, 5),
> +       ID_UNALLOCATED(7, 6),
> +       ID_UNALLOCATED(7, 7),
> +};
> +
> +const struct sys_reg_desc *kvm_arm_find_id_reg(const struct sys_reg_params *params)
> +{

You might want to check if the param indicates the ID register,
before running the binary search for the ID register table.
(I have the same comment to kvm_arm_get_id_reg() and
kvm_arm_set_id_reg()).

> +       return find_reg(params, id_reg_descs, ARRAY_SIZE(id_reg_descs));
> +}
> +
> +void kvm_arm_reset_id_regs(struct kvm_vcpu *vcpu)
> +{
> +       unsigned long i;
> +
> +       for (i = 0; i < ARRAY_SIZE(id_reg_descs); i++)
> +               if (id_reg_descs[i].reset)
> +                       id_reg_descs[i].reset(vcpu, &id_reg_descs[i]);
> +}
> +
> +int kvm_arm_get_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       return kvm_sys_reg_get_user(vcpu, reg,
> +                                   id_reg_descs, ARRAY_SIZE(id_reg_descs));
> +}
> +
> +int kvm_arm_set_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       return kvm_sys_reg_set_user(vcpu, reg,
> +                                   id_reg_descs, ARRAY_SIZE(id_reg_descs));
> +}
> +
> +bool kvm_arm_check_idreg_table(void)
> +{
> +       return check_sysreg_table(id_reg_descs, ARRAY_SIZE(id_reg_descs), false);
> +}
> +
> +/* Assumed ordered tables, see kvm_sys_reg_table_init. */

I don't think we need this comment, as the code doesn't seem to assume that.

> +int kvm_arm_walk_id_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
> +{
> +       const struct sys_reg_desc *i2, *end2;
> +       unsigned int total = 0;
> +       int err;
> +
> +       i2 = id_reg_descs;
> +       end2 = id_reg_descs + ARRAY_SIZE(id_reg_descs);
> +
> +       while (i2 != end2) {
> +               err = walk_one_sys_reg(vcpu, i2++, &uind, &total);
> +               if (err)
> +                       return err;
> +       }
> +       return total;
> +}
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 53749d3a0996..3b23b7a67eb5 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -53,16 +53,6 @@ static bool read_from_write_only(struct kvm_vcpu *vcpu,
>         return false;
>  }
>
> -static bool write_to_read_only(struct kvm_vcpu *vcpu,
> -                              struct sys_reg_params *params,
> -                              const struct sys_reg_desc *r)
> -{
> -       WARN_ONCE(1, "Unexpected sys_reg write to read-only register\n");
> -       print_sys_reg_instr(params);
> -       kvm_inject_undefined(vcpu);
> -       return false;
> -}
> -
>  u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg)
>  {
>         u64 val = 0x8badf00d8badf00d;
> @@ -1153,163 +1143,6 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
>         return true;
>  }
>
> -static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
> -{
> -       if (kvm_vcpu_has_pmu(vcpu))
> -               return vcpu->kvm->arch.dfr0_pmuver.imp;
> -
> -       return vcpu->kvm->arch.dfr0_pmuver.unimp;
> -}
> -
> -static u8 perfmon_to_pmuver(u8 perfmon)
> -{
> -       switch (perfmon) {
> -       case ID_DFR0_EL1_PerfMon_PMUv3:
> -               return ID_AA64DFR0_EL1_PMUVer_IMP;
> -       case ID_DFR0_EL1_PerfMon_IMPDEF:
> -               return ID_AA64DFR0_EL1_PMUVer_IMP_DEF;
> -       default:
> -               /* Anything ARMv8.1+ and NI have the same value. For now. */
> -               return perfmon;
> -       }
> -}
> -
> -static u8 pmuver_to_perfmon(u8 pmuver)
> -{
> -       switch (pmuver) {
> -       case ID_AA64DFR0_EL1_PMUVer_IMP:
> -               return ID_DFR0_EL1_PerfMon_PMUv3;
> -       case ID_AA64DFR0_EL1_PMUVer_IMP_DEF:
> -               return ID_DFR0_EL1_PerfMon_IMPDEF;
> -       default:
> -               /* Anything ARMv8.1+ and NI have the same value. For now. */
> -               return pmuver;
> -       }
> -}
> -
> -/* Read a sanitised cpufeature ID register by sys_reg_desc */
> -static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r)
> -{
> -       u32 id = reg_to_encoding(r);
> -       u64 val;
> -
> -       if (sysreg_visible_as_raz(vcpu, r))
> -               return 0;
> -
> -       val = read_sanitised_ftr_reg(id);
> -
> -       switch (id) {
> -       case SYS_ID_AA64PFR0_EL1:
> -               if (!vcpu_has_sve(vcpu))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), (u64)vcpu->kvm->arch.pfr0_csv2);
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), (u64)vcpu->kvm->arch.pfr0_csv3);
> -               if (kvm_vgic_global_state.type == VGIC_V3) {
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC);
> -                       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_GIC), 1);
> -               }
> -               break;
> -       case SYS_ID_AA64PFR1_EL1:
> -               if (!kvm_has_mte(vcpu->kvm))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);
> -
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
> -               break;
> -       case SYS_ID_AA64ISAR1_EL1:
> -               if (!vcpu_has_ptrauth(vcpu))
> -                       val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_APA) |
> -                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_API) |
> -                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPA) |
> -                                ARM64_FEATURE_MASK(ID_AA64ISAR1_EL1_GPI));
> -               break;
> -       case SYS_ID_AA64ISAR2_EL1:
> -               if (!vcpu_has_ptrauth(vcpu))
> -                       val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) |
> -                                ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
> -               if (!cpus_have_final_cap(ARM64_HAS_WFXT))
> -                       val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
> -               break;
> -       case SYS_ID_AA64DFR0_EL1:
> -               /* Limit debug to ARMv8.0 */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> -               /* Set PMUver to the required version */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> -                                 vcpu_pmuver(vcpu));
> -               /* Hide SPE from guests */
> -               val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> -               break;
> -       case SYS_ID_DFR0_EL1:
> -               val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> -               val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> -                                 pmuver_to_perfmon(vcpu_pmuver(vcpu)));
> -               break;
> -       case SYS_ID_AA64MMFR2_EL1:
> -               val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
> -               break;
> -       case SYS_ID_MMFR4_EL1:
> -               val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
> -               break;
> -       }
> -
> -       return val;
> -}
> -
> -static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
> -                                 const struct sys_reg_desc *r)
> -{
> -       u32 id = reg_to_encoding(r);
> -
> -       switch (id) {
> -       case SYS_ID_AA64ZFR0_EL1:
> -               if (!vcpu_has_sve(vcpu))
> -                       return REG_RAZ;
> -               break;
> -       }
> -
> -       return 0;
> -}
> -
> -static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
> -                                      const struct sys_reg_desc *r)
> -{
> -       /*
> -        * AArch32 ID registers are UNKNOWN if AArch32 isn't implemented at any
> -        * EL. Promote to RAZ/WI in order to guarantee consistency between
> -        * systems.
> -        */
> -       if (!kvm_supports_32bit_el0())
> -               return REG_RAZ | REG_USER_WI;
> -
> -       return id_visibility(vcpu, r);
> -}
> -
> -static unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
> -                                  const struct sys_reg_desc *r)
> -{
> -       return REG_RAZ;
> -}
> -
> -/* cpufeature ID register access trap handlers */
> -
> -static bool access_id_reg(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);
> -
> -       p->regval = read_id_reg(vcpu, r);
> -       if (vcpu_has_nv(vcpu))
> -               access_nested_id_reg(vcpu, p, r);
> -
> -       return true;
> -}
> -
>  /* Visibility overrides for SVE-specific control registers */
>  static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>                                    const struct sys_reg_desc *rd)
> @@ -1320,144 +1153,6 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
>         return REG_HIDDEN;
>  }
>
> -static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              u64 val)
> -{
> -       u8 csv2, csv3;
> -
> -       /*
> -        * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as
> -        * it doesn't promise more than what is actually provided (the
> -        * guest could otherwise be covered in ectoplasmic residue).
> -        */
> -       csv2 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV2_SHIFT);
> -       if (csv2 > 1 ||
> -           (csv2 && arm64_get_spectre_v2_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* Same thing for CSV3 */
> -       csv3 = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR0_EL1_CSV3_SHIFT);
> -       if (csv3 > 1 ||
> -           (csv3 && arm64_get_meltdown_state() != SPECTRE_UNAFFECTED))
> -               return -EINVAL;
> -
> -       /* We can only differ with CSV[23], and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2) |
> -                ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3));
> -       if (val)
> -               return -EINVAL;
> -
> -       vcpu->kvm->arch.pfr0_csv2 = csv2;
> -       vcpu->kvm->arch.pfr0_csv3 = csv3;
> -
> -       return 0;
> -}
> -
> -static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> -                              const struct sys_reg_desc *rd,
> -                              u64 val)
> -{
> -       u8 pmuver, host_pmuver;
> -       bool valid_pmu;
> -
> -       host_pmuver = kvm_arm_pmu_get_pmuver_limit();
> -
> -       /*
> -        * Allow AA64DFR0_EL1.PMUver to be set from userspace as long
> -        * as it doesn't promise more than what the HW gives us. We
> -        * allow an IMPDEF PMU though, only if no PMU is supported
> -        * (KVM backward compatibility handling).
> -        */
> -       pmuver = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), val);
> -       if ((pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver > host_pmuver))
> -               return -EINVAL;
> -
> -       valid_pmu = (pmuver != 0 && pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> -
> -       /* Make sure view register and PMU support do match */
> -       if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> -               return -EINVAL;
> -
> -       /* We can only differ with PMUver, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> -       if (val)
> -               return -EINVAL;
> -
> -       if (valid_pmu)
> -               vcpu->kvm->arch.dfr0_pmuver.imp = pmuver;
> -       else
> -               vcpu->kvm->arch.dfr0_pmuver.unimp = pmuver;
> -
> -       return 0;
> -}
> -
> -static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
> -                          const struct sys_reg_desc *rd,
> -                          u64 val)
> -{
> -       u8 perfmon, host_perfmon;
> -       bool valid_pmu;
> -
> -       host_perfmon = pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
> -
> -       /*
> -        * Allow DFR0_EL1.PerfMon to be set from userspace as long as
> -        * it doesn't promise more than what the HW gives us on the
> -        * AArch64 side (as everything is emulated with that), and
> -        * that this is a PMUv3.
> -        */
> -       perfmon = FIELD_GET(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), val);
> -       if ((perfmon != ID_DFR0_EL1_PerfMon_IMPDEF && perfmon > host_perfmon) ||
> -           (perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3))
> -               return -EINVAL;
> -
> -       valid_pmu = (perfmon != 0 && perfmon != ID_DFR0_EL1_PerfMon_IMPDEF);
> -
> -       /* Make sure view register and PMU support do match */
> -       if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
> -               return -EINVAL;
> -
> -       /* We can only differ with PerfMon, and anything else is an error */
> -       val ^= read_id_reg(vcpu, rd);
> -       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> -       if (val)
> -               return -EINVAL;
> -
> -       if (valid_pmu)
> -               vcpu->kvm->arch.dfr0_pmuver.imp = perfmon_to_pmuver(perfmon);
> -       else
> -               vcpu->kvm->arch.dfr0_pmuver.unimp = perfmon_to_pmuver(perfmon);
> -
> -       return 0;
> -}
> -
> -/*
> - * cpufeature ID register user accessors
> - *
> - * For now, these registers are immutable for userspace, so no values
> - * are stored, and for set_id_reg() we don't allow the effective value
> - * to be changed.
> - */
> -static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> -                     u64 *val)
> -{
> -       *val = read_id_reg(vcpu, rd);
> -       return 0;
> -}
> -
> -static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
> -                     u64 val)
> -{
> -       /* This is what we mean by invariant: you can't change it. */
> -       if (val != read_id_reg(vcpu, rd))
> -               return -EINVAL;
> -
> -       return 0;
> -}
> -
>  static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
>                        u64 *val)
>  {
> @@ -1642,50 +1337,6 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
>         .visibility = elx2_visibility,          \
>  }
>
> -/* sys_reg_desc initialiser for known cpufeature ID registers */
> -#define ID_SANITISED(name) {                   \
> -       SYS_DESC(SYS_##name),                   \
> -       .access = access_id_reg,                \
> -       .get_user = get_id_reg,                 \
> -       .set_user = set_id_reg,                 \
> -       .visibility = id_visibility,            \
> -}
> -
> -/* sys_reg_desc initialiser for known cpufeature ID registers */
> -#define AA32_ID_SANITISED(name) {              \
> -       SYS_DESC(SYS_##name),                   \
> -       .access = access_id_reg,                \
> -       .get_user = get_id_reg,                 \
> -       .set_user = set_id_reg,                 \
> -       .visibility = aa32_id_visibility,       \
> -}
> -
> -/*
> - * sys_reg_desc initialiser for architecturally unallocated cpufeature ID
> - * register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
> - * (1 <= crm < 8, 0 <= Op2 < 8).
> - */
> -#define ID_UNALLOCATED(crm, op2) {                     \
> -       Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2),     \
> -       .access = access_id_reg,                        \
> -       .get_user = get_id_reg,                         \
> -       .set_user = set_id_reg,                         \
> -       .visibility = raz_visibility                    \
> -}
> -
> -/*
> - * sys_reg_desc initialiser for known ID registers that we hide from guests.
> - * For now, these are exposed just like unallocated ID regs: they appear
> - * RAZ for the guest.
> - */
> -#define ID_HIDDEN(name) {                      \
> -       SYS_DESC(SYS_##name),                   \
> -       .access = access_id_reg,                \
> -       .get_user = get_id_reg,                 \
> -       .set_user = set_id_reg,                 \
> -       .visibility = raz_visibility,           \
> -}
> -
>  static bool access_sp_el1(struct kvm_vcpu *vcpu,
>                           struct sys_reg_params *p,
>                           const struct sys_reg_desc *r)
> @@ -1776,87 +1427,6 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>
>         { SYS_DESC(SYS_MPIDR_EL1), NULL, reset_mpidr, MPIDR_EL1 },
>
> -       /*
> -        * ID regs: all ID_SANITISED() entries here must have corresponding
> -        * entries in arm64_ftr_regs[].
> -        */
> -
> -       /* AArch64 mappings of the AArch32 ID registers */
> -       /* CRm=1 */
> -       AA32_ID_SANITISED(ID_PFR0_EL1),
> -       AA32_ID_SANITISED(ID_PFR1_EL1),
> -       { SYS_DESC(SYS_ID_DFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_dfr0_el1,
> -         .visibility = aa32_id_visibility, },
> -       ID_HIDDEN(ID_AFR0_EL1),
> -       AA32_ID_SANITISED(ID_MMFR0_EL1),
> -       AA32_ID_SANITISED(ID_MMFR1_EL1),
> -       AA32_ID_SANITISED(ID_MMFR2_EL1),
> -       AA32_ID_SANITISED(ID_MMFR3_EL1),
> -
> -       /* CRm=2 */
> -       AA32_ID_SANITISED(ID_ISAR0_EL1),
> -       AA32_ID_SANITISED(ID_ISAR1_EL1),
> -       AA32_ID_SANITISED(ID_ISAR2_EL1),
> -       AA32_ID_SANITISED(ID_ISAR3_EL1),
> -       AA32_ID_SANITISED(ID_ISAR4_EL1),
> -       AA32_ID_SANITISED(ID_ISAR5_EL1),
> -       AA32_ID_SANITISED(ID_MMFR4_EL1),
> -       AA32_ID_SANITISED(ID_ISAR6_EL1),
> -
> -       /* CRm=3 */
> -       AA32_ID_SANITISED(MVFR0_EL1),
> -       AA32_ID_SANITISED(MVFR1_EL1),
> -       AA32_ID_SANITISED(MVFR2_EL1),
> -       ID_UNALLOCATED(3,3),
> -       AA32_ID_SANITISED(ID_PFR2_EL1),
> -       ID_HIDDEN(ID_DFR1_EL1),
> -       AA32_ID_SANITISED(ID_MMFR5_EL1),
> -       ID_UNALLOCATED(3,7),
> -
> -       /* AArch64 ID registers */
> -       /* CRm=4 */
> -       { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },
> -       ID_SANITISED(ID_AA64PFR1_EL1),
> -       ID_UNALLOCATED(4,2),
> -       ID_UNALLOCATED(4,3),
> -       ID_SANITISED(ID_AA64ZFR0_EL1),
> -       ID_HIDDEN(ID_AA64SMFR0_EL1),
> -       ID_UNALLOCATED(4,6),
> -       ID_UNALLOCATED(4,7),
> -
> -       /* CRm=5 */
> -       { SYS_DESC(SYS_ID_AA64DFR0_EL1), .access = access_id_reg,
> -         .get_user = get_id_reg, .set_user = set_id_aa64dfr0_el1, },
> -       ID_SANITISED(ID_AA64DFR1_EL1),
> -       ID_UNALLOCATED(5,2),
> -       ID_UNALLOCATED(5,3),
> -       ID_HIDDEN(ID_AA64AFR0_EL1),
> -       ID_HIDDEN(ID_AA64AFR1_EL1),
> -       ID_UNALLOCATED(5,6),
> -       ID_UNALLOCATED(5,7),
> -
> -       /* CRm=6 */
> -       ID_SANITISED(ID_AA64ISAR0_EL1),
> -       ID_SANITISED(ID_AA64ISAR1_EL1),
> -       ID_SANITISED(ID_AA64ISAR2_EL1),
> -       ID_UNALLOCATED(6,3),
> -       ID_UNALLOCATED(6,4),
> -       ID_UNALLOCATED(6,5),
> -       ID_UNALLOCATED(6,6),
> -       ID_UNALLOCATED(6,7),
> -
> -       /* CRm=7 */
> -       ID_SANITISED(ID_AA64MMFR0_EL1),
> -       ID_SANITISED(ID_AA64MMFR1_EL1),
> -       ID_SANITISED(ID_AA64MMFR2_EL1),
> -       ID_UNALLOCATED(7,3),
> -       ID_UNALLOCATED(7,4),
> -       ID_UNALLOCATED(7,5),
> -       ID_UNALLOCATED(7,6),
> -       ID_UNALLOCATED(7,7),
> -
>         { SYS_DESC(SYS_SCTLR_EL1), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 },
>         { SYS_DESC(SYS_ACTLR_EL1), access_actlr, reset_actlr, ACTLR_EL1 },
>         { SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 },
> @@ -2531,8 +2101,8 @@ static const struct sys_reg_desc cp15_64_regs[] = {
>         { SYS_DESC(SYS_AARCH32_CNTP_CVAL),    access_arch_timer },
>  };
>
> -static bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
> -                              bool is_32)
> +bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
> +                       bool is_32)
>  {
>         unsigned int i;
>
> @@ -2883,7 +2453,9 @@ static bool emulate_sys_reg(struct kvm_vcpu *vcpu,
>  {
>         const struct sys_reg_desc *r;
>
> -       r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
> +       r = kvm_arm_find_id_reg(params);

It appears that this is the only one code that enforces id_regs.c
to expose sys_reg_desc of ID registers. Although I'm not sure your
intention, you might want to create emulate_id_reg(), instead of
exposing that unnecessarily ?

Thank you,
Reiji

> +       if (!r)
> +               r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
>
>         if (likely(r)) {
>                 perform_access(vcpu, params, r);
> @@ -2912,6 +2484,8 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
>  {
>         unsigned long i;
>
> +       kvm_arm_reset_id_regs(vcpu);
> +
>         for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++)
>                 if (sys_reg_descs[i].reset)
>                         sys_reg_descs[i].reset(vcpu, &sys_reg_descs[i]);
> @@ -3160,6 +2734,10 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
>         if (err != -ENOENT)
>                 return err;
>
> +       err = kvm_arm_get_id_reg(vcpu, reg);
> +       if (err != -ENOENT)
> +               return err;
> +
>         return kvm_sys_reg_get_user(vcpu, reg,
>                                     sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
>  }
> @@ -3204,6 +2782,10 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
>         if (err != -ENOENT)
>                 return err;
>
> +       err = kvm_arm_set_id_reg(vcpu, reg);
> +       if (err != -ENOENT)
> +               return err;
> +
>         return kvm_sys_reg_set_user(vcpu, reg,
>                                     sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
>  }
> @@ -3250,10 +2832,10 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
>         return true;
>  }
>
> -static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
> -                           const struct sys_reg_desc *rd,
> -                           u64 __user **uind,
> -                           unsigned int *total)
> +int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
> +                    const struct sys_reg_desc *rd,
> +                    u64 __user **uind,
> +                    unsigned int *total)
>  {
>         /*
>          * Ignore registers we trap but don't save,
> @@ -3294,6 +2876,7 @@ unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
>  {
>         return ARRAY_SIZE(invariant_sys_regs)
>                 + num_demux_regs()
> +               + kvm_arm_walk_id_regs(vcpu, (u64 __user *)NULL)
>                 + walk_sys_regs(vcpu, (u64 __user *)NULL);
>  }
>
> @@ -3309,6 +2892,11 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
>                 uindices++;
>         }
>
> +       err = kvm_arm_walk_id_regs(vcpu, uindices);
> +       if (err < 0)
> +               return err;
> +       uindices += err;
> +
>         err = walk_sys_regs(vcpu, uindices);
>         if (err < 0)
>                 return err;
> @@ -3323,6 +2911,7 @@ int __init kvm_sys_reg_table_init(void)
>         unsigned int i;
>
>         /* Make sure tables are unique and in order. */
> +       valid &= kvm_arm_check_idreg_table();
>         valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false);
>         valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true);
>         valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
> diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
> index 6b11f2cc7146..96a52936d130 100644
> --- a/arch/arm64/kvm/sys_regs.h
> +++ b/arch/arm64/kvm/sys_regs.h
> @@ -210,6 +210,22 @@ find_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[],
>         return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match_sys_reg);
>  }
>
> +static inline unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc *r)
> +{
> +       return REG_RAZ;
> +}
> +
> +static inline bool write_to_read_only(struct kvm_vcpu *vcpu,
> +                                     struct sys_reg_params *params,
> +                                     const struct sys_reg_desc *r)
> +{
> +       WARN_ONCE(1, "Unexpected sys_reg write to read-only register\n");
> +       print_sys_reg_instr(params);
> +       kvm_inject_undefined(vcpu);
> +       return false;
> +}
> +
>  const struct sys_reg_desc *get_reg_by_id(u64 id,
>                                          const struct sys_reg_desc table[],
>                                          unsigned int num);
> @@ -220,6 +236,18 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
>                          const struct sys_reg_desc table[], unsigned int num);
>  int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
>                          const struct sys_reg_desc table[], unsigned int num);
> +bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
> +                       bool is_32);
> +int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
> +                    const struct sys_reg_desc *rd,
> +                    u64 __user **uind,
> +                    unsigned int *total);
> +const struct sys_reg_desc *kvm_arm_find_id_reg(const struct sys_reg_params *params);
> +void kvm_arm_reset_id_regs(struct kvm_vcpu *vcpu);
> +int kvm_arm_get_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +int kvm_arm_set_id_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> +bool kvm_arm_check_idreg_table(void);
> +int kvm_arm_walk_id_regs(struct kvm_vcpu *vcpu, u64 __user *uind);
>
>  #define AA32(_x)       .aarch32_map = AA32_##_x
>  #define Op0(_x)        .Op0 = _x
> --
> 2.39.1.581.gbfd45094c4-goog
>



More information about the linux-arm-kernel mailing list