arm64: Add an override for ID_AA64SMFR0_EL1.FA64
[platform/kernel/linux-starfive.git] / arch / arm64 / kernel / idreg-override.c
index 8a2ceb5..aa2a53d 100644 (file)
 #define FTR_ALIAS_NAME_LEN     30
 #define FTR_ALIAS_OPTION_LEN   116
 
+static u64 __boot_status __initdata;
+
 struct ftr_set_desc {
        char                            name[FTR_DESC_NAME_LEN];
        struct arm64_ftr_override       *override;
        struct {
                char                    name[FTR_DESC_FIELD_LEN];
                u8                      shift;
+               u8                      width;
                bool                    (*filter)(u64 val);
        }                               fields[];
 };
 
+#define FIELD(n, s, f) { .name = n, .shift = s, .width = 4, .filter = f }
+
 static bool __init mmfr1_vh_filter(u64 val)
 {
        /*
@@ -37,24 +42,65 @@ static bool __init mmfr1_vh_filter(u64 val)
         * the user was trying to force nVHE on us, proceed with
         * attitude adjustment.
         */
-       return !(is_kernel_in_hyp_mode() && val == 0);
+       return !(__boot_status == (BOOT_CPU_FLAG_E2H | BOOT_CPU_MODE_EL2) &&
+                val == 0);
 }
 
 static const struct ftr_set_desc mmfr1 __initconst = {
        .name           = "id_aa64mmfr1",
        .override       = &id_aa64mmfr1_override,
        .fields         = {
-               { "vh", ID_AA64MMFR1_VHE_SHIFT, mmfr1_vh_filter },
+               FIELD("vh", ID_AA64MMFR1_VHE_SHIFT, mmfr1_vh_filter),
                {}
        },
 };
 
+static bool __init pfr0_sve_filter(u64 val)
+{
+       /*
+        * Disabling SVE also means disabling all the features that
+        * are associated with it. The easiest way to do it is just to
+        * override id_aa64zfr0_el1 to be 0.
+        */
+       if (!val) {
+               id_aa64zfr0_override.val = 0;
+               id_aa64zfr0_override.mask = GENMASK(63, 0);
+       }
+
+       return true;
+}
+
+static const struct ftr_set_desc pfr0 __initconst = {
+       .name           = "id_aa64pfr0",
+       .override       = &id_aa64pfr0_override,
+       .fields         = {
+               FIELD("sve", ID_AA64PFR0_SVE_SHIFT, pfr0_sve_filter),
+               {}
+       },
+};
+
+static bool __init pfr1_sme_filter(u64 val)
+{
+       /*
+        * Similarly to SVE, disabling SME also means disabling all
+        * the features that are associated with it. Just set
+        * id_aa64smfr0_el1 to 0 and don't look back.
+        */
+       if (!val) {
+               id_aa64smfr0_override.val = 0;
+               id_aa64smfr0_override.mask = GENMASK(63, 0);
+       }
+
+       return true;
+}
+
 static const struct ftr_set_desc pfr1 __initconst = {
        .name           = "id_aa64pfr1",
        .override       = &id_aa64pfr1_override,
        .fields         = {
-               { "bt", ID_AA64PFR1_BT_SHIFT },
-               { "mte", ID_AA64PFR1_MTE_SHIFT},
+               FIELD("bt", ID_AA64PFR1_BT_SHIFT, NULL),
+               FIELD("mte", ID_AA64PFR1_MTE_SHIFT, NULL),
+               FIELD("sme", ID_AA64PFR1_SME_SHIFT, pfr1_sme_filter),
                {}
        },
 };
@@ -63,10 +109,10 @@ static const struct ftr_set_desc isar1 __initconst = {
        .name           = "id_aa64isar1",
        .override       = &id_aa64isar1_override,
        .fields         = {
-               { "gpi", ID_AA64ISAR1_GPI_SHIFT },
-               { "gpa", ID_AA64ISAR1_GPA_SHIFT },
-               { "api", ID_AA64ISAR1_API_SHIFT },
-               { "apa", ID_AA64ISAR1_APA_SHIFT },
+               FIELD("gpi", ID_AA64ISAR1_GPI_SHIFT, NULL),
+               FIELD("gpa", ID_AA64ISAR1_GPA_SHIFT, NULL),
+               FIELD("api", ID_AA64ISAR1_API_SHIFT, NULL),
+               FIELD("apa", ID_AA64ISAR1_APA_SHIFT, NULL),
                {}
        },
 };
