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">