Add unit tests for Pointer Authentication

Add unit tests to:
    Test access to the key registers.
    Use the pointer authentication instructions.
    Call psci version and check the EL3 pointer authentication keys
    aren't leaked.
    Make a tsp call and check the secure world keys aren't leaked.

Change-Id: Ic7940757e6f9fc905ccef8c035e0c22b47b35cd7
Signed-off-by: Joel Hutton <Joel.Hutton@Arm.com>
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index 983aed3..2cf6e83 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.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
  */
@@ -7,6 +7,7 @@
 #ifndef __TEST_HELPERS_H__
 #define __TEST_HELPERS_H__
 
+#include <arch_features.h>
 #include <plat_topology.h>
 #include <psci.h>
 #include <spci_svc.h>
@@ -25,6 +26,16 @@
 
 typedef test_result_t (*test_function_arg_t)(void *arg);
 
+#ifdef AARCH32
+#define SKIP_TEST_IF_AARCH32()							\
+	do {									\
+		tftf_testcase_printf("Test not supported on aarch32\n");	\
+		return TEST_RESULT_SKIPPED;					\
+	} while (0)
+#else
+#define SKIP_TEST_IF_AARCH32()
+#endif
+
 #define SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(n)					\
 	do {									\
 		unsigned int clusters_cnt;					\
@@ -77,6 +88,15 @@
 		}								\
 	} while (0)
 
+#define SKIP_TEST_IF_PAUTH_NOT_SUPPORTED()					\
+	do {									\
+		if (!is_armv8_3_pauth_present()) {				\
+			tftf_testcase_printf(					\
+				"Pointer Authentication not supported\n");	\
+			return TEST_RESULT_SKIPPED;				\
+		}								\
+	} while (0)
+
 #define SKIP_TEST_IF_MM_NOT_PRESENT()						\
 	do {									\
 		smc_args version_smc = { MM_VERSION_AARCH32 };			\
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index c5cdc3e..5891c7a 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -48,6 +48,14 @@
 	return (read_id_aa64isar1_el1() & mask) != 0U;
 }
 
+static inline bool is_armv8_3_pauth_gpa_gpi_present(void)
+{
+	uint64_t mask = (ID_AA64ISAR1_GPI_MASK << ID_AA64ISAR1_GPI_SHIFT) |
+		(ID_AA64ISAR1_GPA_MASK << ID_AA64ISAR1_GPA_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 b6afdd2..151e5be 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -445,9 +445,24 @@
 DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1)
 
 /* Armv8.3 Pointer Authentication Registers */
+/* Instruction keys A and B */
 DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeyhi_el1, APIAKeyHi_EL1)
 DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeylo_el1, APIAKeyLo_EL1)
 
