refactor(cpufeat): convert FEAT_PAuth setup to C
An oversimplified view of FEAT_PAuth is that it's a symmetric encryption
of the LR. PAC instructions execute as NOPs until explicitly turned on.
So in a function that turns PAuth on, the signing would have executed as
a NOP and the authentication will encrypt the address, leading to a
failure. That's why enablement is in assembly - we have full control of
when pointer authentications happen.
However, assembly is hard to read, is opaque to the compiler for
optimisations, and we need to call into C anyway for the platform hook
to get the key. So convert it to C. We can instruct the compiler to not
generate branch protection for the enable function only and as long as
the caller doesn't do branch protection (and all callers are entrypoints
written in assembly) everything will work.
Change-Id: I8917a26e1293033c910e3058664e3ca9207359b7
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/include/lib/extensions/pauth.h b/include/lib/extensions/pauth.h
index dbc2226..db47b80 100644
--- a/include/lib/extensions/pauth.h
+++ b/include/lib/extensions/pauth.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2019-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -7,12 +7,34 @@
#ifndef PAUTH_H
#define PAUTH_H
-/*******************************************************************************
- * ARMv8.3-PAuth support functions
- ******************************************************************************/
+#if ENABLE_PAUTH
+/* Platform hook to generate the APIAKey */
+uint128_t plat_init_apkey(void);
-/* Disable ARMv8.3 pointer authentication in EL1/EL3 */
+void pauth_init(void);
+void pauth_enable_el1(void);
+void pauth_enable_el3(void);
+void pauth_enable_el2(void);
void pauth_disable_el1(void);
void pauth_disable_el3(void);
-
+#else
+static inline void pauth_init(void)
+{
+}
+static inline void pauth_enable_el1(void)
+{
+}
+static inline void pauth_enable_el3(void)
+{
+}
+static inline void pauth_enable_el2(void)
+{
+}
+static inline void pauth_disable_el1(void)
+{
+}
+static inline void pauth_disable_el3(void)
+{
+}
+#endif
#endif /* PAUTH_H */
diff --git a/include/lib/libc/cdefs.h b/include/lib/libc/cdefs.h
index 5febe9d..3bd4a70 100644
--- a/include/lib/libc/cdefs.h
+++ b/include/lib/libc/cdefs.h
@@ -17,6 +17,7 @@
#define __section(x) __attribute__((__section__(x)))
#define __fallthrough __attribute__((__fallthrough__))
#define __noinline __attribute__((__noinline__))
+#define __no_pauth __attribute__((target("branch-protection=none")))
#if RECLAIM_INIT_CODE
/*
* Add each function to a section that is unique so the functions can still
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index 6d8a06a..098506a 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -61,6 +61,9 @@
#define HI(addr) (addr >> 32)
#define LO(addr) (addr & 0xffffffff)
+#define HI_64(addr) (addr >> 64)
+#define LO_64(addr) (addr & 0xffffffffffffffff)
+
/*
* This variant of div_round_up can be used in macro definition but should not
* be used in C code as the `div` parameter is evaluated twice.
diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c
index c155329..0cfabb3 100644
--- a/lib/el3_runtime/aarch64/context_mgmt.c
+++ b/lib/el3_runtime/aarch64/context_mgmt.c
@@ -30,6 +30,7 @@
#include <lib/extensions/fgt2.h>
#include <lib/extensions/fpmr.h>
#include <lib/extensions/mpam.h>
+#include <lib/extensions/pauth.h>
#include <lib/extensions/pmuv3.h>
#include <lib/extensions/sme.h>
#include <lib/extensions/spe.h>
@@ -844,20 +845,6 @@
#endif /* IMAGE_BL31 */
}
-/* TODO: move to lib/extensions/pauth when it has been ported to FEAT_STATE */
-static __unused void enable_pauth_el2(void)
-{
- u_register_t hcr_el2 = read_hcr_el2();
- /*
- * For Armv8.3 pointer authentication feature, disable traps to EL2 when
- * accessing key registers or using pointer authentication instructions
- * from lower ELs.
- */
- hcr_el2 |= (HCR_API_BIT | HCR_APK_BIT);
-
- write_hcr_el2(hcr_el2);
-}
-
#if INIT_UNUSED_NS_EL2
/*******************************************************************************
* Enable architecture extensions in-place at EL2 on first entry to Non-secure
@@ -904,9 +891,9 @@
write_hcrx_el2(read_hcrx_el2() | HCRX_EL2_MSCEn_BIT);
}
-#if ENABLE_PAUTH
- enable_pauth_el2();
-#endif /* ENABLE_PAUTH */
+ if (is_feat_pauth_supported()) {
+ pauth_enable_el2();
+ }
#endif /* IMAGE_BL31 */
}
#endif /* INIT_UNUSED_NS_EL2 */
diff --git a/lib/extensions/pauth/pauth.c b/lib/extensions/pauth/pauth.c
new file mode 100644
index 0000000..c6c6e10
--- /dev/null
+++ b/lib/extensions/pauth/pauth.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <arch.h>
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <lib/extensions/pauth.h>
+
+void __no_pauth pauth_init_enable_el3(void)
+{
+ if (is_feat_pauth_supported()) {
+ pauth_init();
+ pauth_enable_el3();
+ }
+}
+
+void __no_pauth pauth_init_enable_el1(void)
+{
+ if (is_feat_pauth_supported()) {
+ pauth_init();
+ pauth_enable_el1();
+ }
+}
+
+void pauth_init(void)
+{
+ uint128_t keys = plat_init_apkey();
+ uint64_t key_lo = LO_64(keys);
+ uint64_t key_hi = HI_64(keys);
+
+ /* Program instruction key A used by the Trusted Firmware */
+ write_apiakeylo_el1(key_lo);
+ write_apiakeyhi_el1(key_hi);
+}
+
+/*
+ * Begin checking function calls at the current EL. This function must not have
+ * PAuth guards because the signing will be a NOP and attempting to authenticate
+ * will fail. Includes an ISB to avoid accidental failures.
+ */
+void __no_pauth pauth_enable_el3(void)
+{
+ write_sctlr_el3(read_sctlr_el3() | SCTLR_EnIA_BIT);
+ isb();
+}
+
+void __no_pauth pauth_enable_el1(void)
+{
+ write_sctlr_el1(read_sctlr_el1() | SCTLR_EnIA_BIT);
+ isb();
+}
+
+/* Enable PAuth for EL2 */
+void pauth_enable_el2(void)
+{
+ u_register_t hcr_el2 = read_hcr_el2();
+ /*
+ * For Armv8.3 pointer authentication feature, disable traps to EL2 when
+ * accessing key registers or using pointer authentication instructions
+ * from lower ELs.
+ */
+ hcr_el2 |= (HCR_API_BIT | HCR_APK_BIT);
+
+ write_hcr_el2(hcr_el2);
+}
+
+void __no_pauth pauth_disable_el1(void)
+{
+ write_sctlr_el1(read_sctlr_el1() & ~SCTLR_EnIA_BIT);
+ isb(); /* usually called by caller, here it's for compatibility */
+}
+
+void __no_pauth pauth_disable_el3(void)
+{
+ write_sctlr_el3(read_sctlr_el3() & ~SCTLR_EnIA_BIT);
+ isb(); /* usually called by caller, here it's for compatibility */
+}
diff --git a/lib/extensions/pauth/pauth_helpers.S b/lib/extensions/pauth/pauth_helpers.S
index 1f0e2e0..f9777bb 100644
--- a/lib/extensions/pauth/pauth_helpers.S
+++ b/lib/extensions/pauth/pauth_helpers.S
@@ -8,86 +8,10 @@
#include <asm_macros.S>
#include <lib/el3_runtime/cpu_data.h>
- .global pauth_init_enable_el1
- .global pauth_disable_el1
- .global pauth_init_enable_el3
- .global pauth_disable_el3
.globl pauth_load_bl31_apiakey
.globl pauth_load_bl1_apiakey_enable
/* -------------------------------------------------------------
- * Program APIAKey_EL1 and enable pointer authentication in EL1
- * -------------------------------------------------------------
- */
-func pauth_init_enable_el1
- stp x29, x30, [sp, #-16]!
-
- /* Initialize platform key */
- bl plat_init_apkey
-
- /* Program instruction key A used by the Trusted Firmware */
- msr APIAKeyLo_EL1, x0
- msr APIAKeyHi_EL1, x1
-
- /* Enable pointer authentication */
- mrs x0, sctlr_el1
- orr x0, x0, #SCTLR_EnIA_BIT
- msr sctlr_el1, x0
- isb
-
- ldp x29, x30, [sp], #16
- ret
-endfunc pauth_init_enable_el1
-
-/* -------------------------------------------------------------
- * Disable pointer authentication in EL1
- * -------------------------------------------------------------
- */
-func pauth_disable_el1
- mrs x0, sctlr_el1
- bic x0, x0, #SCTLR_EnIA_BIT
- msr sctlr_el1, x0
- isb
- ret
-endfunc pauth_disable_el1
-
-/* -------------------------------------------------------------
- * Program APIAKey_EL1 and enable pointer authentication in EL3
- * -------------------------------------------------------------
- */
-func pauth_init_enable_el3
- stp x29, x30, [sp, #-16]!
-
- /* Initialize platform key */
- bl plat_init_apkey
-
- /* Program instruction key A used by the Trusted Firmware */
- msr APIAKeyLo_EL1, x0
- msr APIAKeyHi_EL1, x1
-
- /* Enable pointer authentication */
- mrs x0, sctlr_el3
- orr x0, x0, #SCTLR_EnIA_BIT
- msr sctlr_el3, x0
- isb
-
- ldp x29, x30, [sp], #16
- ret
-endfunc pauth_init_enable_el3
-
-/* -------------------------------------------------------------
- * Disable pointer authentication in EL3
- * -------------------------------------------------------------
- */
-func pauth_disable_el3
- mrs x0, sctlr_el3
- bic x0, x0, #SCTLR_EnIA_BIT
- msr sctlr_el3, x0
- isb
- ret
-endfunc pauth_disable_el3
-
-/* -------------------------------------------------------------
* The following functions strictly follow the AArch64 PCS
* to use x9-x17 (temporary caller-saved registers) to load
* the APIAKey_EL1 and enable pointer authentication.