@@ -75,8 +121,18 @@ static const struct ftr_set_desc isar2 __initconst = {
        .name           = "id_aa64isar2",
        .override       = &id_aa64isar2_override,
        .fields         = {
-               { "gpa3", ID_AA64ISAR2_GPA3_SHIFT },
-               { "apa3", ID_AA64ISAR2_APA3_SHIFT },
+               FIELD("gpa3", ID_AA64ISAR2_GPA3_SHIFT, NULL),
+               FIELD("apa3", ID_AA64ISAR2_APA3_SHIFT, NULL),
+               {}
+       },
+};
+
+static const struct ftr_set_desc smfr0 __initconst = {
+       .name           = "id_aa64smfr0",
+       .override       = &id_aa64smfr0_override,
+       .fields         = {
+               /* FA64 is a one bit field... :-/ */
+               { "fa64", ID_AA64SMFR0_FA64_SHIFT, 1, },
                {}
        },
 };
@@ -89,16 +145,18 @@ static const struct ftr_set_desc kaslr __initconst = {
        .override       = &kaslr_feature_override,
 #endif
        .fields         = {
-               { "disabled", 0 },
+               FIELD("disabled", 0, NULL),
                {}
        },
 };
 
 static const struct ftr_set_desc * const regs[] __initconst = {
        &mmfr1,
+       &pfr0,
        &pfr1,
        &isar1,
        &isar2,
+       &smfr0,
        &kaslr,
 };
 
@@ -108,6 +166,8 @@ static const struct {
 } aliases[] __initconst = {
        { "kvm-arm.mode=nvhe",          "id_aa64mmfr1.vh=0" },
        { "kvm-arm.mode=protected",     "id_aa64mmfr1.vh=0" },
+       { "arm64.nosve",                "id_aa64pfr0.sve=0 id_aa64pfr1.sme=0" },
+       { "arm64.nosme",                "id_aa64pfr1.sme=0" },
        { "arm64.nobti",                "id_aa64pfr1.bt=0" },
        { "arm64.nopauth",
          "id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
@@ -144,7 +204,8 @@ static void __init match_options(const char *cmdline)
 
                for (f = 0; strlen(regs[i]->fields[f].name); f++) {
                        u64 shift = regs[i]->fields[f].shift;
-                       u64 mask = 0xfUL << shift;
+                       u64 width = regs[i]->fields[f].width ?: 4;
+                       u64 mask = GENMASK_ULL(shift + width - 1, shift);
                        u64 v;
 
                        if (find_field(cmdline, regs[i], f, &v))
@@ -152,7 +213,7 @@ static void __init match_options(const char *cmdline)
 
                        /*
                         * If an override gets filtered out, advertise
-                        * it by setting the value to 0xf, but
+                        * it by setting the value to the all-ones while
                         * clearing the mask... Yes, this is fragile.
                         */
                        if (regs[i]->fields[f].filter &&
@@ -201,16 +262,11 @@ static __init void __parse_cmdline(const char *cmdline, bool parse_aliases)
        } while (1);
 }
 
-static __init const u8 *get_bootargs_cmdline(void)
+static __init const u8 *get_bootargs_cmdline(const void *fdt)
 {
        const u8 *prop;
-       void *fdt;
        int node;
 
-       fdt = get_early_fdt_ptr();
-       if (!fdt)
-               return NULL;
-
        node = fdt_path_offset(fdt, "/chosen");
        if (node < 0)
                return NULL;
@@ -222,9 +278,9 @@ static __init const u8 *get_bootargs_cmdline(void)
        return strlen(prop) ? prop : NULL;
 }
 
-static __init void parse_cmdline(void)
+static __init void parse_cmdline(const void *fdt)
 {
-       const u8 *prop = get_bootargs_cmdline();
+       const u8 *prop = get_bootargs_cmdline(fdt);
 
        if (IS_ENABLED(CONFIG_CMDLINE_FORCE) || !prop)
                __parse_cmdline(CONFIG_CMDLINE, true);
@@ -234,9 +290,9 @@ static __init void parse_cmdline(void)
 }
 
 /* Keep checkers quiet */
-void init_feature_override(void);
+void init_feature_override(const void *fdt, u64 boot_status);
 
-asmlinkage void __init init_feature_override(void)
+asmlinkage void __init init_feature_override(const void *fdt, u64 boot_status)
 {
        int i;
 
@@ -247,7 +303,9 @@ asmlinkage void __init init_feature_override(void)
                }
        }
 
-       parse_cmdline();
+       __boot_status = boot_status;
+
+       parse_cmdline(fdt);
 
        for (i = 0; i < ARRAY_SIZE(regs); i++) {
                if (regs[i]->override)