[PATCH v8 6/6] KVM: arm64: Refactor writings for PMUVer/CSV2/CSV3

Jitindar Singh, Suraj surajjs at amazon.com
Wed May 17 15:00:20 PDT 2023


On Wed, 2023-05-03 at 17:16 +0000, Jing Zhang wrote:
> Refactor writings for ID_AA64PFR0_EL1.[CSV2|CSV3],
> ID_AA64DFR0_EL1.PMUVer and ID_DFR0_ELF.PerfMon based on utilities
> introduced by ID register descriptor array.
> 
> Signed-off-by: Jing Zhang <jingzhangos at google.com>
> ---
>  arch/arm64/include/asm/cpufeature.h |   1 +
>  arch/arm64/kernel/cpufeature.c      |   2 +-
>  arch/arm64/kvm/id_regs.c            | 361 ++++++++++++++++++--------
> --
>  3 files changed, 242 insertions(+), 122 deletions(-)
> 
> 

[ SNIP ]

>  
> +static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc
> *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * The default is to expose CSV2 == 1 if the HW isn't
> affected.
> +        * Although this is a per-CPU feature, we make it global
> because
> +        * asymmetric systems are just a nuisance.
> +        *
> +        * Userspace can override this as long as it doesn't promise
> +        * the impossible.
> +        */
> +       if (arm64_get_spectre_v2_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2);
> +               val |=
> FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV2), 1);
> +       }
> +       if (arm64_get_meltdown_state() == SPECTRE_UNAFFECTED) {
> +               val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3);
> +               val |=
> FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_CSV3), 1);
> +       }
> +
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU);
> +
> +       return val;
> +}
> +
>  static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
>                                const struct sys_reg_desc *rd,
>                                u64 val)
>  {
> -       struct kvm_arch *arch = &vcpu->kvm->arch;
> -       u64 sval = val;
>         u8 csv2, csv3;
> -       int ret = 0;
>  
>         /*
>          * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long
> as
> @@ -226,26 +338,30 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu
> *vcpu,
>         if (csv3 > 1 || (csv3 && arm64_get_meltdown_state() !=
> SPECTRE_UNAFFECTED))
>                 return -EINVAL;

Can't we remove the checking of csv[23] here as it will be checked by
arm64_check_features()?

i.e. in arm64_check_features() we will load the "limit" value from the
"reset" function (read_sanitised_id_aa64pfr0_el1()) which has csv[23]
set appropriately and limit it to a safe value basically performing the
same check as we are here.

>  
> -       mutex_lock(&arch->config_lock);
> -       /* 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) {
> -               ret = -EINVAL;
> -               goto out;
> -       }
> +       return set_id_reg(vcpu, rd, val);
> +}
>  
> -       /* Only allow userspace to change the idregs before VM
> running */
> -       if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &vcpu->kvm-
> >arch.flags)) {
> -               if (sval != read_id_reg(vcpu, rd))
> -                       ret = -EBUSY;
> -       } else {
> -               IDREG(vcpu->kvm, reg_to_encoding(rd)) = sval;
> -       }
> -out:
> -       mutex_unlock(&arch->config_lock);
> -       return ret;
> +static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> +                                         const struct sys_reg_desc
> *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /* Limit debug to ARMv8.0 */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer);
> +       val |=
> FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), 6);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer),
> +                         kvm_arm_pmu_get_pmuver_limit());
> +       /* Hide SPE from guests */
> +       val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer);
> +
> +       return val;
>  }
>  
>  static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> @@ -255,7 +371,6 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu
> *vcpu,
>         struct kvm_arch *arch = &vcpu->kvm->arch;
>         u8 pmuver, host_pmuver;
>         bool valid_pmu;
> -       u64 sval = val;
>         int ret = 0;
>  
>         host_pmuver = kvm_arm_pmu_get_pmuver_limit();
> @@ -277,40 +392,61 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu
> *vcpu,
>                 return -EINVAL;
>  
>         mutex_lock(&arch->config_lock);
> -       /* 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) {
> -               ret = -EINVAL;
> -               goto out;
> -       }
> -
>         /* Only allow userspace to change the idregs before VM
> running */
>         if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &vcpu->kvm-
> >arch.flags)) {
> -               if (sval != read_id_reg(vcpu, rd))
> +               if (val != read_id_reg(vcpu, rd))
>                         ret = -EBUSY;
> -       } else {
> -               if (valid_pmu) {
> -                       val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> -                       val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> -                       val |=
> FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, pmuver);
> -                       IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
> -
> -                       val = IDREG(vcpu->kvm, SYS_ID_DFR0_EL1);
> -                       val &= ~ID_DFR0_EL1_PerfMon_MASK;
> -                       val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> pmuver_to_perfmon(pmuver));
> -                       IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
> -               } else {
> -
>                        assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> &vcpu->kvm->arch.flags,
> -                                  pmuver ==
> ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> -               }
> +               goto out;
> +       }
> +
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PMUVer filed in @val. The PMUVer would

