[PATCH] arm64: Optionally disable EL0 MTE via command-line

Pierre-Clément Tosi ptosi at google.com
Fri Feb 13 03:51:07 PST 2026


Although it is currently possible to fully disable MTE on MTE-capable
CPUs (with arm64.nomte or id_aa64pfr1.mte=0) and to only use MTE in
userspace (with kasan=off), there is no way to limit the use of MTE to
the kernel because CPU capabilities are traditionally exposed directly
to userspace.

To address this, introduce a new cmdline argument (inspired by the
existing arm64.nomte) to only expose the MTE capability of the CPU to
the kernel. Combined with KASAN, this results in only the kernel using
the feature, while HWCAP2_MTE and the corresponding MSR ID_AA64PFR1_EL1
field are hidden from userspace.

Implement it as a software-only feature override, similar to nokaslr.

Signed-off-by: Pierre-Clément Tosi <ptosi at google.com>
---
 Documentation/admin-guide/kernel-parameters.txt | 3 +++
 arch/arm64/include/asm/cpufeature.h             | 1 +
 arch/arm64/kernel/cpufeature.c                  | 8 ++++++++
 arch/arm64/kernel/pi/idreg-override.c           | 2 ++
 4 files changed, 14 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0869294363b3..4d138c1826f0 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -564,6 +564,9 @@ Kernel parameters
 	arm64.nomte	[ARM64] Unconditionally disable Memory Tagging Extension
 			support
 
+	arm64.nomte_el0	[ARM64] Unconditionally disable Memory Tagging Extension
+			support for userspace
+
 	arm64.nopauth	[ARM64] Unconditionally disable Pointer Authentication
 			support
 
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 4de51f8d92cb..0944ff5084a2 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -18,6 +18,7 @@
 #define ARM64_SW_FEATURE_OVERRIDE_NOKASLR	0
 #define ARM64_SW_FEATURE_OVERRIDE_HVHE		4
 #define ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF	8
+#define ARM64_SW_FEATURE_OVERRIDE_NOMTE_EL0	12
 
 #ifndef __ASSEMBLER__
 
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6044d463d3fb..81ea00050e56 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2412,6 +2412,14 @@ static void user_feature_fixup(void)
 		if (regp)
 			regp->user_mask &= ~ID_AA64PFR1_EL1_SSBS_MASK;
 	}
+
+	if (arm64_test_sw_feature_override(ARM64_SW_FEATURE_OVERRIDE_NOMTE_EL0)) {
+		struct arm64_ftr_reg *regp;
+
+		regp = get_arm64_ftr_reg(SYS_ID_AA64PFR1_EL1);
+		if (regp)
+			regp->user_mask &= ~ID_AA64PFR1_EL1_MTE_MASK;
+	}
 }
 
 static void elf_hwcap_fixup(void)
diff --git a/arch/arm64/kernel/pi/idreg-override.c b/arch/arm64/kernel/pi/idreg-override.c
index bc57b290e5e7..758141bf9e37 100644
--- a/arch/arm64/kernel/pi/idreg-override.c
+++ b/arch/arm64/kernel/pi/idreg-override.c
@@ -211,6 +211,7 @@ static const struct ftr_set_desc sw_features __prel64_initconst = {
 		FIELD("nokaslr", ARM64_SW_FEATURE_OVERRIDE_NOKASLR, NULL),
 		FIELD("hvhe", ARM64_SW_FEATURE_OVERRIDE_HVHE, hvhe_filter),
 		FIELD("rodataoff", ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF, NULL),
+		FIELD("nomte_el0", ARM64_SW_FEATURE_OVERRIDE_NOMTE_EL0, NULL),
 		{}
 	},
 };
@@ -244,6 +245,7 @@ static const struct {
 	  "id_aa64isar2.gpa3=0 id_aa64isar2.apa3=0"	   },
 	{ "arm64.nomops",		"id_aa64isar2.mops=0" },
 	{ "arm64.nomte",		"id_aa64pfr1.mte=0" },
+	{ "arm64.nomte_el0",		"arm64_sw.nomte_el0=1" },
 	{ "nokaslr",			"arm64_sw.nokaslr=1" },
 	{ "rodata=off",			"arm64_sw.rodataoff=1" },
 	{ "arm64.nolva",		"id_aa64mmfr2.varange=0" },
-- 
2.53.0.273.g2a3d683680-goog


-- 
Pierre



More information about the linux-arm-kernel mailing list