TFTF: ARMv8.3-PAuth test code enhancements

This patch provides the following feature and makes
modification listed below:
- ARMv8.3-PAuth tests now check for all keys being in use
 (e.g. APIAKey when the test suite is built with
 `ENABLE_PAUTH=1` option) and program new key values otherwise.

Signed-off-by: Alexei Fedorov <Alexei.Fedorov@arm.com>
Change-Id: Ifa4a288274822029da585073563c68a1434f5de7
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index 00bb84a..34601c2 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.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
 #
@@ -73,6 +73,13 @@
 
 FRAMEWORK_SOURCES	+=	${COMPILER_RT_SRCS}
 
+ifeq (${ARCH},aarch64)
+# ARMv8.3 Pointer Authentication support files
+FRAMEWORK_SOURCES	+=						\
+	lib/extensions/pauth/aarch64/pauth.c				\
+	lib/extensions/pauth/aarch64/pauth_helpers.S
+endif
+
 TFTF_LINKERFILE		:=	tftf/framework/tftf.ld.S
 
 
diff --git a/tftf/tests/extensions/pauth/test_pauth.c b/tftf/tests/extensions/pauth/test_pauth.c
index 564c56d..30b78ef 100644
--- a/tftf/tests/extensions/pauth/test_pauth.c
+++ b/tftf/tests/extensions/pauth/test_pauth.c
@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <pauth.h>
 #include <psci.h>
 #include <smccc.h>
 #include <test_helpers.h>
@@ -12,40 +13,156 @@
 #include <tsp.h>
 #include <string.h>
 
-/* The length of the array used to hold the pauth keys */
-#define LENGTH_KEYS 10
+#ifdef __aarch64__
 
-#ifndef AARCH32
-static void read_pauth_keys(uint64_t *pauth_keys, unsigned int len)
+/* Number of ARMv8.3-PAuth keys */
+#define NUM_KEYS	5
+
+static const char * const key_name[] = {"IA", "IB", "DA", "DB", "GA"};
+
+static uint128_t pauth_keys_before[NUM_KEYS];
+static uint128_t pauth_keys_after[NUM_KEYS];
+
+/* Check if ARMv8.3-PAuth key is enabled */
+static bool is_pauth_key_enabled(uint64_t key_bit)
 {
-	assert(len >= LENGTH_KEYS);
+	return (IS_IN_EL2() ?
+		((read_sctlr_el2() & key_bit) != 0U) :
+		((read_sctlr_el1() & key_bit) != 0U));
+}
 
