feat(cpufeat): enable FEAT_PAuth to FEAT_STATE_CHECKED

FEAT_PAuth is the second to last feature to be a boolean choice - it's
either unconditionally compiled in and must be present in hardware or
it's not compiled in. FEAT_PAuth is architected to be backwards
compatible - a subset of the branch guarding instructions (pacia/autia)
execute as NOPs when PAuth is not present. That subset is used with
`-mbranch-protection=standard` and -march pre-8.3. This patch adds the
necessary logic to also check accesses of the non-backward compatible
registers and allow a fully checked implementation.

Note that a checked support requires -march to be pre 8.3, as otherwise
the compiler will include branch protection instructions that are not
NOPs without PAuth (eg retaa) which cannot be checked.

Change-Id: Id942c20cae9d15d25b3d72b8161333642574ddaa
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/Makefile b/Makefile
index aae3452..fd55f03 100644
--- a/Makefile
+++ b/Makefile
@@ -492,6 +492,12 @@
 include ${PLAT_MAKEFILE_FULL}
 
 ################################################################################
+# Setup arch_features based on ARM_ARCH_MAJOR, ARM_ARCH_MINOR provided from
+# platform.
+################################################################################
+
+include ${MAKE_HELPERS_DIRECTORY}arch_features.mk
+################################################################################
 # Process BRANCH_PROTECTION value and set
 # Pointer Authentication and Branch Target Identification flags
 ################################################################################
@@ -517,30 +523,29 @@
 	# Turn on branch target identification mechanism
 	BP_OPTION := bti
 	ENABLE_BTI := 1
+else ifeq (${BRANCH_PROTECTION},5)
+	# Turn on branch target identification mechanism
+	BP_OPTION := standard
+	ENABLE_BTI := 2
+	ENABLE_PAUTH := 2
 else
         $(error Unknown BRANCH_PROTECTION value ${BRANCH_PROTECTION})
 endif #(BRANCH_PROTECTION)
 
-ifeq ($(ENABLE_PAUTH),1)
-	CTX_INCLUDE_PAUTH_REGS := 1
+ifneq ($(ENABLE_PAUTH),0)
+	CTX_INCLUDE_PAUTH_REGS	:= ${ENABLE_PAUTH}
 endif
 ifneq (${BP_OPTION},none)
 	TF_CFLAGS_aarch64	+=	-mbranch-protection=${BP_OPTION}
 endif #(BP_OPTION)
 
 # Pointer Authentication sources
-ifeq (${ENABLE_PAUTH}, 1)
+ifneq (${ENABLE_PAUTH},0)
 # arm/common/aarch64/arm_pauth.c contains a sample platform hook to complete the
 # Pauth support. As it's not secure, it must be reimplemented for real platforms
-	BL_COMMON_SOURCES	+=	lib/extensions/pauth/pauth_helpers.S
+	BL_COMMON_SOURCES	+=	lib/extensions/pauth/pauth.c
 endif
 
-################################################################################
-# Setup arch_features based on ARM_ARCH_MAJOR, ARM_ARCH_MINOR provided from
-# platform.
-################################################################################
-include ${MAKE_HELPERS_DIRECTORY}arch_features.mk
-
 ####################################################
 # Enable required options for Memory Stack Tagging.
 ####################################################
@@ -917,13 +922,13 @@
 # If pointer authentication is used in the firmware, make sure that all the
 # registers associated to it are also saved and restored.
 # Not doing it would leak the value of the keys used by EL3 to EL1 and S-EL1.
-ifeq ($(ENABLE_PAUTH),1)
+ifneq ($(ENABLE_PAUTH),0)
 	ifeq ($(CTX_INCLUDE_PAUTH_REGS),0)
-                $(error Pointer Authentication requires CTX_INCLUDE_PAUTH_REGS=1)
+                $(error Pointer Authentication requires CTX_INCLUDE_PAUTH_REGS to be enabled)
 	endif
 endif #(ENABLE_PAUTH)
 
