cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

idreg-override.c (5651B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Early cpufeature override framework
      4 *
      5 * Copyright (C) 2020 Google LLC
      6 * Author: Marc Zyngier <maz@kernel.org>
      7 */
      8
      9#include <linux/ctype.h>
     10#include <linux/kernel.h>
     11#include <linux/libfdt.h>
     12
     13#include <asm/cacheflush.h>
     14#include <asm/cpufeature.h>
     15#include <asm/setup.h>
     16
     17#define FTR_DESC_NAME_LEN	20
     18#define FTR_DESC_FIELD_LEN	10
     19#define FTR_ALIAS_NAME_LEN	30
     20#define FTR_ALIAS_OPTION_LEN	116
     21
     22struct ftr_set_desc {
     23	char 				name[FTR_DESC_NAME_LEN];
     24	struct arm64_ftr_override	*override;
     25	struct {
     26		char			name[FTR_DESC_FIELD_LEN];
     27		u8			shift;
     28		bool			(*filter)(u64 val);
     29	} 				fields[];
     30};
     31
     32static bool __init mmfr1_vh_filter(u64 val)
     33{
     34	/*
     35	 * If we ever reach this point while running VHE, we're
     36	 * guaranteed to be on one of these funky, VHE-stuck CPUs. If
     37	 * the user was trying to force nVHE on us, proceed with
     38	 * attitude adjustment.
     39	 */
     40	return !(is_kernel_in_hyp_mode() && val == 0);
     41}
     42
     43static const struct ftr_set_desc mmfr1 __initconst = {
     44	.name		= "id_aa64mmfr1",
     45	.override	= &id_aa64mmfr1_override,
     46	.fields		= {
     47		{ "vh", ID_AA64MMFR1_VHE_SHIFT, mmfr1_vh_filter },
     48		{}
     49	},
     50};
     51
     52static const struct ftr_set_desc pfr1 __initconst = {
     53	.name		= "id_aa64pfr1",
     54	.override	= &id_aa64pfr1_override,
     55	.fields		= {
     56	        { "bt", ID_AA64PFR1_BT_SHIFT },
     57		{ "mte", ID_AA64PFR1_MTE_SHIFT},
     58		{}
     59	},
     60};
     61
     62static const struct ftr_set_desc isar1 __initconst = {
     63	.name		= "id_aa64isar1",
     64	.override	= &id_aa64isar1_override,
     65	.fields		= {
     66	        { "gpi", ID_AA64ISAR1_GPI_SHIFT },
     67	        { "gpa", ID_AA64ISAR1_GPA_SHIFT },
     68	        { "api", ID_AA64ISAR1_API_SHIFT },
     69	        { "apa", ID_AA64ISAR1_APA_SHIFT },
     70		{}
     71	},
     72};
     73
     74static const struct ftr_set_desc isar2 __initconst = {
     75	.name		= "id_aa64isar2",
     76	.override	= &id_aa64isar2_override,
     77	.fields		= {
     78	        { "gpa3", ID_AA64ISAR2_GPA3_SHIFT },
     79	        { "apa3", ID_AA64ISAR2_APA3_SHIFT },
     80		{}
     81	},
     82};
     83
     84extern struct arm64_ftr_override kaslr_feature_override;
     85
     86static const struct ftr_set_desc kaslr __initconst = {
     87	.name		= "kaslr",
     88#ifdef CONFIG_RANDOMIZE_BASE
     89	.override	= &kaslr_feature_override,
     90#endif
     91	.fields		= {
     92		{ "disabled", 0 },
     93		{}
     94	},
     95};
     96
     97static const struct ftr_set_desc * const regs[] __initconst = {
     98	&mmfr1,
     99	&pfr1,
    100	&isar1,
    101	&isar2,
    102	&kaslr,
    103};
    104
    105static const struct {
    106	char	alias[FTR_ALIAS_NAME_LEN];
    107	char	feature[FTR_ALIAS_OPTION_LEN];
    108} aliases[] __initconst = {
    109	{ "kvm-arm.mode=nvhe",		"id_aa64mmfr1.vh=0" },
    110	{ "kvm-arm.mode=protected",	"id_aa64mmfr1.vh=0" },
    111	{ "arm64.nobti",		"id_aa64pfr1.bt=0" },
    112	{ "arm64.nopauth",
    113	  "id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
    114	  "id_aa64isar1.api=0 id_aa64isar1.apa=0 "
    115	  "id_aa64isar2.gpa3=0 id_aa64isar2.apa3=0"	   },
    116	{ "arm64.nomte",		"id_aa64pfr1.mte=0" },
    117	{ "nokaslr",			"kaslr.disabled=1" },
    118};
    119
    120static int __init find_field(const char *cmdline,
    121			     const struct ftr_set_desc *reg, int f, u64 *v)
    122{
    123	char opt[FTR_DESC_NAME_LEN + FTR_DESC_FIELD_LEN + 2];
    124	int len;
    125
    126	len = snprintf(opt, ARRAY_SIZE(opt), "%s.%s=",
    127		       reg->name, reg->fields[f].name);
    128
    129	if (!parameqn(cmdline, opt, len))
    130		return -1;
    131
    132	return kstrtou64(cmdline + len, 0, v);
    133}
    134
    135static void __init match_options(const char *cmdline)
    136{
    137	int i;
    138
    139	for (i = 0; i < ARRAY_SIZE(regs); i++) {
    140		int f;
    141
    142		if (!regs[i]->override)
    143			continue;
    144
    145		for (f = 0; strlen(regs[i]->fields[f].name); f++) {
    146			u64 shift = regs[i]->fields[f].shift;
    147			u64 mask = 0xfUL << shift;
    148			u64 v;
    149
    150			if (find_field(cmdline, regs[i], f, &v))
    151				continue;
    152
    153			/*
    154			 * If an override gets filtered out, advertise
    155			 * it by setting the value to 0xf, but
    156			 * clearing the mask... Yes, this is fragile.
    157			 */
    158			if (regs[i]->fields[f].filter &&
    159			    !regs[i]->fields[f].filter(v)) {
    160				regs[i]->override->val  |= mask;
    161				regs[i]->override->mask &= ~mask;
    162				continue;
    163			}
    164
    165			regs[i]->override->val  &= ~mask;
    166			regs[i]->override->val  |= (v << shift) & mask;
    167			regs[i]->override->mask |= mask;
    168
    169			return;
    170		}
    171	}
    172}
    173
    174static __init void __parse_cmdline(const char *cmdline, bool parse_aliases)
    175{
    176	do {
    177		char buf[256];
    178		size_t len;
    179		int i;
    180
    181		cmdline = skip_spaces(cmdline);
    182
    183		for (len = 0; cmdline[len] && !isspace(cmdline[len]); len++);
    184		if (!len)
    185			return;
    186
    187		len = min(len, ARRAY_SIZE(buf) - 1);
    188		strncpy(buf, cmdline, len);
    189		buf[len] = 0;
    190
    191		if (strcmp(buf, "--") == 0)
    192			return;
    193
    194		cmdline += len;
    195
    196		match_options(buf);
    197
    198		for (i = 0; parse_aliases && i < ARRAY_SIZE(aliases); i++)
    199			if (parameq(buf, aliases[i].alias))
    200				__parse_cmdline(aliases[i].feature, false);
    201	} while (1);
    202}
    203
    204static __init const u8 *get_bootargs_cmdline(void)
    205{
    206	const u8 *prop;
    207	void *fdt;
    208	int node;
    209
    210	fdt = get_early_fdt_ptr();
    211	if (!fdt)
    212		return NULL;
    213
    214	node = fdt_path_offset(fdt, "/chosen");
    215	if (node < 0)
    216		return NULL;
    217
    218	prop = fdt_getprop(fdt, node, "bootargs", NULL);
    219	if (!prop)
    220		return NULL;
    221
    222	return strlen(prop) ? prop : NULL;
    223}
    224
    225static __init void parse_cmdline(void)
    226{
    227	const u8 *prop = get_bootargs_cmdline();
    228
    229	if (IS_ENABLED(CONFIG_CMDLINE_FORCE) || !prop)
    230		__parse_cmdline(CONFIG_CMDLINE, true);
    231
    232	if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && prop)
    233		__parse_cmdline(prop, true);
    234}
    235
    236/* Keep checkers quiet */
    237void init_feature_override(void);
    238
    239asmlinkage void __init init_feature_override(void)
    240{
    241	int i;
    242
    243	for (i = 0; i < ARRAY_SIZE(regs); i++) {
    244		if (regs[i]->override) {
    245			regs[i]->override->val  = 0;
    246			regs[i]->override->mask = 0;
    247		}
    248	}
    249
    250	parse_cmdline();
    251
    252	for (i = 0; i < ARRAY_SIZE(regs); i++) {
    253		if (regs[i]->override)
    254			dcache_clean_inval_poc((unsigned long)regs[i]->override,
    255					    (unsigned long)regs[i]->override +
    256					    sizeof(*regs[i]->override));
    257	}
    258}