Commit 8f266a5d authored by Marc Zyngier's avatar Marc Zyngier Committed by Will Deacon
Browse files

arm64: cpufeature: Add global feature override facility



Add a facility to globally override a feature, no matter what
the HW says. Yes, this sounds dangerous, but we do respect the
"safe" value for a given feature. This doesn't mean the user
doesn't need to know what they are doing.

Nothing uses this yet, so we are pretty safe. For now.

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Reviewed-by: default avatarSuzuki K Poulose <suzuki.poulose@arm.com>
Acked-by: default avatarDavid Brazdil <dbrazdil@google.com>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20210208095732.3267263-11-maz@kernel.org


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent d077cb3c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -63,6 +63,11 @@ struct arm64_ftr_bits {
	s64		safe_val; /* safe value for FTR_EXACT features */
};

struct arm64_ftr_override {
	u64		val;
	u64		mask;
};

/*
 * @arm64_ftr_reg - Feature register
 * @strict_mask		Bits which should match across all CPUs for sanity.
@@ -74,6 +79,7 @@ struct arm64_ftr_reg {
	u64				user_mask;
	u64				sys_val;
	u64				user_val;
	struct arm64_ftr_override	*override;
	const struct arm64_ftr_bits	*ftr_bits;
};

+39 −6
Original line number Diff line number Diff line
@@ -352,9 +352,12 @@ static const struct arm64_ftr_bits ftr_ctr[] = {
	ARM64_FTR_END,
};

static struct arm64_ftr_override __ro_after_init no_override = { };

struct arm64_ftr_reg arm64_ftr_reg_ctrel0 = {
	.name		= "SYS_CTR_EL0",
	.ftr_bits	= ftr_ctr
	.ftr_bits	= ftr_ctr,
	.override	= &no_override,
};

static const struct arm64_ftr_bits ftr_id_mmfr0[] = {
@@ -544,13 +547,16 @@ static const struct arm64_ftr_bits ftr_raz[] = {
	ARM64_FTR_END,
};

#define ARM64_FTR_REG(id, table) {		\
#define ARM64_FTR_REG_OVERRIDE(id, table, ovr) {		\
		.sys_id = id,					\
		.reg = 	&(struct arm64_ftr_reg){		\
			.name = #id,				\
			.override = (ovr),			\
			.ftr_bits = &((table)[0]),		\
	}}

#define ARM64_FTR_REG(id, table) ARM64_FTR_REG_OVERRIDE(id, table, &no_override)

static const struct __ftr_reg_entry {
	u32			sys_id;
	struct arm64_ftr_reg 	*reg;
@@ -770,6 +776,33 @@ static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
	for (ftrp = reg->ftr_bits; ftrp->width; ftrp++) {
		u64 ftr_mask = arm64_ftr_mask(ftrp);
		s64 ftr_new = arm64_ftr_value(ftrp, new);
		s64 ftr_ovr = arm64_ftr_value(ftrp, reg->override->val);

		if ((ftr_mask & reg->override->mask) == ftr_mask) {
			s64 tmp = arm64_ftr_safe_value(ftrp, ftr_ovr, ftr_new);
			char *str = NULL;

			if (ftr_ovr != tmp) {
				/* Unsafe, remove the override */
				reg->override->mask &= ~ftr_mask;
				reg->override->val &= ~ftr_mask;
				tmp = ftr_ovr;
				str = "ignoring override";
			} else if (ftr_new != tmp) {
				/* Override was valid */
				ftr_new = tmp;
				str = "forced";
			} else if (ftr_ovr == tmp) {
				/* Override was the safe value */
				str = "already set";
			}

			if (str)
				pr_warn("%s[%d:%d]: %s to %llx\n",
					reg->name,
					ftrp->shift + ftrp->width - 1,
					ftrp->shift, str, tmp);
		}

		val = arm64_ftr_set_value(ftrp, val, ftr_new);