Add support for pointer authentication

The previous commit added the infrastructure to load and save
ARMv8.3-PAuth registers during Non-secure <-> Secure world switches, but
didn't actually enable pointer authentication in the firmware.

This patch adds the functionality needed for platforms to provide
authentication keys for the firmware, and a new option (ENABLE_PAUTH) to
enable pointer authentication in the firmware itself. This option is
disabled by default, and it requires CTX_INCLUDE_PAUTH_REGS to be
enabled.

Change-Id: I35127ec271e1198d43209044de39fa712ef202a5
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
diff --git a/Makefile b/Makefile
index ebc8850..7b0ef5b 100644
--- a/Makefile
+++ b/Makefile
@@ -184,6 +184,14 @@
 ASFLAGS_aarch32		=	$(march32-directive)
 ASFLAGS_aarch64		=	-march=armv8-a
 
+# Set the compiler to ARMv8.3 mode so that it uses all the ARMv8.3-PAuth
+# instructions. Keeping it in 8.0 would make the compiler emit
+# backwards-compatible hint instructions, which needs more space.
+ifeq (${ENABLE_PAUTH},1)
+TF_CFLAGS_aarch64	+=	-march=armv8.3-a
+ASFLAGS_aarch64		+=	-march=armv8.3-a
+endif
+
 WARNING1 := -Wextra
 WARNING1 += -Wunused -Wno-unused-parameter
 WARNING1 += -Wmissing-declarations
@@ -459,6 +467,15 @@
     endif
 endif
 
+# 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 key used by EL3 to EL1 and S-EL1.
+ifeq ($(ENABLE_PAUTH),1)
+    ifeq ($(CTX_INCLUDE_PAUTH_REGS),0)
+        $(error ENABLE_PAUTH=1 requires CTX_INCLUDE_PAUTH_REGS=1)
+    endif
+endif
+
 ################################################################################
 # Process platform overrideable behaviour
 ################################################################################
@@ -587,6 +604,7 @@
 $(eval $(call assert_boolean,ENABLE_AMU))
 $(eval $(call assert_boolean,ENABLE_ASSERTIONS))
 $(eval $(call assert_boolean,ENABLE_MPAM_FOR_LOWER_ELS))
+$(eval $(call assert_boolean,ENABLE_PAUTH))
 $(eval $(call assert_boolean,ENABLE_PIE))
 $(eval $(call assert_boolean,ENABLE_PMF))
 $(eval $(call assert_boolean,ENABLE_PSCI_STAT))
@@ -639,6 +657,7 @@
 $(eval $(call add_define,ENABLE_AMU))
 $(eval $(call add_define,ENABLE_ASSERTIONS))
 $(eval $(call add_define,ENABLE_MPAM_FOR_LOWER_ELS))
+$(eval $(call add_define,ENABLE_PAUTH))
 $(eval $(call add_define,ENABLE_PIE))
 $(eval $(call add_define,ENABLE_PMF))
 $(eval $(call add_define,ENABLE_PSCI_STAT))
diff --git a/bl31/aarch64/ea_delegate.S b/bl31/aarch64/ea_delegate.S
index d5ecfc5..40c3191 100644
--- a/bl31/aarch64/ea_delegate.S
+++ b/bl31/aarch64/ea_delegate.S
@@ -68,9 +68,13 @@
 	/* Save GP registers */
 	bl	save_gp_registers
 
+	/* Save ARMv8.3-PAuth registers and load firmware key */
 #if CTX_INCLUDE_PAUTH_REGS
 	bl	pauth_context_save
 #endif
+#if ENABLE_PAUTH
+	bl	pauth_load_bl_apiakey
+#endif
 
 	/* Setup exception class and syndrome arguments for platform handler */
 	mov	x0, #ERROR_EA_SYNC
@@ -102,9 +106,13 @@
 	/* Save GP registers */
 	bl	save_gp_registers
 
+	/* Save ARMv8.3-PAuth registers and load firmware key */
 #if CTX_INCLUDE_PAUTH_REGS
 	bl	pauth_context_save
 #endif
+#if ENABLE_PAUTH
+	bl	pauth_load_bl_apiakey
+#endif
 
 	/* Setup exception class and syndrome arguments for platform handler */
 	mov	x0, #ERROR_EA_ASYNC
diff --git a/bl31/aarch64/runtime_exceptions.S b/bl31/aarch64/runtime_exceptions.S
index cea7a8a..aa9d007 100644
--- a/bl31/aarch64/runtime_exceptions.S
+++ b/bl31/aarch64/runtime_exceptions.S
@@ -123,9 +123,13 @@
 
 	bl	save_gp_registers
 
+	/* Save ARMv8.3-PAuth registers and load firmware key */
 #if CTX_INCLUDE_PAUTH_REGS
 	bl	pauth_context_save
 #endif
+#if ENABLE_PAUTH
+	bl	pauth_load_bl_apiakey
+#endif
 
 	/* Save the EL3 system registers needed to return from this exception */
 	mrs	x0, spsr_el3
@@ -331,9 +335,13 @@
 	/* Save general purpose registers */
 	bl	save_gp_registers
 