+DEFINE_RENAME_SYSREG_RW_FUNCS(apibkeyhi_el1, APIBKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apibkeylo_el1, APIBKeyLo_EL1)
+
+/* Data keys A and B */
+DEFINE_RENAME_SYSREG_RW_FUNCS(apdakeyhi_el1, APDAKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apdakeylo_el1, APDAKeyLo_EL1)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(apdbkeyhi_el1, APDBKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apdbkeylo_el1, APDBKeyLo_EL1)
+
+/* Generic key */
+DEFINE_RENAME_SYSREG_RW_FUNCS(apgakeyhi_el1, APGAKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apgakeylo_el1, APGAKeyLo_EL1)
+
 #define IS_IN_EL(x) \
 	(GET_EL(read_CurrentEl()) == MODE_EL##x)
 
diff --git a/tftf/tests/extensions/ptrauth/test_ptrauth.c b/tftf/tests/extensions/ptrauth/test_ptrauth.c
new file mode 100644
index 0000000..a1f41e2
--- /dev/null
+++ b/tftf/tests/extensions/ptrauth/test_ptrauth.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <psci.h>
+#include <smccc.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+#include <tftf.h>
+#include <tsp.h>
+#include <string.h>
+
+/* The length of the array used to hold the pauth keys */
+#define LENGTH_KEYS 10
+
+#ifndef AARCH32
+static void read_pauth_keys(uint64_t *pauth_keys, unsigned int len)
+{
+	assert(len >= LENGTH_KEYS);
+
+	memset(pauth_keys, 0, len * sizeof(uint64_t));
+
+	if (is_armv8_3_pauth_apa_api_present()) {
+		/* read instruction keys a and b (both 128 bit) */
+		pauth_keys[0] = read_apiakeylo_el1();
+		pauth_keys[1] = read_apiakeyhi_el1();
+
+		pauth_keys[2] = read_apibkeylo_el1();
+		pauth_keys[3] = read_apibkeyhi_el1();
+
+		/* read data keys a and b (both 128 bit) */
+		pauth_keys[4] = read_apdakeylo_el1();
+		pauth_keys[5] = read_apdakeyhi_el1();
+
+		pauth_keys[6] = read_apdbkeylo_el1();
+		pauth_keys[7] = read_apdbkeyhi_el1();
+	}
+
+	if (is_armv8_3_pauth_gpa_gpi_present()) {
+		/* read generic key */
+		pauth_keys[8] = read_apgakeylo_el1();
+		pauth_keys[9] = read_apgakeyhi_el1();
+	}
+
+}
+#endif
+
+/*
+ * TF-A is expected to allow access to key registers from lower EL's,
+ * reading the keys excercises this, on failure this will trap to
+ * EL3 and crash.
+ */
+test_result_t test_pauth_reg_access(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifndef AARCH32
+	uint64_t pauth_keys[LENGTH_KEYS];
+	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
+	read_pauth_keys(pauth_keys, LENGTH_KEYS);
+	return TEST_RESULT_SUCCESS;
+#endif
+}
+
+/*
+ * Makes a call to psci version, and checks that the EL3 pauth keys are not
+ * leaked when it returns
+ */
+#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ > 0)
+__attribute__((target("sign-return-address=all")))
+#endif
+test_result_t test_pauth_leakage(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifndef AARCH32
+	uint64_t pauth_keys_before[LENGTH_KEYS];
+	uint64_t pauth_keys_after[LENGTH_KEYS];
+	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
+
+	read_pauth_keys(pauth_keys_before, LENGTH_KEYS);
+
+	tftf_get_psci_version();
+
+	read_pauth_keys(pauth_keys_after, LENGTH_KEYS);
+
+	if (memcmp(pauth_keys_before, pauth_keys_after,
+			LENGTH_KEYS * sizeof(uint64_t)) != 0)
+		return TEST_RESULT_FAIL;
+	return TEST_RESULT_SUCCESS;
+#endif
+}
+
+/* Uses the pauth instructions, this checks the enable PAUTH bit has been set */
+test_result_t test_pauth_instructions(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifndef AARCH32
+	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
+	/*
+	 * Pointer authentication instructions (explicit encoding for compilers
+	 * that do not recognize these instructions)
+	 */
+	/* paciasp */
+	__asm__ volatile (".inst 0xD503233F");
+	/* autiasp */
+	__asm__ volatile (".inst 0xD50323BF");
+	/* paciasp */
+	__asm__ volatile (".inst 0xD503233F");
+	/* xpaclri */
+	__asm__ volatile (".inst 0xD50320FF");
+	return TEST_RESULT_SUCCESS;
+#endif
+}
+
+/*
+ * Makes a call to TSP ADD, and checks that the checks that the Secure World
+ * pauth keys are not leaked
+ */
+#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ > 0)
+__attribute__((target("sign-return-address=all")))
+#endif
+test_result_t test_pauth_leakage_tsp(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifndef AARCH32
+	smc_args tsp_svc_params;
+	smc_ret_values tsp_result = {0};
+	uint64_t pauth_keys_before[LENGTH_KEYS];
+	uint64_t pauth_keys_after[LENGTH_KEYS];
+
+	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
+	SKIP_TEST_IF_TSP_NOT_PRESENT();
+
+	read_pauth_keys(pauth_keys_before, LENGTH_KEYS);
+
+	/* Standard SMC to ADD two numbers */
+	tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
+	tsp_svc_params.arg1 = 4;
+	tsp_svc_params.arg2 = 6;
+	tsp_result = tftf_smc(&tsp_svc_params);
+
+	/*
+	 * Check the result of the addition-TSP_ADD will add
+	 * the arguments to themselves and return
+	 */
+	if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
+				tsp_result.ret2 != 12) {
+		tftf_testcase_printf("TSP add returned wrong result:"
+				     "got %d %d %d expected: 0 8 12\n",
+						(unsigned int)tsp_result.ret0,
+						(unsigned int)tsp_result.ret1,
+						(unsigned int)tsp_result.ret2);
+
+		return TEST_RESULT_FAIL;
+	}
+	read_pauth_keys(pauth_keys_after, LENGTH_KEYS);
+
+	if (memcmp(pauth_keys_before, pauth_keys_after,
+			LENGTH_KEYS * sizeof(uint64_t)) != 0)
+		return TEST_RESULT_FAIL;
+	return TEST_RESULT_SUCCESS;
+#endif
+}
diff --git a/tftf/tests/tests-cpu-extensions.mk b/tftf/tests/tests-cpu-extensions.mk
index 38271dc..daf9528 100644
--- a/tftf/tests/tests-cpu-extensions.mk
+++ b/tftf/tests/tests-cpu-extensions.mk
@@ -10,4 +10,5 @@
 	extensions/sve/test_sve.c					\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_1.c		\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_2.c		\
+	extensions/ptrauth/test_ptrauth.c				\
 )
diff --git a/tftf/tests/tests-cpu-extensions.xml b/tftf/tests/tests-cpu-extensions.xml
index 666d5a0..4c3ad43 100644
--- a/tftf/tests/tests-cpu-extensions.xml
+++ b/tftf/tests/tests-cpu-extensions.xml
@@ -12,6 +12,10 @@
     <testcase name="AMUv1 non-zero counters" function="test_amu_nonzero_ctr" />
     <testcase name="AMUv1 suspend/resume" function="test_amu_suspend_resume" />
     <testcase name="SVE support" function="test_sve_support" />
+    <testcase name="Access Pointer Authentication Registers" function="test_pauth_reg_access" />
+    <testcase name="Use Pointer Authentication Instructions" function="test_pauth_instructions" />
+    <testcase name="Check for Pointer Authentication key leakage from EL3" function="test_pauth_leakage" />
+    <testcase name="Check for Pointer Authentication key leakage from TSP" function="test_pauth_leakage_tsp" />
   </testsuite>
 
   <testsuite name="ARM_ARCH_SVC" description="Arm Architecture Service tests">