arm64: cpufeature: Allow early filtering of feature override
authorMarc Zyngier <maz@kernel.org>
Thu, 8 Apr 2021 13:10:08 +0000 (14:10 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Thu, 8 Apr 2021 17:45:16 +0000 (18:45 +0100)
Some CPUs are broken enough that some overrides need to be rejected
at the earliest opportunity. In some cases, that's right at cpu
feature override time.

Provide the necessary infrastructure to filter out overrides,
and to report such filtered out overrides to the core cpufeature code.

Acked-by: Will Deacon <will@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20210408131010.1109027-2-maz@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/cpufeature.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/idreg-override.c

index 61177bac49fa7fe7831c381598708a3946a868fe..338840c00e8ed72f339aa8fd513c2b2729ce14d7 100644 (file)
@@ -63,6 +63,23 @@ struct arm64_ftr_bits {
        s64             safe_val; /* safe value for FTR_EXACT features */
 };
 
+/*
+ * Describe the early feature override to the core override code:
+ *
+ * @val                        Values that are to be merged into the final
+ *                     sanitised value of the register. Only the bitfields
+ *                     set to 1 in @mask are valid
+ * @mask               Mask of the features that are overridden by @val
+ *
+ * A @mask field set to full-1 indicates that the corresponding field
+ * in @val is a valid override.
+ *
+ * A @mask field set to full-0 with the corresponding @val field set
+ * to full-0 denotes that this field has no override
+ *
+ * A @mask field set to full-0 with the corresponding @val field set
+ * to full-1 denotes thath this field has an invalid override.
+ */
 struct arm64_ftr_override {
        u64             val;
        u64             mask;
index 066030717a4c7f4c555659eeeb362871f25b7583..6de15deaa9126f7a5e5a030058e8b1ff5565ee87 100644 (file)
@@ -809,6 +809,12 @@ static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
                                        reg->name,
                                        ftrp->shift + ftrp->width - 1,
                                        ftrp->shift, str, tmp);
+               } else if ((ftr_mask & reg->override->val) == ftr_mask) {
+                       reg->override->val &= ~ftr_mask;
+                       pr_warn("%s[%d:%d]: impossible override, ignored\n",
+                               reg->name,
+                               ftrp->shift + ftrp->width - 1,
+                               ftrp->shift);
                }
 
                val = arm64_ftr_set_value(ftrp, val, ftr_new);
index 83f1c4b92095e9a6acf0c2eac454f965228bf040..be92fcd319a1b997d91af32de9a80f45a39c2da9 100644 (file)
@@ -25,6 +25,7 @@ struct ftr_set_desc {
        struct {
                char                    name[FTR_DESC_FIELD_LEN];
                u8                      shift;
+               bool                    (*filter)(u64 val);
        }                               fields[];
 };
 
@@ -124,6 +125,18 @@ static void __init match_options(const char *cmdline)
                        if (find_field(cmdline, regs[i], f, &v))
                                continue;
 
+                       /*
+                        * If an override gets filtered out, advertise
+                        * it by setting the value to 0xf, but
+                        * clearing the mask... Yes, this is fragile.
+                        */
+                       if (regs[i]->fields[f].filter &&
+                           !regs[i]->fields[f].filter(v)) {
+                               regs[i]->override->val  |= mask;
+                               regs[i]->override->mask &= ~mask;
+                               continue;
+                       }
+
                        regs[i]->override->val  &= ~mask;
                        regs[i]->override->val  |= (v << shift) & mask;
                        regs[i]->override->mask |= mask;