diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 1515f6f153a0..6ea17249f33f 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1532,6 +1532,24 @@ config ARM64_PTR_AUTH
 	  This feature works with FUNCTION_GRAPH_TRACER option only if
 	  DYNAMIC_FTRACE_WITH_REGS is enabled.
 
+config ARM64_TBI_DATA
+	bool "Restrict top-byte ignore to data accesses"
+	help
+	  Normally, the kernel will enable top-byte ignore for instruction
+	  accesses as well as data accesses. With this configuration option
+	  enabled, on hardware supporting pointer authentication top-byte
+	  ignore will only be enabled for data accesses.
+
+	  The most important consequence of this is that it increases
+	  the number of signature bits used by the pointer authentication
+	  instructions for instruction addresses by 8, which improves the
+	  security of pointer authentication, but it also has the consequence
+	  of changing the operation of the branch instructions so that they
+	  no longer ignore the top byte of the target address but instead
+	  fault if they are non-zero. If your userspace does not depend on
+	  branch instructions ignoring the top byte it is recommended to
+	  select this option.
+
 config CC_HAS_BRANCH_PROT_PAC_RET
 	# GCC 9 or later, clang 8 or later
 	def_bool $(cc-option,-mbranch-protection=pac-ret+leaf)
diff --git a/arch/arm64/include/asm/compiler.h b/arch/arm64/include/asm/compiler.h
index 6fb2e6bcc392..7332fd35bf6f 100644
--- a/arch/arm64/include/asm/compiler.h
+++ b/arch/arm64/include/asm/compiler.h
@@ -12,15 +12,21 @@
  * The EL0/EL1 pointer bits used by a pointer authentication code.
  * This is dependent on TBI0/TBI1 being enabled, or bits 63:56 would also apply.
  */
-#define ptrauth_user_pac_mask()		GENMASK_ULL(54, vabits_actual)
+#ifdef CONFIG_ARM64_TBI_DATA
+#define ptrauth_user_insn_pac_mask()	GENMASK_ULL(63, vabits_actual)
+#else
+#define ptrauth_user_insn_pac_mask()	GENMASK_ULL(54, vabits_actual)
+#endif
+#define ptrauth_user_data_pac_mask()	GENMASK_ULL(54, vabits_actual)
 #define ptrauth_kernel_pac_mask()	GENMASK_ULL(63, vabits_actual)
 
 /* Valid for EL0 TTBR0 and EL1 TTBR1 instruction pointers */
-#define ptrauth_clear_pac(ptr)						\
-	((ptr & BIT_ULL(55)) ? (ptr | ptrauth_kernel_pac_mask()) :	\
-			       (ptr & ~ptrauth_user_pac_mask()))
+#define ptrauth_clear_insn_pac(ptr)                                            \
+	((ptr & BIT_ULL(55)) ? (ptr | ptrauth_kernel_pac_mask()) :             \
+				     (ptr & ~ptrauth_user_insn_pac_mask()))
 
-#define __builtin_return_address(val)					\
-	(void *)(ptrauth_clear_pac((unsigned long)__builtin_return_address(val)))
+#define __builtin_return_address(val)                                          \
+	((void *)(ptrauth_clear_insn_pac(                                      \
+		(unsigned long)__builtin_return_address(val))))
 
 #endif /* __ASM_COMPILER_H */
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index 42442a0ae2ab..90e69048442d 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -260,6 +260,7 @@
 #define TCR_TBI1		(UL(1) << 38)
 #define TCR_HA			(UL(1) << 39)
 #define TCR_HD			(UL(1) << 40)
+#define TCR_TBID0		(UL(1) << 51)
 #define TCR_TBID1		(UL(1) << 52)
 #define TCR_NFD0		(UL(1) << 53)
 #define TCR_NFD1		(UL(1) << 54)
diff --git a/arch/arm64/include/asm/pointer_auth.h b/arch/arm64/include/asm/pointer_auth.h
index c6b4f0603024..a0022867b8ed 100644
--- a/arch/arm64/include/asm/pointer_auth.h
+++ b/arch/arm64/include/asm/pointer_auth.h
@@ -73,7 +73,7 @@ extern int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg);
 
 static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
 {
-	return ptrauth_clear_pac(ptr);
+	return ptrauth_clear_insn_pac(ptr);
 }
 
 #define ptrauth_thread_init_user(tsk)					\
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 8ac487c84e37..44afc5c3427e 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -893,13 +893,11 @@ static int pac_mask_get(struct task_struct *target,
 {
 	/*
 	 * The PAC bits can differ across data and instruction pointers
-	 * depending on TCR_EL1.TBID*, which we may make use of in future, so
-	 * we expose separate masks.
+	 * depending on TCR_EL1.TBID0, so we expose separate masks.
 	 */
-	unsigned long mask = ptrauth_user_pac_mask();
 	struct user_pac_mask uregs = {
-		.data_mask = mask,
-		.insn_mask = mask,
+		.data_mask = ptrauth_user_data_pac_mask(),
+		.insn_mask = ptrauth_user_insn_pac_mask(),
 	};
 
 	if (!system_supports_address_auth())
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 29a6b8c9e830..617f9f43f528 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -458,11 +458,23 @@ static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
 	vm_fault_t fault;
 	unsigned long vm_flags = VM_ACCESS_FLAGS;
 	unsigned int mm_flags = FAULT_FLAG_DEFAULT;
-	unsigned long addr = untagged_addr(far);
+	unsigned long addr;
 
 	if (kprobe_page_fault(regs, esr))
 		return 0;
 
+	/*
+	 * If TBID0 is set then we may get an IABT with a tagged address here as
+	 * a result of branching to a tagged address. In this case we want to
+	 * avoid untagging the address, let the VMA lookup fail and get a
+	 * SIGSEGV. Leaving the address as is will also work if TBID0 is clear
+	 * or unsupported because the tag bits of FAR_EL1 will be clear.
+	 */
+	if (is_el0_instruction_abort(esr))
+		addr = far;
+	else
+		addr = untagged_addr(far);
+
 	/*
 	 * If we're in an interrupt or have no user context, we must not take
 	 * the fault.
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 97a97a61a8dc..0e715b9604a1 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -45,6 +45,12 @@
 #define TCR_KASAN_FLAGS 0
 #endif
 
+#ifdef CONFIG_ARM64_TBI_DATA
+#define TCR_TBI_DATA_FLAGS TCR_TBID0
+#else
+#define TCR_TBI_DATA_FLAGS 0
+#endif
+
 /*
  * Default MAIR_EL1. MT_NORMAL_TAGGED is initially mapped as Normal memory and
  * changed during __cpu_setup to Normal Tagged if the system supports MTE.
@@ -456,7 +462,7 @@ SYM_FUNC_START(__cpu_setup)
 	 */
 	mov_q	x10, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
 			TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
-			TCR_TBI0 | TCR_A1 | TCR_KASAN_FLAGS
+			TCR_TBI0 | TCR_TBI_DATA_FLAGS | TCR_A1 | TCR_KASAN_FLAGS
 	tcr_clear_errata_bits x10, x9, x5
 
 #ifdef CONFIG_ARM64_VA_BITS_52