+	/* Save ARMv8.3-PAuth registers and load firmware key */
 #if CTX_INCLUDE_PAUTH_REGS
 	bl	pauth_context_save
 #endif
+#if ENABLE_PAUTH
+	bl	pauth_load_bl_apiakey
+#endif
 
 	/*
 	 * Populate the parameters for the SMC handler.
diff --git a/common/bl_common.c b/common/bl_common.c
index 84ff99c..4e76dd3 100644
--- a/common/bl_common.c
+++ b/common/bl_common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -9,6 +9,7 @@
 #include <string.h>
 
 #include <arch.h>
+#include <arch_features.h>
 #include <arch_helpers.h>
 #include <common/bl_common.h>
 #include <common/debug.h>
@@ -243,3 +244,53 @@
 #endif
 #undef PRINT_IMAGE_ARG
 }
+
+#ifdef AARCH64
+/*******************************************************************************
+ * Handle all possible cases regarding ARMv8.3-PAuth.
+ ******************************************************************************/
+void bl_handle_pauth(void)
+{
+#if ENABLE_PAUTH
+	/*
+	 * ENABLE_PAUTH = 1 && CTX_INCLUDE_PAUTH_REGS = 1
+	 *
+	 * Check that the system supports address authentication to avoid
+	 * getting an access fault when accessing the registers. This is all
+	 * that is needed to check. If any of the authentication mechanisms is
+	 * supported, the system knows about ARMv8.3-PAuth, so all the registers
+	 * are available and accessing them won't generate a fault.
+	 *
+	 * Obtain 128-bit instruction key A from the platform and save it to the
+	 * system registers. Pointer authentication can't be enabled here or the
+	 * authentication will fail when returning from this function.
+	 */
+	assert(is_armv8_3_pauth_api_present());
+
+	uint64_t *apiakey = plat_init_apiakey();
+
+	write_apiakeylo_el1(apiakey[0]);
+	write_apiakeyhi_el1(apiakey[1]);
+#else /* if !ENABLE_PAUTH */
+
+# if CTX_INCLUDE_PAUTH_REGS
+	/*
+	 * ENABLE_PAUTH = 0 && CTX_INCLUDE_PAUTH_REGS = 1
+	 *
+	 * Assert that the ARMv8.3-PAuth registers are present or an access
+	 * fault will be triggered when they are being saved or restored.
+	 */
+	assert(is_armv8_3_pauth_present());
+# else
+	/*
+	 * ENABLE_PAUTH = 0 && CTX_INCLUDE_PAUTH_REGS = 0
+	 *
+	 * Pointer authentication is allowed in the Non-secure world, but
+	 * prohibited in the Secure world. The Trusted Firmware doesn't save the
+	 * registers during a world switch. No check needed.
+	 */
+# endif /* CTX_INCLUDE_PAUTH_REGS */
+
+#endif /* ENABLE_PAUTH */
+}
+#endif /* AARCH64 */
diff --git a/docs/firmware-design.rst b/docs/firmware-design.rst
index 808afee..ead7297 100644
--- a/docs/firmware-design.rst
+++ b/docs/firmware-design.rst
@@ -2566,6 +2566,11 @@
    must be set to 1. This will add all pointer authentication system registers
    to the context that is saved when doing a world switch.
 
+   The Trusted Firmware itself has support for pointer authentication at runtime
+   that can be enabled by setting both options ``ENABLE_PAUTH`` and
+   ``CTX_INCLUDE_PAUTH_REGS`` to 1. This enables pointer authentication in BL1,
+   BL2, BL31, and the TSP if it is used.
+
 Armv7-A
 ~~~~~~~
 
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index 7a3963b..c3df389 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -1792,6 +1792,22 @@
 On DynamIQ systems, this function must not use stack while enabling MMU, which
 is how the function in xlat table library version 2 is implemented.
 
+Function : plat_init_apiakey [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+    Argument : void
+    Return   : uint64_t *
+
+This function populates the ``plat_apiakey`` array that contains the values used
+to set the ``APIAKey{Hi,Lo}_EL1`` registers. It returns a pointer to this array.
+
+The value should be obtained from a reliable source of randomness.
+
+This function is only needed if ARMv8.3 pointer authentication is used in the
+Trusted Firmware by building with ``ENABLE_PAUTH=1``.
+
 Function : plat_get_syscnt_freq2() [mandatory]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/user-guide.rst b/docs/user-guide.rst
index 70c1d5a..b420127 100644
--- a/docs/user-guide.rst
+++ b/docs/user-guide.rst
@@ -362,7 +362,7 @@
    the ARMv8.3-PAuth registers to be included when saving and restoring the CPU
    context. Note that if the hardware supports this extension and this option is
    set to 0 the value of the registers will be leaked between Secure and
-   Non-secure worlds. The default is 0.
+   Non-secure worlds if PAuth is used on both sides. The default is 0.
 
 -  ``DEBUG``: Chooses between a debug and release build. It can take either 0
    (release) or 1 (debug) as values. 0 is the default.
@@ -411,6 +411,13 @@
    partitioning in EL3, however. Platform initialisation code should configure
    and use partitions in EL3 as required. This option defaults to ``0``.
 
+-  ``ENABLE_PAUTH``: Boolean option to enable ARMv8.3 Pointer Authentication
+   (``ARMv8.3-PAuth``) support in the Trusted Firmware itself. Note that this
+   option doesn't affect the saving of the registers introduced with this
+   extension, they are always saved if they are detected regardless of the value
+   of this option. If enabled, it is needed to use a compiler that supports the
+   option ``-msign-return-address``. It defaults to 0.
+
 -  ``ENABLE_PIE``: Boolean option to enable Position Independent Executable(PIE)
    support within generic code in TF-A. This option is currently only supported
    in BL31. Default is 0.
diff --git a/include/arch/aarch64/arch_features.h b/include/arch/aarch64/arch_features.h
index da8b6e4..495ecb3 100644
--- a/include/arch/aarch64/arch_features.h
+++ b/include/arch/aarch64/arch_features.h
@@ -23,6 +23,23 @@
 		ID_AA64MMFR2_EL1_CNP_MASK) != 0U;
 }
 
