Commit 237405eb authored by James Morse's avatar James Morse Committed by Catalin Marinas
Browse files

arm64: cpufeature: Force HWCAP to be based on the sysreg visible to user-space



arm64 advertises hardware features to user-space via HWCAPs, and by
emulating access to the CPUs id registers. The cpufeature code has a
sanitised system-wide view of an id register, and a sanitised user-space
view of an id register, where some features use their 'safe' value
instead of the hardware value.

It is currently possible for a HWCAP to be advertised where the user-space
view of the id register does not show the feature as supported.
Erratum workaround need to remove both the HWCAP, and the feature from
the user-space view of the id register. This involves duplicating the
code, and spreading it over cpufeature.c and cpu_errata.c.

Make the HWCAP code use the user-space view of id registers. This ensures
the values never diverge, and allows erratum workaround to remove HWCAP
by modifying the user-space view of the id register.

Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Reviewed-by: default avatarSuzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20220909165938.3931307-2-james.morse@arm.com


Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent b90cb105
Loading
Loading
Loading
Loading
+30 −7
Original line number Diff line number Diff line
@@ -1401,17 +1401,40 @@ feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
	return val >= entry->min_field_value;
}

static bool
has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
static u64
read_scoped_sysreg(const struct arm64_cpu_capabilities *entry, int scope)
{
	u64 val;

	WARN_ON(scope == SCOPE_LOCAL_CPU && preemptible());
	if (scope == SCOPE_SYSTEM)
		val = read_sanitised_ftr_reg(entry->sys_reg);
		return read_sanitised_ftr_reg(entry->sys_reg);
	else
		val = __read_sysreg_by_encoding(entry->sys_reg);
		return __read_sysreg_by_encoding(entry->sys_reg);
}

static bool
has_user_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
{
	int mask;
	struct arm64_ftr_reg *regp;
	u64 val = read_scoped_sysreg(entry, scope);

	regp = get_arm64_ftr_reg(entry->sys_reg);
	if (!regp)
		return false;

	mask = cpuid_feature_extract_unsigned_field_width(regp->user_mask,
							  entry->field_pos,
							  entry->field_width);
	if (!mask)
		return false;

	return feature_matches(val, entry);
}

static bool
has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
{
	u64 val = read_scoped_sysreg(entry, scope);
	return feature_matches(val, entry);
}

@@ -2624,7 +2647,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
};

#define HWCAP_CPUID_MATCH(reg, field, width, s, min_value)			\
		.matches = has_cpuid_feature,					\
		.matches = has_user_cpuid_feature,					\
		.sys_reg = reg,							\
		.field_pos = field,						\
		.field_width = width,						\