diff --git a/arch/arm64/include/asm/mte.h b/arch/arm64/include/asm/mte.h
index aa523591a44e5..bcabafe010d8c 100644
--- a/arch/arm64/include/asm/mte.h
+++ b/arch/arm64/include/asm/mte.h
@@ -48,6 +48,7 @@ long get_mte_ctrl(struct task_struct *task);
 int mte_ptrace_copy_tags(struct task_struct *child, long request,
 			 unsigned long addr, unsigned long data);
 size_t mte_probe_user_range(const char __user *uaddr, size_t size);
+void mte_update_mair(void);
 
 #else /* CONFIG_ARM64_MTE */
 
@@ -87,6 +88,10 @@ static inline int mte_ptrace_copy_tags(struct task_struct *child,
 	return -EIO;
 }
 
+static inline void mte_update_mair(void)
+{
+}
+
 #endif /* CONFIG_ARM64_MTE */
 
 static inline void mte_disable_tco_entry(struct task_struct *task)
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
index 0d5d1f0525eb3..34d9c25960343 100644
--- a/arch/arm64/include/asm/proc-fns.h
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -19,6 +19,10 @@ extern void cpu_do_idle(void);
 extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
 extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
 
+#ifdef CONFIG_ARM64_MTE
+extern void cpu_mte_update_mair(void);
+#endif
+
 #include <asm/memory.h>
 
 #endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index b2b730233274b..4882c259febb4 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -21,6 +21,7 @@
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
 #include <asm/mte.h>
+#include <asm/proc-fns.h>
 #include <asm/ptrace.h>
 #include <asm/sysreg.h>
 
@@ -574,3 +575,24 @@ size_t mte_probe_user_range(const char __user *uaddr, size_t size)
 
 	return 0;
 }
+
+/*
+ * Open coded check for MTE before CPU features are set up.
+ */
+static bool arm64_early_this_cpu_has_mte(void)
+{
+	u64 pfr1;
+
+	if (!IS_ENABLED(CONFIG_ARM64_MTE))
+		return false;
+
+	pfr1 = __read_sysreg_by_encoding(SYS_ID_AA64PFR1_EL1);
+	return cpuid_feature_extract_unsigned_field(pfr1,
+						    ID_AA64PFR1_MTE_SHIFT);
+}
+
+void mte_update_mair(void)
+{
+	if (arm64_early_this_cpu_has_mte())
+		cpu_mte_update_mair();
+}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 62ed361a4376b..6725f978d6e50 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -43,6 +43,7 @@
 #include <asm/daifflags.h>
 #include <asm/kvm_mmu.h>
 #include <asm/mmu_context.h>
+#include <asm/mte.h>
 #include <asm/numa.h>
 #include <asm/processor.h>
 #include <asm/smp_plat.h>
@@ -226,6 +227,8 @@ asmlinkage notrace void secondary_start_kernel(void)
 	 */
 	check_local_cpu_capabilities();
 
+	mte_update_mair();
+
 	ops = get_cpu_ops(cpu);
 	if (ops->cpu_postboot)
 		ops->cpu_postboot();
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index db7c4e6ae57bb..37835686369ea 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -35,6 +35,7 @@
 #include <linux/sizes.h>
 #include <asm/tlb.h>
 #include <asm/mmu_context.h>
+#include <asm/mte.h>
 #include <asm/ptdump.h>
 #include <asm/tlbflush.h>
 #include <asm/pgalloc.h>
@@ -812,6 +813,8 @@ void __init paging_init(void)
 
 	idmap_t0sz = 63UL - __fls(__pa_symbol(_end) | GENMASK(VA_BITS_MIN - 1, 0));
 
+	mte_update_mair();
+
 	map_kernel(pgdp);
 	map_mem(pgdp);
 
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 7837a69524c53..dc740db298dea 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -393,6 +393,22 @@ SYM_FUNC_END(idmap_kpti_install_ng_mappings)
 	.popsection
 #endif
 
+#ifdef CONFIG_ARM64_MTE
+/*
+ * 	cpu_mte_update_mair
+ *
+ * 	Setup Normal Tagged memory type at the corresponding MAIR index
+ */
+SYM_FUNC_START(cpu_mte_update_mair)
+	mov_q	x9, MAIR_EL1_SET
+	mov	x10, #MAIR_ATTR_NORMAL_TAGGED
+	bfi	x9, x10, #(8 *  MT_NORMAL_TAGGED), #8
+	msr	mair_el1, x9
+	tlbi	vmalle1				// Invalidate local TLB
+	dsb	nsh
+	ret
+SYM_FUNC_END(cpu_mte_update_mair)
+#endif
 /*
  *	__cpu_setup
  *
@@ -421,16 +437,14 @@ SYM_FUNC_START(__cpu_setup)
 	 * Default values for VMSA control registers. These will be adjusted
 	 * below depending on detected CPU features.
 	 */
-	mair	.req	x17
 	tcr	.req	x16
-	mov_q	mair, MAIR_EL1_SET
 	mov_q	tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
 			TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
 			TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS
 
 #ifdef CONFIG_ARM64_MTE
 	/*
-	 * Update MAIR_EL1, GCR_EL1 and TFSR*_EL1 if MTE is supported
+	 * Update GCR_EL1 and TFSR*_EL1 if MTE is supported
 	 * (ID_AA64PFR1_EL1[11:8] > 1).
 	 */
 	mrs	x10, ID_AA64PFR1_EL1
@@ -438,10 +452,6 @@ SYM_FUNC_START(__cpu_setup)
 	cmp	x10, #ID_AA64PFR1_MTE
 	b.lt	1f
 
-	/* Normal Tagged memory type at the corresponding MAIR index */
-	mov	x10, #MAIR_ATTR_NORMAL_TAGGED
-	bfi	mair, x10, #(8 *  MT_NORMAL_TAGGED), #8
-
 	mov	x10, #KERNEL_GCR_EL1
 	msr_s	SYS_GCR_EL1, x10
 
@@ -493,7 +503,8 @@ SYM_FUNC_START(__cpu_setup)
 	orr	tcr, tcr, #TCR_HA		// hardware Access flag update
 1:
 #endif	/* CONFIG_ARM64_HW_AFDBM */
-	msr	mair_el1, mair
+	mov_q	x9, MAIR_EL1_SET
+	msr	mair_el1, x9
 	msr	tcr_el1, tcr
 	/*
 	 * Prepare SCTLR
@@ -501,6 +512,5 @@ SYM_FUNC_START(__cpu_setup)
 	mov_q	x0, INIT_SCTLR_EL1_MMU_ON
 	ret					// return to head.S
 
-	.unreq	mair
 	.unreq	tcr
 SYM_FUNC_END(__cpu_setup)
