Add ARMv8.3 pointer authentication support

ARMv8.3-PAuth adds functionality that supports address authentication of
the contents of a register before that register is used as the target of
an indirect branch, or as a load.

This feature is supported only in AArch64 state.

This feature is mandatory in ARMv8.3 implementations.

This patch adds the functionality needed for platforms to provide
authentication keys for the TF-A Test Framework, and a new option
(ENABLE_PAUTH) to enable pointer authentication in the framework itself.
This option is disabled by default.

Pointer authentication support has been added to FVP.

Change-Id: Id2d5c978deb68ae60107879f1c3d0b231cba9f42
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
diff --git a/Makefile b/Makefile
index 0179bbf..b354433 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -134,6 +134,7 @@
 ################################################################################
 $(eval $(call assert_boolean,DEBUG))
 $(eval $(call assert_boolean,ENABLE_ASSERTIONS))
+$(eval $(call assert_boolean,ENABLE_PAUTH))
 $(eval $(call assert_boolean,FIRMWARE_UPDATE))
 $(eval $(call assert_boolean,FWU_BL_TEST))
 $(eval $(call assert_boolean,NEW_TEST_SESSION))
@@ -148,6 +149,7 @@
 $(eval $(call add_define,TFTF_DEFINES,ARM_ARCH_MINOR))
 $(eval $(call add_define,TFTF_DEFINES,DEBUG))
 $(eval $(call add_define,TFTF_DEFINES,ENABLE_ASSERTIONS))
+$(eval $(call add_define,TFTF_DEFINES,ENABLE_PAUTH))
 $(eval $(call add_define,TFTF_DEFINES,LOG_LEVEL))
 $(eval $(call add_define,TFTF_DEFINES,NEW_TEST_SESSION))
 $(eval $(call add_define,TFTF_DEFINES,PLAT_${PLAT}))
@@ -210,6 +212,10 @@
 TFTF_ASFLAGS		+= ${COMMON_ASFLAGS}
 TFTF_LDFLAGS		+= ${COMMON_LDFLAGS}
 
+ifeq (${ENABLE_PAUTH},1)
+TFTF_CFLAGS		+=	-msign-return-address=non-leaf
+endif
+
 NS_BL1U_SOURCES		+= ${PLAT_SOURCES} ${LIBC_SRCS}
 NS_BL1U_INCLUDES	+= ${PLAT_INCLUDES}
 NS_BL1U_CFLAGS		+= ${COMMON_CFLAGS}
diff --git a/defaults.mk b/defaults.mk
index 0f74652..cf90aaf 100644
--- a/defaults.mk
+++ b/defaults.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -26,6 +26,9 @@
 # Build platform
 DEFAULT_PLAT		:= fvp
 
+# Enable Pointer Authentication support in the TFTF
+ENABLE_PAUTH		:= 0
+
 # Whether the Firmware Update images (i.e. NS_BL1U and NS_BL2U images) should be
 # built. The platform makefile is free to override this value.
 FIRMWARE_UPDATE		:= 0
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index 9c0812f..0decf1a 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -361,6 +361,20 @@
 Common implementation of this function is provided in
 ``plat/common/aarch64/platform_mp_stack.S``.
 
+Function : plat_init_apiakey
+````````````````````````````
+
+::
+
+    Argument : void
+    Return   : uint64_t *
+
+This function returns a pointer to an array with the values used to set the
+``APIAKey{Hi,Lo}_EL1`` registers.
+
+This function is only needed if ARMv8.3 pointer authentication is used by
+building with ``ENABLE_PAUTH=1``.
+
 Function : tftf_platform_end()
 ``````````````````````````````
 
@@ -433,7 +447,7 @@
 
 --------------
 
-*Copyright (c) 2018, Arm Limited. All rights reserved.*
+*Copyright (c) 2018-2019, Arm Limited. All rights reserved.*
 
 .. _docs/psci-pd-tree.rst: https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/psci-pd-tree.rst
 .. _SP805: https://static.docs.arm.com/ddi0270/b/DDI0270.pdf
diff --git a/docs/user-guide.rst b/docs/user-guide.rst
index c9559d2..5bfab79 100644
--- a/docs/user-guide.rst
+++ b/docs/user-guide.rst
@@ -362,6 +362,11 @@
 TFTF build options
 ''''''''''''''''''
 