-ifeq ($(CTX_INCLUDE_PAUTH_REGS),1)
+ifneq ($(CTX_INCLUDE_PAUTH_REGS),0)
 	ifneq (${ARCH},aarch64)
                 $(error CTX_INCLUDE_PAUTH_REGS requires AArch64)
 	endif
diff --git a/bl2/bl2_main.c b/bl2/bl2_main.c
index 4ff73d7..dbe9453 100644
--- a/bl2/bl2_main.c
+++ b/bl2/bl2_main.c
@@ -120,12 +120,12 @@
 	disable_mmu_icache_secure();
 #endif /* !__aarch64__ */
 
-#if ENABLE_PAUTH
 	/*
 	 * Disable pointer authentication before running next boot image
 	 */
-	pauth_disable_el1();
-#endif /* ENABLE_PAUTH */
+	if (is_feat_pauth_supported()) {
+		pauth_disable_el1();
+	}
 
 #if ENABLE_RUNTIME_INSTRUMENTATION
 	PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_EXIT, PMF_CACHE_MAINT);
@@ -148,12 +148,12 @@
 #endif
 	console_flush();
 
-#if ENABLE_PAUTH
 	/*
 	 * Disable pointer authentication before running next boot image
 	 */
-	pauth_disable_el3();
-#endif /* ENABLE_PAUTH */
+	if (is_feat_pauth_supported()) {
+		pauth_disable_el3();
+	}
 
 	bl2_run_next_image(next_bl_ep_info);
 #endif /* BL2_RUNS_AT_EL3 */
