mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
f8da5752fd
In order to be able to disable Pointer Authentication at runtime, whether it is for testing purposes, or to work around HW issues, let's add support for overriding the ID_AA64ISAR1_EL1.{GPI,GPA,API,APA} fields. This is further mapped on the arm64.nopauth command-line alias. Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: David Brazdil <dbrazdil@google.com> Tested-by: Srinivas Ramana <sramana@codeaurora.org> Link: https://lore.kernel.org/r/20210208095732.3267263-23-maz@kernel.org Signed-off-by: Will Deacon <will@kernel.org>
217 lines
4.3 KiB
C
217 lines
4.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Early cpufeature override framework
|
|
*
|
|
* Copyright (C) 2020 Google LLC
|
|
* Author: Marc Zyngier <maz@kernel.org>
|
|
*/
|
|
|
|
#include <linux/ctype.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/libfdt.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cpufeature.h>
|
|
#include <asm/setup.h>
|
|
|
|
#define FTR_DESC_NAME_LEN 20
|
|
#define FTR_DESC_FIELD_LEN 10
|
|
#define FTR_ALIAS_NAME_LEN 30
|
|
#define FTR_ALIAS_OPTION_LEN 80
|
|
|
|
struct ftr_set_desc {
|
|
char name[FTR_DESC_NAME_LEN];
|
|
struct arm64_ftr_override *override;
|
|
struct {
|
|
char name[FTR_DESC_FIELD_LEN];
|
|
u8 shift;
|
|
} fields[];
|
|
};
|
|
|
|
static const struct ftr_set_desc mmfr1 __initconst = {
|
|
.name = "id_aa64mmfr1",
|
|
.override = &id_aa64mmfr1_override,
|
|
.fields = {
|
|
{ "vh", ID_AA64MMFR1_VHE_SHIFT },
|
|
{}
|
|
},
|
|
};
|
|
|
|
static const struct ftr_set_desc pfr1 __initconst = {
|
|
.name = "id_aa64pfr1",
|
|
.override = &id_aa64pfr1_override,
|
|
.fields = {
|
|
{ "bt", ID_AA64PFR1_BT_SHIFT },
|
|
{}
|
|
},
|
|
};
|
|
|
|
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 },
|
|
{}
|
|
},
|
|
};
|
|
|
|
extern struct arm64_ftr_override kaslr_feature_override;
|
|
|
|
static const struct ftr_set_desc kaslr __initconst = {
|
|
.name = "kaslr",
|
|
#ifdef CONFIG_RANDOMIZE_BASE
|
|
.override = &kaslr_feature_override,
|
|
#endif
|
|
.fields = {
|
|
{ "disabled", 0 },
|
|
{}
|
|
},
|
|
};
|
|
|
|
static const struct ftr_set_desc * const regs[] __initconst = {
|
|
&mmfr1,
|
|
&pfr1,
|
|
&isar1,
|
|
&kaslr,
|
|
};
|
|
|
|
static const struct {
|
|
char alias[FTR_ALIAS_NAME_LEN];
|
|
char feature[FTR_ALIAS_OPTION_LEN];
|
|
} aliases[] __initconst = {
|
|
{ "kvm-arm.mode=nvhe", "id_aa64mmfr1.vh=0" },
|
|
{ "kvm-arm.mode=protected", "id_aa64mmfr1.vh=0" },
|
|
{ "arm64.nobti", "id_aa64pfr1.bt=0" },
|
|
{ "arm64.nopauth",
|
|
"id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
|
|
"id_aa64isar1.api=0 id_aa64isar1.apa=0" },
|
|
{ "nokaslr", "kaslr.disabled=1" },
|
|
};
|
|
|
|
static int __init find_field(const char *cmdline,
|
|
const struct ftr_set_desc *reg, int f, u64 *v)
|
|
{
|
|
char opt[FTR_DESC_NAME_LEN + FTR_DESC_FIELD_LEN + 2];
|
|
int len;
|
|
|
|
len = snprintf(opt, ARRAY_SIZE(opt), "%s.%s=",
|
|
reg->name, reg->fields[f].name);
|
|
|
|
if (!parameqn(cmdline, opt, len))
|
|
return -1;
|
|
|
|
return kstrtou64(cmdline + len, 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]->override)
|
|
continue;
|
|
|
|
for (f = 0; strlen(regs[i]->fields[f].name); f++) {
|
|
u64 shift = regs[i]->fields[f].shift;
|
|
u64 mask = 0xfUL << shift;
|
|
u64 v;
|
|
|
|
if (find_field(cmdline, regs[i], f, &v))
|
|
continue;
|
|
|
|
regs[i]->override->val &= ~mask;
|
|
regs[i]->override->val |= (v << shift) & mask;
|
|
regs[i]->override->mask |= mask;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static __init void __parse_cmdline(const char *cmdline, bool parse_aliases)
|
|
{
|
|
do {
|
|
char buf[256];
|
|
size_t len;
|
|
int i;
|
|
|
|
cmdline = skip_spaces(cmdline);
|
|
|
|
for (len = 0; cmdline[len] && !isspace(cmdline[len]); len++);
|
|
if (!len)
|
|
return;
|
|
|
|
len = min(len, ARRAY_SIZE(buf) - 1);
|
|
strncpy(buf, cmdline, len);
|
|
buf[len] = 0;
|
|
|
|
if (strcmp(buf, "--") == 0)
|
|
return;
|
|
|
|
cmdline += len;
|
|
|
|
match_options(buf);
|
|
|
|
for (i = 0; parse_aliases && i < ARRAY_SIZE(aliases); i++)
|
|
if (parameq(buf, aliases[i].alias))
|
|
__parse_cmdline(aliases[i].feature, false);
|
|
} while (1);
|
|
}
|
|
|
|
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;
|
|
|
|
__parse_cmdline(prop, true);
|
|
|
|
if (!IS_ENABLED(CONFIG_CMDLINE_EXTEND))
|
|
return;
|
|
}
|
|
|
|
out:
|
|
__parse_cmdline(CONFIG_CMDLINE, true);
|
|
}
|
|
|
|
/* Keep checkers quiet */
|
|
void init_feature_override(void);
|
|
|
|
asmlinkage void __init init_feature_override(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
|
if (regs[i]->override) {
|
|
regs[i]->override->val = 0;
|
|
regs[i]->override->mask = 0;
|
|
}
|
|
}
|
|
|
|
parse_cmdline();
|
|
|
|
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
|
if (regs[i]->override)
|
|
__flush_dcache_area(regs[i]->override,
|
|
sizeof(*regs[i]->override));
|
|
}
|
|
}
|