Message ID | 20210118094533.2874082-13-maz@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | arm64: Early CPU feature override, and applications to VHE, BTI and PAuth | expand |
On Mon, Jan 18, 2021 at 09:45:24AM +0000, Marc Zyngier wrote: > In order to be able to override CPU features at boot time, > let's add a command line parser that matches options of the > form "cpureg.feature=value", and store the corresponding > value into the override val/mask pair. > > No features are currently defined, so no expected change in > functionality. > > Signed-off-by: Marc Zyngier <maz@kernel.org> Acked-by: David Brazdil <dbrazdil@google.com> > --- > arch/arm64/kernel/Makefile | 2 +- > arch/arm64/kernel/head.S | 1 + > arch/arm64/kernel/idreg-override.c | 119 +++++++++++++++++++++++++++++ > 3 files changed, 121 insertions(+), 1 deletion(-) > create mode 100644 arch/arm64/kernel/idreg-override.c > > diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile > index 86364ab6f13f..2262f0392857 100644 > --- a/arch/arm64/kernel/Makefile > +++ b/arch/arm64/kernel/Makefile > @@ -17,7 +17,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ > return_address.o cpuinfo.o cpu_errata.o \ > cpufeature.o alternative.o cacheinfo.o \ > smp.o smp_spin_table.o topology.o smccc-call.o \ > - syscall.o proton-pack.o > + syscall.o proton-pack.o idreg-override.o > > targets += efi-entry.o > > diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S > index d74e5f84042e..b3c4dd04f74b 100644 > --- a/arch/arm64/kernel/head.S > +++ b/arch/arm64/kernel/head.S > @@ -435,6 +435,7 @@ SYM_FUNC_START_LOCAL(__primary_switched) > > mov x0, x21 // pass FDT address in x0 > bl early_fdt_map // Try mapping the FDT early > + bl init_shadow_regs > bl switch_to_vhe > #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) > bl kasan_early_init > diff --git a/arch/arm64/kernel/idreg-override.c b/arch/arm64/kernel/idreg-override.c > new file mode 100644 > index 000000000000..392f93b67103 > --- /dev/null > +++ b/arch/arm64/kernel/idreg-override.c > @@ -0,0 +1,119 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Early cpufeature override framework > + * > + * Copyright (C) 2020 Google LLC > + * Author: Marc Zyngier <maz@kernel.org> > + */ > + > +#include <linux/kernel.h> > +#include <linux/libfdt.h> > + > +#include <asm/cacheflush.h> > +#include <asm/setup.h> > + > +struct reg_desc { > + const char * const name; > + u64 * const val; > + u64 * const mask; > + struct { > + const char * const name; > + u8 shift; nit: There's an extra space before `shift`. > + } fields[]; > +}; > + > +static const struct reg_desc * const regs[] __initdata = { > +}; > + > +static int __init find_field(const char *cmdline, const struct reg_desc *reg, > + int f, u64 *v) > +{ > + char buf[256], *str; > + size_t len; > + > + snprintf(buf, ARRAY_SIZE(buf), "%s.%s=", reg->name, reg->fields[f].name); > + > + str = strstr(cmdline, buf); > + if (!(str == cmdline || (str > cmdline && *(str - 1) == ' '))) > + return -1; > + > + str += strlen(buf); > + len = strcspn(str, " "); > + len = min(len, ARRAY_SIZE(buf) - 1); > + strncpy(buf, str, len); > + buf[len] = 0; > + > + return kstrtou64(buf, 0, v); > +} > + > +static void __init match_options(const char *cmdline) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(regs); i++) { > + int f; > + > + if (!regs[i]->val || !regs[i]->mask) > + continue; > + > + for (f = 0; regs[i]->fields[f].name; f++) { > + u64 v; > + > + if (find_field(cmdline, regs[i], f, &v)) > + continue; > + > + *regs[i]->val |= (v & 0xf) << regs[i]->fields[f].shift; > + *regs[i]->mask |= 0xfUL << regs[i]->fields[f].shift; > + } > + } > +} > + > +static __init void parse_cmdline(void) > +{ > + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { > + const u8 *prop; > + void *fdt; > + int node; > + > + fdt = get_early_fdt_ptr(); > + if (!fdt) > + goto out; > + > + node = fdt_path_offset(fdt, "/chosen"); > + if (node < 0) > + goto out; > + > + prop = fdt_getprop(fdt, node, "bootargs", NULL); > + if (!prop) > + goto out; > + > + match_options(prop); > + > + if (!IS_ENABLED(CONFIG_CMDLINE_EXTEND)) > + return; > + } > + > +out: > + match_options(CONFIG_CMDLINE); > +} > + > +void __init init_shadow_regs(void) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(regs); i++) { > + if (regs[i]->val) > + *regs[i]->val = 0; > + if (regs[i]->mask) > + *regs[i]->mask = 0; > + } > + > + parse_cmdline(); > + > + for (i = 0; i < ARRAY_SIZE(regs); i++) { > + if (regs[i]->val) > + __flush_dcache_area(regs[i]->val, sizeof(*regs[i]->val)); > + if (regs[i]->mask) > + __flush_dcache_area(regs[i]->mask, sizeof(*regs[i]->mask)); > + } > +} > -- > 2.29.2 >
On Mon, Jan 18, 2021 at 09:45:24AM +0000, Marc Zyngier wrote: > diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S > index d74e5f84042e..b3c4dd04f74b 100644 > --- a/arch/arm64/kernel/head.S > +++ b/arch/arm64/kernel/head.S > @@ -435,6 +435,7 @@ SYM_FUNC_START_LOCAL(__primary_switched) > > mov x0, x21 // pass FDT address in x0 > bl early_fdt_map // Try mapping the FDT early > + bl init_shadow_regs > bl switch_to_vhe > #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) > bl kasan_early_init [...] > +void __init init_shadow_regs(void) Do we need an asmlinkage here? And a declaration somewhere to silence sparse (we did this for entry-common.c function even if the .S files don't consume the prototypes).
On Mon, Jan 18, 2021 at 09:45:24AM +0000, Marc Zyngier wrote: > +struct reg_desc { > + const char * const name; > + u64 * const val; > + u64 * const mask; > + struct { > + const char * const name; > + u8 shift; > + } fields[]; > +}; Sorry, I didn't see this earlier. Do we need to add all these consts here? So you want the pointers to be const but why is 'shift' special and not a const then? Is it modified later? Would this not work: struct reg_desc { const char *name; u64 *val; u64 *mask; struct { const char *name; u8 shift; } fields[]; }; > +static const struct reg_desc * const regs[] __initdata = { as we already declare the whole struct reg_desc pointers here as const. I may have confused myself...
On Sat, 23 Jan 2021 13:43:52 +0000, Catalin Marinas <catalin.marinas@arm.com> wrote: > > On Mon, Jan 18, 2021 at 09:45:24AM +0000, Marc Zyngier wrote: > > +struct reg_desc { > > + const char * const name; > > + u64 * const val; > > + u64 * const mask; > > + struct { > > + const char * const name; > > + u8 shift; > > + } fields[]; > > +}; > > Sorry, I didn't see this earlier. Do we need to add all these consts > here? So you want the pointers to be const but why is 'shift' special > and not a const then? Is it modified later? > > Would this not work: > > struct reg_desc { > const char *name; > u64 *val; > u64 *mask; > struct { > const char *name; > u8 shift; > } fields[]; > }; > > > +static const struct reg_desc * const regs[] __initdata = { > > as we already declare the whole struct reg_desc pointers here as const. > I may have confused myself... It definitely is better. Specially given that we throw all of this away right after boot, there is no harm in keeping it simple. I've also renamed "reg_desc" to "ftr_set_desc", because we don't always describe a register (like for kaslr). Thanks, M.
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 86364ab6f13f..2262f0392857 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -17,7 +17,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ return_address.o cpuinfo.o cpu_errata.o \ cpufeature.o alternative.o cacheinfo.o \ smp.o smp_spin_table.o topology.o smccc-call.o \ - syscall.o proton-pack.o + syscall.o proton-pack.o idreg-override.o targets += efi-entry.o diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index d74e5f84042e..b3c4dd04f74b 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -435,6 +435,7 @@ SYM_FUNC_START_LOCAL(__primary_switched) mov x0, x21 // pass FDT address in x0 bl early_fdt_map // Try mapping the FDT early + bl init_shadow_regs bl switch_to_vhe #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init diff --git a/arch/arm64/kernel/idreg-override.c b/arch/arm64/kernel/idreg-override.c new file mode 100644 index 000000000000..392f93b67103 --- /dev/null +++ b/arch/arm64/kernel/idreg-override.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Early cpufeature override framework + * + * Copyright (C) 2020 Google LLC + * Author: Marc Zyngier <maz@kernel.org> + */ + +#include <linux/kernel.h> +#include <linux/libfdt.h> + +#include <asm/cacheflush.h> +#include <asm/setup.h> + +struct reg_desc { + const char * const name; + u64 * const val; + u64 * const mask; + struct { + const char * const name; + u8 shift; + } fields[]; +}; + +static const struct reg_desc * const regs[] __initdata = { +}; + +static int __init find_field(const char *cmdline, const struct reg_desc *reg, + int f, u64 *v) +{ + char buf[256], *str; + size_t len; + + snprintf(buf, ARRAY_SIZE(buf), "%s.%s=", reg->name, reg->fields[f].name); + + str = strstr(cmdline, buf); + if (!(str == cmdline || (str > cmdline && *(str - 1) == ' '))) + return -1; + + str += strlen(buf); + len = strcspn(str, " "); + len = min(len, ARRAY_SIZE(buf) - 1); + strncpy(buf, str, len); + buf[len] = 0; + + return kstrtou64(buf, 0, v); +} + +static void __init match_options(const char *cmdline) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + int f; + + if (!regs[i]->val || !regs[i]->mask) + continue; + + for (f = 0; regs[i]->fields[f].name; f++) { + u64 v; + + if (find_field(cmdline, regs[i], f, &v)) + continue; + + *regs[i]->val |= (v & 0xf) << regs[i]->fields[f].shift; + *regs[i]->mask |= 0xfUL << regs[i]->fields[f].shift; + } + } +} + +static __init void parse_cmdline(void) +{ + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { + const u8 *prop; + void *fdt; + int node; + + fdt = get_early_fdt_ptr(); + if (!fdt) + goto out; + + node = fdt_path_offset(fdt, "/chosen"); + if (node < 0) + goto out; + + prop = fdt_getprop(fdt, node, "bootargs", NULL); + if (!prop) + goto out; + + match_options(prop); + + if (!IS_ENABLED(CONFIG_CMDLINE_EXTEND)) + return; + } + +out: + match_options(CONFIG_CMDLINE); +} + +void __init init_shadow_regs(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + if (regs[i]->val) + *regs[i]->val = 0; + if (regs[i]->mask) + *regs[i]->mask = 0; + } + + parse_cmdline(); + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + if (regs[i]->val) + __flush_dcache_area(regs[i]->val, sizeof(*regs[i]->val)); + if (regs[i]->mask) + __flush_dcache_area(regs[i]->mask, sizeof(*regs[i]->mask)); + } +}
In order to be able to override CPU features at boot time, let's add a command line parser that matches options of the form "cpureg.feature=value", and store the corresponding value into the override val/mask pair. No features are currently defined, so no expected change in functionality. Signed-off-by: Marc Zyngier <maz@kernel.org> --- arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/head.S | 1 + arch/arm64/kernel/idreg-override.c | 119 +++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/idreg-override.c