diff --git a/bl31/aarch64/crash_reporting.S b/bl31/aarch64/crash_reporting.S
index b5bf575..7b61692 100644
--- a/bl31/aarch64/crash_reporting.S
+++ b/bl31/aarch64/crash_reporting.S
@@ -397,8 +397,26 @@
 	ldr	x4, [x0, #REGSZ * 7]
 
 #if ENABLE_PAUTH
+#if ENABLE_PAUTH == 2
+	/* Skip if not present in hardware */
+	is_feat_pauth_present_asm x0, x1
+	beq	1f
+#endif
+	/*
+	 * The assembler must see support for xpaci. So turn the compiler
+	 * extension on. GCC prior to 10 doesn't understand the PAuth extension
+	 * but it does understand armv8.3-a in general. Avoid using 8.3 if
+	 * the compiler understands "pauth" so we don't downgrade a higher
+	 * -march that was specified on the commandline.
+	 */
+#if __GNUC__ < 10
+	.arch armv8.3-a
+#else
+	.arch_extension pauth
+#endif
 	/* Demangle address */
 	xpaci	x4
+1:
 #endif
 	bl	asm_print_hex
 	bl	asm_print_newline
diff --git a/common/backtrace/backtrace.c b/common/backtrace/backtrace.c
index f994ae5..479c4fd 100644
--- a/common/backtrace/backtrace.c
+++ b/common/backtrace/backtrace.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -8,6 +8,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include <arch_features.h>
 #include <arch_helpers.h>
 #include <common/debug.h>
 #include <drivers/console.h>
@@ -41,15 +42,14 @@
 {
 	uintptr_t ret = address;
 
-#if ENABLE_PAUTH
 	/*
 	 * When pointer authentication is enabled, the LR value saved on the
 	 * stack contains a PAC. It must be stripped to retrieve the return
 	 * address.
 	 */
-
-	xpaci(ret);
-#endif
+	if (is_feat_pauth_supported()) {
+		ret = xpaci(address);
+	}
 
 	return ret;
 }
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index b5814bb..2a42269 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -113,12 +113,16 @@
 -  ``BRANCH_PROTECTION``: Numeric value to enable ARMv8.3 Pointer Authentication
    and ARMv8.5 Branch Target Identification support for TF-A BL images themselves.
    If enabled, it is needed to use a compiler that supports the option
-   ``-mbranch-protection``. Selects the branch protection features to use:
--  0: Default value turns off all types of branch protection
+   ``-mbranch-protection``. The value of the ``-march`` (via ``ARM_ARCH_MINOR``
+   and ``ARM_ARCH_MAJOR``) option will control which instructions will be
+   emitted (HINT space or not). Selects the branch protection features to use:
+-  0: Default value turns off all types of branch protection (FEAT_STATE_DISABLED)
 -  1: Enables all types of branch protection features
 -  2: Return address signing to its standard level
 -  3: Extend the signing to include leaf functions
 -  4: Turn on branch target identification mechanism
+-  5: Enables all types of branch protection features, only if present in
+   hardware (FEAT_STATE_CHECK).
 
    The table below summarizes ``BRANCH_PROTECTION`` values, GCC compilation options
    and resulting PAuth/BTI features.
@@ -136,6 +140,8 @@
    +-------+--------------+-------+-----+
    |   4   |     bti      |   N   |  Y  |
    +-------+--------------+-------+-----+
+   |   5   |   dynamic    |   Y   |  Y  |
+   +-------+--------------+-------+-----+
 
    This option defaults to 0.
    Note that Pointer Authentication is enabled for Non-secure world
@@ -198,11 +204,13 @@
 -  ``CTX_INCLUDE_PAUTH_REGS``: Numeric value to enable the Pointer
    Authentication for Secure world. This will cause the ARMv8.3-PAuth registers
    to be included when saving and restoring the CPU context as part of world
-   switch. This flag can take values 0 to 2, to align with ``ENABLE_FEAT``
-   mechanism. Default value is 0.
+   switch. Automatically enabled when ``BRANCH_PROTECTION`` is enabled. This flag
+   can take values 0 to 2, to align with ``ENABLE_FEAT`` mechanism. Default value
+   is 0.
 
    Note that Pointer Authentication is enabled for Non-secure world irrespective
-   of the value of this flag if the CPU supports it.
+   of the value of this flag if the CPU supports it. Alternatively, when
+   ``BRANCH_PROTECTION`` is enabled, this flag is superseded.
 
 -  ``CTX_INCLUDE_SVE_REGS``: Boolean option that, when set to 1, will cause the
    SVE registers to be included when saving and restoring the CPU context. Note
diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h
index 569182a..9419583 100644
--- a/include/arch/aarch64/arch_helpers.h
+++ b/include/arch/aarch64/arch_helpers.h
@@ -238,7 +238,15 @@
 /*******************************************************************************
  * Strip Pointer Authentication Code
  ******************************************************************************/
-DEFINE_SYSOP_PARAM_FUNC(xpaci)
+static inline u_register_t xpaci(u_register_t arg)
+{
+	register u_register_t x0 asm("x0") = arg;
+
+	/* `xpaci x0` for compatibility with older compiler and/or older -march */
+	__asm__ (".arch armv8.3-a; xpaci %0\n" : "+r" (x0));
+
+	return x0;
+}
 
 void flush_dcache_range(uintptr_t addr, size_t size);
 void flush_dcache_to_popa_range(uintptr_t addr, size_t size);
diff --git a/include/arch/aarch64/asm_macros.S b/include/arch/aarch64/asm_macros.S
index dce07d9..da51bf8 100644
--- a/include/arch/aarch64/asm_macros.S
+++ b/include/arch/aarch64/asm_macros.S
@@ -329,7 +329,7 @@
 	.endm
 
 	/*
-	* is_feat_sysreg128_present_asm - Set flags and reg if FEAT_SYSREG128
+	* is_feat_XYZ_present_asm - Set flags and reg if FEAT_XYZ
 	* is enabled at runtime.
 	*
 	* Arguments:
@@ -342,6 +342,15 @@
 	ands	\reg, \reg, #(ID_AA64ISAR2_SYSREG128_MASK << ID_AA64ISAR2_SYSREG128_SHIFT)
 	.endm
 
+	.macro is_feat_pauth_present_asm reg:req, clobber:req
+	mrs	\reg, ID_AA64ISAR1_EL1
+	mov_imm	\clobber, ((ID_AA64ISAR1_GPI_MASK << ID_AA64ISAR1_GPI_SHIFT) \
+			 | (ID_AA64ISAR1_GPA_MASK << ID_AA64ISAR1_GPA_SHIFT) \
+			 | (ID_AA64ISAR1_API_MASK << ID_AA64ISAR1_API_SHIFT) \
+			 | (ID_AA64ISAR1_APA_MASK << ID_AA64ISAR1_APA_SHIFT))
+	tst	\reg, \clobber
+	.endm
+
 .macro call_reset_handler
 #if !(defined(IMAGE_BL2) && ENABLE_RME)
 	/* ---------------------------------------------------------------------
diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S
index 7d003b6..7929901 100644
--- a/lib/el3_runtime/aarch64/context.S
+++ b/lib/el3_runtime/aarch64/context.S
@@ -379,6 +379,11 @@
 	mrs	x9, pmcr_el0
 	str	x9, [sp, #CTX_EL3STATE_OFFSET + CTX_PMCR_EL0]
 #if CTX_INCLUDE_PAUTH_REGS
+#if CTX_INCLUDE_PAUTH_REGS == 2
+	/* Skip if not present in hardware */
+	is_feat_pauth_present_asm x9, x10
+	beq	no_pauth_\@
+#endif
 	/* ----------------------------------------------------------
  	 * Save the ARMv8.3-PAuth keys as they are not banked
  	 * by exception level
@@ -419,6 +424,7 @@
 	/* Program instruction key A */
 	msr	APIAKeyLo_EL1, x10
 	msr	APIAKeyHi_EL1, x11
+no_pauth_\@:
 #endif /* ENABLE_PAUTH */
 #endif /* CTX_INCLUDE_PAUTH_REGS */
 	.endm /* save_gp_pmcr_pauth_regs */
@@ -460,6 +466,11 @@
  */
 func restore_gp_pmcr_pauth_regs
 #if CTX_INCLUDE_PAUTH_REGS
+#if CTX_INCLUDE_PAUTH_REGS == 2
+	/* Skip if not present in hardware */
+	is_feat_pauth_present_asm x0, x1
+	beq	no_pauth
+#endif
  	/* Restore the ARMv8.3 PAuth keys */
 	add	x10, sp, #CTX_PAUTH_REGS_OFFSET
 
@@ -479,6 +490,7 @@
 	msr	APDBKeyHi_EL1, x7
 	msr	APGAKeyLo_EL1, x8
 	msr	APGAKeyHi_EL1, x9
+no_pauth:
 #endif /* CTX_INCLUDE_PAUTH_REGS */
 
 	/* PMUv3 is presumed to be always present */
diff --git a/lib/el3_runtime/aarch64/context_debug.c b/lib/el3_runtime/aarch64/context_debug.c
index b37bcb7..1ae7f6b 100644
--- a/lib/el3_runtime/aarch64/context_debug.c
+++ b/lib/el3_runtime/aarch64/context_debug.c
@@ -1,11 +1,12 @@
 /*
- * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2023-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <string.h>
 
+#include <arch_features.h>
 #include <common/debug.h>
 #include <context.h>
 #include <lib/el3_runtime/context_mgmt.h>
@@ -65,11 +66,11 @@
 #else
 	size_t el1_total = 0U;
 #endif /* CTX_INCLUDE_EL2_REGS */
-
-#if CTX_INCLUDE_PAUTH_REGS
 	size_t pauth_total = 0U;
-	PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
-#endif
+
+	if (is_ctx_pauth_supported()) {
+		PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
+	}
 
 	PRINT_MEM_USAGE_SEPARATOR();
 
@@ -80,9 +81,9 @@
 	printf("|    EL1    ");
 #endif /* CTX_INCLUDE_EL2_REGS */
 
-#if CTX_INCLUDE_PAUTH_REGS
-	printf("|   PAUTH   ");
-#endif
+	if (is_ctx_pauth_supported()) {
+		printf("|   PAUTH   ");
+	}
 
 	printf("|   Other   |   Total   |\n");
 
@@ -96,11 +97,11 @@
 #else
 		size_t el1_size = 0U;
 #endif /* CTX_INCLUDE_EL2_REGS */
-
-#if CTX_INCLUDE_PAUTH_REGS
 		size_t pauth_size = 0U;
-		PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
-#endif
+
+		if (is_ctx_pauth_supported()) {
+			PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
+		}
 
 		PRINT_MEM_USAGE_SEPARATOR();
 
@@ -124,12 +125,12 @@
 		printf("| %8luB ", el1_size);
 #endif /* CTX_INCLUDE_EL2_REGS */
 
-#if CTX_INCLUDE_PAUTH_REGS
-		pauth_size = sizeof(ctx->pauth_ctx);
-		size_other -= pauth_size;
-		pauth_total += pauth_size;
-		printf("| %8luB ", pauth_size);
-#endif
+		if (is_ctx_pauth_supported()) {
+			pauth_size = sizeof(ctx->pauth_ctx);
+			size_other -= pauth_size;
+			pauth_total += pauth_size;
+			printf("| %8luB ", pauth_size);
+		}
 		printf("| %8luB | %8luB |\n", size_other, core_total);
 
 		gp_total += gp_size;
@@ -138,15 +139,15 @@
 		total += core_total;
 	}
 