+static inline bool is_armv8_3_pauth_present(void)
+{
+	uint64_t mask = (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);
+
+	/* If any of the fields is not zero, PAuth is present */
+	return (read_id_aa64isar1_el1() & mask) != 0U;
+}
+
+static inline bool is_armv8_3_pauth_api_present(void)
+{
+	return ((read_id_aa64isar1_el1() >> ID_AA64ISAR1_API_SHIFT) &
+		ID_AA64ISAR1_API_MASK) != 0U;
+}
+
 static inline bool is_armv8_4_ttst_present(void)
 {
 	return ((read_id_aa64mmfr2_el1() >> ID_AA64MMFR2_EL1_ST_SHIFT) &
diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h
index 4e459bb..e07db30 100644
--- a/include/arch/aarch64/arch_helpers.h
+++ b/include/arch/aarch64/arch_helpers.h
@@ -454,7 +454,8 @@
 DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1)
 
 /* Armv8.3 Pointer Authentication Registers */
-DEFINE_RENAME_SYSREG_RW_FUNCS(apgakeylo_el1, APGAKeyLo_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeyhi_el1, APIAKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeylo_el1, APIAKeyLo_EL1)
 
 #define IS_IN_EL(x) \
 	(GET_EL(read_CurrentEl()) == MODE_EL##x)
diff --git a/include/common/bl_common.h b/include/common/bl_common.h
index fd7656eb..9817ec7 100644
--- a/include/common/bl_common.h
+++ b/include/common/bl_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -207,6 +207,8 @@
 void setup_page_tables(const struct mmap_region *bl_regions,
 			   const struct mmap_region *plat_regions);
 
+void bl_handle_pauth(void);
+
 #endif /*__ASSEMBLY__*/
 
 #endif /* BL_COMMON_H */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index 13767ff..4832e49 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -104,6 +104,7 @@
 void bl2_plat_preload_setup(void);
 int plat_try_next_boot_source(void);
 int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size);
+uint64_t *plat_init_apiakey(void);
 
 /*******************************************************************************
  * Mandatory BL1 functions
diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S
index f5fed72..4489e90 100644
--- a/lib/el3_runtime/aarch64/context.S
+++ b/lib/el3_runtime/aarch64/context.S
@@ -18,6 +18,9 @@
 	.global	pauth_context_restore
 	.global	pauth_context_save
 #endif
+#if ENABLE_PAUTH
+	.global	pauth_load_bl_apiakey
+#endif
 	.global	save_gp_registers
 	.global	restore_gp_registers
 	.global	restore_gp_registers_eret
@@ -374,6 +377,26 @@
 #endif /* CTX_INCLUDE_PAUTH_REGS */
 
 /* -----------------------------------------------------
+ * The following function strictly follows the AArch64
+ * PCS to use x9-x17 (temporary caller-saved registers)
+ * to load the APIA key used by the firmware.
+ * -----------------------------------------------------
+ */
+#if ENABLE_PAUTH
+func pauth_load_bl_apiakey
+	/* Load instruction key A used by the Trusted Firmware. */
+	adrp	x11, plat_apiakey
+	add	x11, x11, :lo12:plat_apiakey
+	ldp	x9, x10, [x11, #0]
+
+	msr	APIAKeyLo_EL1, x9
+	msr	APIAKeyHi_EL1, x10
+
+	ret
+endfunc pauth_load_bl_apiakey
+#endif /* ENABLE_PAUTH */
+
+/* -----------------------------------------------------
  * The following functions are used to save and restore
  * all the general purpose registers. Ideally we would
  * only save and restore the callee saved registers when
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 6422397..819abcd 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -87,6 +87,9 @@
 # Flag to enable exception handling in EL3
 EL3_EXCEPTION_HANDLING		:= 0
 
+# Flag to enable Pointer Authentication
+ENABLE_PAUTH			:= 0
+
 # Build flag to treat usage of deprecated platform and framework APIs as error.
 ERROR_DEPRECATED		:= 0