arm64: cpufeature: Relax AArch32 system checks if EL1 is 64-bit only
authorWill Deacon <will@kernel.org>
Tue, 21 Apr 2020 14:29:20 +0000 (15:29 +0100)
committerWill Deacon <will@kernel.org>
Tue, 28 Apr 2020 13:23:37 +0000 (14:23 +0100)
If AArch32 is not supported at EL1, the AArch32 feature register fields
no longer advertise support for some system features:

  * ISAR4.SMC
  * PFR1.{Virt_frac, Sec_frac, Virtualization, Security, ProgMod}

In which case, we don't need to emit "SANITY CHECK" failures for all of
them.

Add logic to relax the strictness of individual feature register fields
at runtime and use this for the fields above if 32-bit EL1 is not
supported.

Tested-by: Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20200421142922.18950-7-will@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/include/asm/cpufeature.h
arch/arm64/kernel/cpufeature.c

index afe08251ff95640818a89453db51d2127258a11c..f5c4672e498bf35a04c381e27c1fc697a0fe7fb7 100644 (file)
@@ -551,6 +551,13 @@ static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
                cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL0_SHIFT) == 0x1;
 }
 
+static inline bool id_aa64pfr0_32bit_el1(u64 pfr0)
+{
+       u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL1_SHIFT);
+
+       return val == ID_AA64PFR0_EL1_32BIT_64BIT;
+}
+
 static inline bool id_aa64pfr0_32bit_el0(u64 pfr0)
 {
        u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL0_SHIFT);
index 6892b24406765167bbd8a192d821ad2743a10656..7e0dbe2a2f2d93ceef58485f74307e222387026c 100644 (file)
@@ -715,6 +715,25 @@ static int check_update_ftr_reg(u32 sys_id, int cpu, u64 val, u64 boot)
        return 1;
 }
 
+static void relax_cpu_ftr_reg(u32 sys_id, int field)
+{
+       const struct arm64_ftr_bits *ftrp;
+       struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_id);
+
+       if (WARN_ON(!regp))
+               return;
+
+       for (ftrp = regp->ftr_bits; ftrp->width; ftrp++) {
+               if (ftrp->shift == field) {
+                       regp->strict_mask &= ~arm64_ftr_mask(ftrp);
+                       break;
+               }
+       }
+
+       /* Bogus field? */
+       WARN_ON(!ftrp->width);
+}
+
 static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
                                     struct cpuinfo_arm64 *boot)
 {
@@ -729,6 +748,19 @@ static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
        if (!id_aa64pfr0_32bit_el0(pfr0))
                return taint;
 
+       /*
+        * If we don't have AArch32 at EL1, then relax the strictness of
+        * EL1-dependent register fields to avoid spurious sanity check fails.
+        */
+       if (!id_aa64pfr0_32bit_el1(pfr0)) {
+               relax_cpu_ftr_reg(SYS_ID_ISAR4_EL1, ID_ISAR4_SMC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_VIRT_FRAC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_SEC_FRAC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_VIRTUALIZATION_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_SECURITY_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_PROGMOD_SHIFT);
+       }
+
        taint |= check_update_ftr_reg(SYS_ID_DFR0_EL1, cpu,
                                      info->reg_id_dfr0, boot->reg_id_dfr0);
        taint |= check_update_ftr_reg(SYS_ID_ISAR0_EL1, cpu,
@@ -773,7 +805,6 @@ static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
        return taint;
 }
 
-
 /*
  * Update system wide CPU feature registers with the values from a
  * non-boot CPU. Also performs SANITY checks to make sure that there