-#if CTX_INCLUDE_PAUTH_REGS
-	PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
-#endif
+	if (is_ctx_pauth_supported()) {
+		PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
+	}
 
 	PRINT_MEM_USAGE_SEPARATOR();
 
-#if CTX_INCLUDE_PAUTH_REGS
-	PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
-#endif
+	if (is_ctx_pauth_supported()) {
+		PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
+	}
 
 	PRINT_MEM_USAGE_SEPARATOR();
 
@@ -158,15 +159,15 @@
 	printf("| %8luB ", el1_total);
 #endif /* CTX_INCLUDE_EL2_REGS */
 
-#if CTX_INCLUDE_PAUTH_REGS
-	printf("| %8luB ", pauth_total);
-#endif
+	if (is_ctx_pauth_supported()) {
+		printf("| %8luB ", pauth_total);
+	}
 
 	printf("| %8luB | %8luB |\n", other_total, total);
 
-#if CTX_INCLUDE_PAUTH_REGS
-	PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
-#endif
+	if (is_ctx_pauth_supported()) {
+		PRINT_SINGLE_MEM_USAGE_SEP_BLOCK();
+	}
 	PRINT_MEM_USAGE_SEPARATOR();
 	printf("\n");
 
diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk
index 9659e73..d3c2a96 100644
--- a/plat/arm/common/arm_common.mk
+++ b/plat/arm/common/arm_common.mk
@@ -356,7 +356,7 @@
 endif
 
 # Pointer Authentication sources
-ifeq ($(BRANCH_PROTECTION),$(filter $(BRANCH_PROTECTION),1 2 3))
+ifeq ($(BRANCH_PROTECTION),$(filter $(BRANCH_PROTECTION),1 2 3 5))
 PLAT_BL_COMMON_SOURCES	+=	plat/arm/common/aarch64/arm_pauth.c
 endif
 
diff --git a/plat/qemu/common/common.mk b/plat/qemu/common/common.mk
index da981e5..751511c 100644
--- a/plat/qemu/common/common.mk
+++ b/plat/qemu/common/common.mk
@@ -148,7 +148,7 @@
 endif
 
 # Pointer Authentication sources
-ifeq ($(BRANCH_PROTECTION),$(filter $(BRANCH_PROTECTION),1 2 3))
+ifeq ($(BRANCH_PROTECTION),$(filter $(BRANCH_PROTECTION),1 2 3 5))
 PLAT_BL_COMMON_SOURCES	+=	plat/arm/common/aarch64/arm_pauth.c
 endif