Nit s/filed/field

> be determined
> +                * by arch flags bit
> KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               pmuver = FIELD_GET(ID_AA64DFR0_EL1_PMUVer_MASK,
> +                                  IDREG(vcpu->kvm,
> SYS_ID_AA64DFR0_EL1));
> +               val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> +               val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> pmuver);
>         }
>  
> +       ret = arm64_check_features(vcpu, rd, val);
> +       if (ret)
> +               goto out;
> +
> +       IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
> +
> +       val = IDREG(vcpu->kvm, SYS_ID_DFR0_EL1);
> +       val &= ~ID_DFR0_EL1_PerfMon_MASK;
> +       val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> pmuver_to_perfmon(pmuver));
> +       IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
> +
> +       if (!valid_pmu)
> +               assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu-
> >kvm->arch.flags,
> +                          pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
> +
>  out:
>         mutex_unlock(&arch->config_lock);
>         return ret;
>  }
>  
> +static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
> +                                     const struct sys_reg_desc *rd)
> +{
> +       u64 val;
> +       u32 id = reg_to_encoding(rd);
> +
> +       val = read_sanitised_ftr_reg(id);
> +       /*
> +        * Initialise the default PMUver before there is a chance to
> +        * create an actual PMU.
> +        */
> +       val &= ~ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon);
> +       val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon),
> kvm_arm_pmu_get_pmuver_limit());
> +
> +       return val;
> +}
> +
>  static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
>                            const struct sys_reg_desc *rd,
>                            u64 val)
> @@ -318,7 +454,6 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
>         struct kvm_arch *arch = &vcpu->kvm->arch;
>         u8 perfmon, host_perfmon;
>         bool valid_pmu;
> -       u64 sval = val;
>         int ret = 0;
>  
>         host_perfmon =
> pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
> @@ -341,35 +476,39 @@ static int set_id_dfr0_el1(struct kvm_vcpu
> *vcpu,
>                 return -EINVAL;
>  
>         mutex_lock(&arch->config_lock);
> -       /* 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) {
> -               ret = -EINVAL;
> -               goto out;
> -       }
> -
>         /* Only allow userspace to change the idregs before VM
> running */
>         if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &vcpu->kvm-
> >arch.flags)) {
> -               if (sval != read_id_reg(vcpu, rd))
> +               if (val != read_id_reg(vcpu, rd))
>                         ret = -EBUSY;
> -       } else {
> -               if (valid_pmu) {
> -                       val = IDREG(vcpu->kvm, SYS_ID_DFR0_EL1);
> -                       val &= ~ID_DFR0_EL1_PerfMon_MASK;
> -                       val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK,
> perfmon);
> -                       IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
> -
> -                       val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> -                       val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> -                       val |=
> FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK, perfmon_to_pmuver(perfmon));
> -                       IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
> -               } else {
> -
>                        assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> &vcpu->kvm->arch.flags,
> -                                  perfmon ==
> ID_DFR0_EL1_PerfMon_IMPDEF);
> -               }
> +               goto out;
> +       }
> +
> +       if (!valid_pmu) {
> +               /*
> +                * Ignore the PerfMon filed in @val. The PerfMon

Nit s/filed/field

> would be determined
> +                * by arch flags bit
> KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU,
> +                */
> +               perfmon = FIELD_GET(ID_DFR0_EL1_PerfMon_MASK,
> +                                   IDREG(vcpu->kvm,
> SYS_ID_DFR0_EL1));
> +               val &= ~ID_DFR0_EL1_PerfMon_MASK;
> +               val |= FIELD_PREP(ID_DFR0_EL1_PerfMon_MASK, perfmon);
>         }
>  
> +       ret = arm64_check_features(vcpu, rd, val);
> +       if (ret)
> +               goto out;
> +
> +       IDREG(vcpu->kvm, SYS_ID_DFR0_EL1) = val;
> +
> +       val = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
> +       val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
> +       val |= FIELD_PREP(ID_AA64DFR0_EL1_PMUVer_MASK,
> perfmon_to_pmuver(perfmon));
> +       IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1) = val;
> +
> +       if (!valid_pmu)
> +               assign_bit(KVM_ARCH_FLAG_VCPU_HAS_IMP_DEF_PMU, &vcpu-
> >kvm->arch.flags,
> +                          perfmon == ID_DFR0_EL1_PerfMon_IMPDEF);
> +
>  out:
>         mutex_unlock(&arch->config_lock);
>         return ret;

Otherwise looks good!

Thanks,
Suraj


More information about the linux-arm-kernel mailing list