+-  ``ENABLE_PAUTH``: Boolean option to enable ARMv8.3 Pointer Authentication
+   (``ARMv8.3-PAuth``) support in the Trusted Firmware-A Test Framework itself.
+   If enabled, it is needed to use a compiler that supports the option
+   ``-msign-return-address``. It defaults to 0.
+
 -  ``NEW_TEST_SESSION``: Choose whether a new test session should be started
    every time or whether the framework should determine whether a previous
    session was interrupted and resume it. It can take either 1 (always
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 39b2a42..c4ec4ff 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -154,26 +154,22 @@
 
 #define ID_AA64PFR0_GIC_SHIFT	U(24)
 #define ID_AA64PFR0_GIC_WIDTH	U(4)
-#define ID_AA64PFR0_GIC_MASK	((ULL(1) << ID_AA64PFR0_GIC_WIDTH) - ULL(1))
+#define ID_AA64PFR0_GIC_MASK	ULL(0xf)
 
 /* ID_AA64ISAR1_EL1 definitions */
+#define ID_AA64ISAR1_EL1	S3_0_C0_C6_1
 #define ID_AA64ISAR1_GPI_SHIFT	U(28)
 #define ID_AA64ISAR1_GPI_WIDTH	U(4)
+#define ID_AA64ISAR1_GPI_MASK	ULL(0xf)
 #define ID_AA64ISAR1_GPA_SHIFT	U(24)
 #define ID_AA64ISAR1_GPA_WIDTH	U(4)
+#define ID_AA64ISAR1_GPA_MASK	ULL(0xf)
 #define ID_AA64ISAR1_API_SHIFT	U(8)
 #define ID_AA64ISAR1_API_WIDTH	U(4)
+#define ID_AA64ISAR1_API_MASK	ULL(0xf)
 #define ID_AA64ISAR1_APA_SHIFT	U(4)
 #define ID_AA64ISAR1_APA_WIDTH	U(4)
-
-#define ID_AA64ISAR1_GPI_MASK \
-	(((ULL(1) << ID_AA64ISAR1_GPI_WIDTH) - ULL(1)) << ID_AA64ISAR1_GPI_SHIFT)
-#define ID_AA64ISAR1_GPA_MASK \
-	(((ULL(1) << ID_AA64ISAR1_GPA_WIDTH) - ULL(1)) << ID_AA64ISAR1_GPA_SHIFT)
-#define ID_AA64ISAR1_API_MASK \
-	(((ULL(1) << ID_AA64ISAR1_API_WIDTH) - ULL(1)) << ID_AA64ISAR1_API_SHIFT)
-#define ID_AA64ISAR1_APA_MASK \
-	(((ULL(1) << ID_AA64ISAR1_APA_WIDTH) - ULL(1)) << ID_AA64ISAR1_APA_SHIFT)
+#define ID_AA64ISAR1_APA_MASK	ULL(0xf)
 
 /* ID_AA64MMFR0_EL1 definitions */
 #define ID_AA64MMFR0_EL1_PARANGE_SHIFT	U(0)
@@ -258,9 +254,7 @@
 #define SCTLR_E0E_BIT		(ULL(1) << 24)
 #define SCTLR_EE_BIT		(ULL(1) << 25)
 #define SCTLR_UCI_BIT		(ULL(1) << 26)
-#define SCTLR_TRE_BIT		(ULL(1) << 28)
-#define SCTLR_AFE_BIT		(ULL(1) << 29)
-#define SCTLR_TE_BIT		(ULL(1) << 30)
+#define SCTLR_EnIA_BIT		(ULL(1) << 31)
 #define SCTLR_DSSBS_BIT		(ULL(1) << 44)
 #define SCTLR_RESET_VAL		SCTLR_EL3_RES1
 
@@ -822,7 +816,16 @@
 /*******************************************************************************
  * Armv8.3 Pointer Authentication Registers
  ******************************************************************************/
+#define APIAKeyLo_EL1		S3_0_C2_C1_0
+#define APIAKeyHi_EL1		S3_0_C2_C1_1
+#define APIBKeyLo_EL1		S3_0_C2_C1_2
+#define APIBKeyHi_EL1		S3_0_C2_C1_3
+#define APDAKeyLo_EL1		S3_0_C2_C2_0
+#define APDAKeyHi_EL1		S3_0_C2_C2_1
+#define APDBKeyLo_EL1		S3_0_C2_C2_2
+#define APDBKeyHi_EL1		S3_0_C2_C2_3
 #define APGAKeyLo_EL1		S3_0_C2_C3_0
+#define APGAKeyHi_EL1		S3_0_C2_C3_1
 
 /*******************************************************************************
  * Armv8.4 Data Independent Timing Registers
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index da8b6e4..6af1d03 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -23,6 +23,25 @@
 		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_apa_api_present(void)
+{
+	uint64_t mask = (ID_AA64ISAR1_API_MASK << ID_AA64ISAR1_API_SHIFT) |
+			(ID_AA64ISAR1_APA_MASK << ID_AA64ISAR1_APA_SHIFT);
+
+	return (read_id_aa64isar1_el1() & 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/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 750bbbd..cdb01f0 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -444,7 +444,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/plat/common/platform.h b/include/plat/common/platform.h
index 3f452ad..b30c41e 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -67,6 +67,9 @@
 /* Gets a handle for the initialised IO entity */
 void plat_get_nvm_handle(uintptr_t *handle);
 
+/* Initialize and get a pointer to a uint64_t[2] array with a 128-key */
+uint64_t *plat_init_apiakey(void);
+
 /*
  * Returns the platform topology description array. The size of this
  * array should be PLATFORM_NUM_AFFS - PLATFORM_CORE_COUNT + 1.
diff --git a/plat/arm/fvp/platform.mk b/plat/arm/fvp/platform.mk
index 0230124..9fb84c2 100644
--- a/plat/arm/fvp/platform.mk
+++ b/plat/arm/fvp/platform.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -23,4 +23,8 @@
 # Firmware update is implemented on FVP.
 FIRMWARE_UPDATE := 1
 
+ifeq (${ARCH},aarch64)
+PLAT_SOURCES	+=	plat/common/aarch64/pauth.c
+endif
+
 include plat/arm/common/arm_common.mk
diff --git a/plat/common/aarch64/pauth.c b/plat/common/aarch64/pauth.c
new file mode 100644
index 0000000..7555740
--- /dev/null
+++ b/plat/common/aarch64/pauth.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cdefs.h>
+#include <stdint.h>
+
+/*
+ * Instruction pointer authentication key A. The low 64-bit are at [0], and the
+ * high bits at [1].
+ */
+static uint64_t plat_apiakey[2];
+
+/*
+ * This is only a toy implementation to generate a seemingly random 128-bit key
+ * from sp and x30 values.
+ */
+uint64_t *plat_init_apiakey(void)
+{
+	uintptr_t return_addr = (uintptr_t)__builtin_return_address(0U);
+	uintptr_t frame_addr = (uintptr_t)__builtin_frame_address(0U);
+
+	plat_apiakey[0] = (return_addr << 13) ^ frame_addr;
+	plat_apiakey[1] = (frame_addr << 15) ^ return_addr;
+
+	return plat_apiakey;
+}
diff --git a/tftf/framework/main.c b/tftf/framework/main.c
index 67f565d..e84e450 100644
--- a/tftf/framework/main.c
+++ b/tftf/framework/main.c
@@ -1,10 +1,11 @@
 /*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <arch_helpers.h>
+#include <arch_features.h>
 #include <assert.h>
 #include <debug.h>
 #include <drivers/arm/arm_gic.h>
@@ -518,6 +519,30 @@
 #endif
 
 	tftf_arch_setup();
+
+	/*
+	 * Enable pointer authentication. tftf_cold_boot_main() never returns,
+	 * so it is safe to do it here. If this function was to return, the
+	 * authentication would fail then.
+	 */
+#if ENABLE_PAUTH
+	assert(is_armv8_3_pauth_apa_api_present());
+
+	uint64_t *apiakey = plat_init_apiakey();
+
+	write_apiakeylo_el1(apiakey[0]);
+	write_apiakeyhi_el1(apiakey[1]);
+
+	if (IS_IN_EL2()) {
+		write_sctlr_el2(read_sctlr_el2() | SCTLR_EnIA_BIT);
+	} else {
+		assert(IS_IN_EL1());
+		write_sctlr_el1(read_sctlr_el1() | SCTLR_EnIA_BIT);
+	}
+
+	isb();
+#endif /* ENABLE_PAUTH */
+
 	tftf_platform_setup();
 	tftf_init_topology();