-	memset(pauth_keys, 0, len * sizeof(uint64_t));
+static test_result_t compare_pauth_keys(void)
+{
+	test_result_t result = TEST_RESULT_SUCCESS;
+
+	for (unsigned int i = 0; i < NUM_KEYS; ++i) {
+		if (pauth_keys_before[i] != pauth_keys_after[i]) {
+			ERROR("AP%sKey_EL1 read 0x%llx:%llx "
+			"expected 0x%llx:%llx\n", key_name[i],
+			(uint64_t)(pauth_keys_after[i] >> 64),
+			(uint64_t)(pauth_keys_after[i]),
+			(uint64_t)(pauth_keys_before[i] >> 64),
+			(uint64_t)(pauth_keys_before[i]));
+
+			result = TEST_RESULT_FAIL;
+		}
+	}
+	return result;
+}
+
+/*
+ * Program or read ARMv8.3-PAuth keys (if already enabled)
+ * and store them in <pauth_keys_before> buffer
+ */
+static void set_store_pauth_keys(void)
+{
+	uint128_t plat_key;
+
+	memset(pauth_keys_before, 0, NUM_KEYS * sizeof(uint128_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();
+		if (is_pauth_key_enabled(SCTLR_EnIA_BIT)) {
+			/* Read APIAKey_EL1 */
+			plat_key = read_apiakeylo_el1() |
+				((uint128_t)(read_apiakeyhi_el1()) << 64);
+			INFO("EnIA is set\n");
+		} else {
+			/* Program APIAKey_EL1 */
+			plat_key = init_apkey();
+			write_apiakeylo_el1((uint64_t)plat_key);
+			write_apiakeyhi_el1((uint64_t)(plat_key >> 64));
+		}
+		pauth_keys_before[0] = plat_key;
 
-		pauth_keys[2] = read_apibkeylo_el1();
-		pauth_keys[3] = read_apibkeyhi_el1();
+		if (is_pauth_key_enabled(SCTLR_EnIB_BIT)) {
+			/* Read APIBKey_EL1 */
+			plat_key = read_apibkeylo_el1() |
+				((uint128_t)(read_apibkeyhi_el1()) << 64);
+			INFO("EnIB is set\n");
+		} else {
+			/* Program APIBKey_EL1 */
+			plat_key = init_apkey();
+			write_apibkeylo_el1((uint64_t)plat_key);
+			write_apibkeyhi_el1((uint64_t)(plat_key >> 64));
+		}
+		pauth_keys_before[1] = plat_key;
 
-		/* read data keys a and b (both 128 bit) */
-		pauth_keys[4] = read_apdakeylo_el1();
-		pauth_keys[5] = read_apdakeyhi_el1();
+		if (is_pauth_key_enabled(SCTLR_EnDA_BIT)) {
+			/* Read APDAKey_EL1 */
+			plat_key = read_apdakeylo_el1() |
+				((uint128_t)(read_apdakeyhi_el1()) << 64);
+			INFO("EnDA is set\n");
+		} else {
+			/* Program APDAKey_EL1 */
+			plat_key = init_apkey();
+			write_apdakeylo_el1((uint64_t)plat_key);
+			write_apdakeyhi_el1((uint64_t)(plat_key >> 64));
+		}
+		pauth_keys_before[2] = plat_key;
 
-		pauth_keys[6] = read_apdbkeylo_el1();
-		pauth_keys[7] = read_apdbkeyhi_el1();
+		if (is_pauth_key_enabled(SCTLR_EnDB_BIT)) {
+			/* Read APDBKey_EL1 */
+			plat_key = read_apdbkeylo_el1() |
+				((uint128_t)(read_apdbkeyhi_el1()) << 64);
+			INFO("EnDB is set\n");
+		} else {
+			/* Program APDBKey_EL1 */
+			plat_key = init_apkey();
+			write_apdbkeylo_el1((uint64_t)plat_key);
+			write_apdbkeyhi_el1((uint64_t)(plat_key >> 64));
+		}
+		pauth_keys_before[3] = plat_key;
+	}
+
+	/*
+	 * It is safe to assume that Generic Pointer authentication code key
+	 * APGAKey_EL1 can be re-programmed, as this key is not set in
+	 * TF-A Test suite and PACGA instruction is not used.
+	 */
+	if (is_armv8_3_pauth_gpa_gpi_present()) {
+		/* Program APGAKey_EL1 */
+		plat_key = init_apkey();
+		write_apgakeylo_el1((uint64_t)plat_key);
+		write_apgakeyhi_el1((uint64_t)(plat_key >> 64));
+		pauth_keys_before[4] = plat_key;
+	}
+
+	isb();
+}
+
+/*
+ * Read ARMv8.3-PAuth keys and store them in
+ * <pauth_keys_after> buffer
+ */
+static void read_pauth_keys(void)
+{
+	memset(pauth_keys_after, 0, NUM_KEYS * sizeof(uint128_t));
+
+	if (is_armv8_3_pauth_apa_api_present()) {
+		/* Read APIAKey_EL1 */
+		pauth_keys_after[0] = read_apiakeylo_el1() |
+			((uint128_t)(read_apiakeyhi_el1()) << 64);
+
+		/* Read APIBKey_EL1 */
+		pauth_keys_after[1] = read_apibkeylo_el1() |
+			((uint128_t)(read_apibkeyhi_el1()) << 64);
+
+		/* Read APDAKey_EL1 */
+		pauth_keys_after[2] = read_apdakeylo_el1() |
+			((uint128_t)(read_apdakeyhi_el1()) << 64);
+
+		/* Read APDBKey_EL1 */
+		pauth_keys_after[3] = read_apdbkeylo_el1() |
+			((uint128_t)(read_apdbkeyhi_el1()) << 64);
 	}
 
 	if (is_armv8_3_pauth_gpa_gpi_present()) {
-		/* read generic key */
-		pauth_keys[8] = read_apgakeylo_el1();
-		pauth_keys[9] = read_apgakeyhi_el1();
+		/* Read APGAKey_EL1 */
+		pauth_keys_after[4] = read_apgakeylo_el1() |
+			((uint128_t)(read_apgakeyhi_el1()) << 64);
 	}
-
 }
-#endif
+#endif	/* __aarch64__ */
 
 /*
  * TF-A is expected to allow access to key registers from lower EL's,
@@ -55,84 +172,73 @@
 test_result_t test_pauth_reg_access(void)
 {
 	SKIP_TEST_IF_AARCH32();
-#ifndef AARCH32
-	uint64_t pauth_keys[LENGTH_KEYS];
+#ifdef __aarch64__
 	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
-	read_pauth_keys(pauth_keys, LENGTH_KEYS);
+	read_pauth_keys();
 	return TEST_RESULT_SUCCESS;
-#endif
+#endif	/* __aarch64__ */
 }
 
 /*
- * Makes a call to psci version, and checks that the EL3 pauth keys are not
+ * 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)) && AARCH64
-__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];
+#ifdef __aarch64__
 	SKIP_TEST_IF_PAUTH_NOT_SUPPORTED();
-
-	read_pauth_keys(pauth_keys_before, LENGTH_KEYS);
+	set_store_pauth_keys();
 
 	tftf_get_psci_version();
 
-	read_pauth_keys(pauth_keys_after, LENGTH_KEYS);
+	read_pauth_keys();
 
-	if (memcmp(pauth_keys_before, pauth_keys_after,
-			LENGTH_KEYS * sizeof(uint64_t)) != 0)
-		return TEST_RESULT_FAIL;
-	return TEST_RESULT_SUCCESS;
-#endif
+	return compare_pauth_keys();
+#endif	/* __aarch64__ */
 }
 
