@@ -94,6 +94,7 @@
memory@80000000 {
device_type = "memory";
+ arm,armv8.5-memtag;
reg = <0x00000000 0x80000000 0 0x80000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
@@ -61,6 +61,7 @@ struct arm64_ftr_bits {
u8 shift;
u8 width;
s64 safe_val; /* safe value for FTR_EXACT features */
+ s64 (*filter)(const struct arm64_ftr_bits *, s64);
};
/*
@@ -542,7 +543,10 @@ cpuid_feature_extract_field(u64 features, int field, bool sign)
static inline s64 arm64_ftr_value(const struct arm64_ftr_bits *ftrp, u64 val)
{
- return (s64)cpuid_feature_extract_field_width(val, ftrp->shift, ftrp->width, ftrp->sign);
+ s64 fval = (s64)cpuid_feature_extract_field_width(val, ftrp->shift, ftrp->width, ftrp->sign);
+ if (ftrp->filter)
+ fval = ftrp->filter(ftrp, fval);
+ return fval;
}
static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) "CPU features: " fmt
+#include <linux/acpi.h>
#include <linux/bsearch.h>
#include <linux/cpumask.h>
#include <linux/crash_dump.h>
@@ -14,6 +15,7 @@
#include <linux/stop_machine.h>
#include <linux/types.h>
#include <linux/mm.h>
+#include <linux/of.h>
#include <linux/cpu.h>
#include <asm/cpu.h>
#include <asm/cpufeature.h>
@@ -87,23 +89,28 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(cpu_hwcap_keys, ARM64_NCAPS);
EXPORT_SYMBOL(cpu_hwcap_keys);
#define __ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
- { \
.sign = SIGNED, \
.visible = VISIBLE, \
.strict = STRICT, \
.type = TYPE, \
.shift = SHIFT, \
.width = WIDTH, \
- .safe_val = SAFE_VAL, \
- }
+ .safe_val = SAFE_VAL
/* Define a feature with unsigned values */
#define ARM64_FTR_BITS(VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
- __ARM64_FTR_BITS(FTR_UNSIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+ { __ARM64_FTR_BITS(FTR_UNSIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL), }
/* Define a feature with a signed value */
#define S_ARM64_FTR_BITS(VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
- __ARM64_FTR_BITS(FTR_SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+ { __ARM64_FTR_BITS(FTR_SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL), }
+
+/* Define a feature with a filter function to process the field value */
+#define FILTERED_ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL, filter_fn) \
+ { \
+ __ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL), \
+ .filter = filter_fn, \
+ }
#define ARM64_FTR_END \
{ \
@@ -118,6 +125,42 @@ static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);
static bool __system_matches_cap(unsigned int n);
+#ifdef CONFIG_ARM64_MTE
+s64 mte_ftr_filter(const struct arm64_ftr_bits *ftrp, s64 val)
+{
+ struct device_node *np;
+ static bool memory_checked = false;
+ static bool mte_capable = true;
+
+ /* EL0-only MTE is not supported by Linux, don't expose it */
+ if (val < ID_AA64PFR1_MTE)
+ return ID_AA64PFR1_MTE_NI;
+
+ if (memory_checked)
+ return mte_capable ? val : ID_AA64PFR1_MTE_NI;
+
+ if (!acpi_disabled) {
+ pr_warn("MTE not supported on ACPI systems\n");
+ return ID_AA64PFR1_MTE_NI;
+ }
+
+ /* check the DT "memory" nodes for MTE support */
+ for_each_node_by_type(np, "memory") {
+ memory_checked = true;
+ mte_capable &= of_property_read_bool(np, "arm,armv8.5-memtag");
+ }
+
+ if (!memory_checked || !mte_capable) {
+ pr_warn("System memory is not MTE-capable\n");
+ memory_checked = true;
+ mte_capable = false;
+ return ID_AA64PFR1_MTE_NI;
+ }
+
+ return val;
+}
+#endif
+
/*
* NOTE: Any changes to the visibility of features should be kept in
* sync with the documentation of the CPU feature register ABI.
@@ -182,8 +225,10 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = {
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_SSBS_SHIFT, 4, ID_AA64PFR1_SSBS_PSTATE_NI),
- ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_MTE),
- FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_MTE_SHIFT, 4, ID_AA64PFR1_MTE_NI),
+#ifdef CONFIG_ARM64_MTE
+ FILTERED_ARM64_FTR_BITS(FTR_UNSIGNED, FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE,
+ ID_AA64PFR1_MTE_SHIFT, 4, ID_AA64PFR1_MTE_NI, mte_ftr_filter),
+#endif
ARM64_FTR_END,
};