-/* Uses the pauth instructions, this checks the enable PAUTH bit has been set */
+/* Test execution of ARMv8.3-PAuth instructions */
 test_result_t test_pauth_instructions(void)
 {
 	SKIP_TEST_IF_AARCH32();
-#ifndef AARCH32
+#ifdef __aarch64__
 	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");
+
+#if ARM_ARCH_AT_LEAST(8, 3)
+	/* Pointer authentication instructions */
+	__asm__ volatile (
+		"paciasp\n"
+		"autiasp\n"
+		"paciasp\n"
+		"xpaclri"
+	);
 	return TEST_RESULT_SUCCESS;
-#endif
+#else
+	tftf_testcase_printf("Pointer Authentication instructions "
+				"are not supported on ARMv%u.%u\n",
+				ARM_ARCH_MAJOR, ARM_ARCH_MINOR);
+	return TEST_RESULT_SKIPPED;
+#endif	/* ARM_ARCH_AT_LEAST(8, 3) */
+
+#endif	/* __aarch64__ */
 }
 
 /*
  * 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)) && AARCH64
-__attribute__((target("sign-return-address=all")))
-#endif
 test_result_t test_pauth_leakage_tsp(void)
 {
 	SKIP_TEST_IF_AARCH32();
-#ifndef AARCH32
+#ifdef __aarch64__
 	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);
+	set_store_pauth_keys();
 
 	/* Standard SMC to ADD two numbers */
 	tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
@@ -145,20 +251,17 @@
 	 * 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:"
+	    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);
-
+				     (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
+	read_pauth_keys();
+
+	return compare_pauth_keys();
+#endif	/* __aarch64__ */
 }