Add test to check PSTATE in the SDEI handler

When handling an SDEI event the processor must be in a known state.
The SDEI specification requires PSTATE bits DAIF= 0b1111,
EL = Client_El, nRw = 0 and SP = 1. All other bits are populated
according to the AArch64.TakeException() pseudocode function
defined in the Arm Architecture Reference Manual.
This patch adds tests to check the PSTATE in the event handler to
follow these requirements

Change-Id: If7178c5f1c25e92c49612631b9a6a57c9e3062cd
Signed-off-by: Daniel Boulby <daniel.boulby@arm.com>
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index f268167..f7d2420 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -223,6 +223,14 @@
 #define ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED	ULL(0x1)
 #define ID_AA64MMFR0_EL1_TGRAN16_NOT_SUPPORTED	ULL(0x0)
 
+/* ID_AA64MMFR1_EL1 definitions */
+#define ID_AA64MMFR1_EL1_PAN_SHIFT		U(20)
+#define ID_AA64MMFR1_EL1_PAN_MASK		ULL(0xf)
+#define ID_AA64MMFR1_EL1_PAN_NOT_SUPPORTED	ULL(0x0)
+#define ID_AA64MMFR1_EL1_PAN_SUPPORTED		ULL(0x1)
+#define ID_AA64MMFR1_EL1_PAN2_SUPPORTED		ULL(0x2)
+#define ID_AA64MMFR1_EL1_PAN3_SUPPORTED		ULL(0x3)
+
 /* ID_AA64MMFR2_EL1 definitions */
 #define ID_AA64MMFR2_EL1		S3_0_C0_C7_2
 
@@ -289,6 +297,7 @@
 #define SCTLR_WXN_BIT		(ULL(1) << 19)
 #define SCTLR_UWXN_BIT		(ULL(1) << 20)
 #define SCTLR_IESB_BIT		(ULL(1) << 21)
+#define SCTLR_SPAN_BIT		(ULL(1) << 23)
 #define SCTLR_E0E_BIT		(ULL(1) << 24)
 #define SCTLR_EE_BIT		(ULL(1) << 25)
 #define SCTLR_UCI_BIT		(ULL(1) << 26)
@@ -371,6 +380,7 @@
 #define HCR_AMVOFFEN_BIT	(ULL(1) << 51)
 #define HCR_API_BIT		(ULL(1) << 41)
 #define HCR_APK_BIT		(ULL(1) << 40)
+#define HCR_E2H_BIT		(ULL(1) << 34)
 #define HCR_TGE_BIT		(ULL(1) << 27)
 #define HCR_RW_SHIFT		U(31)
 #define HCR_RW_BIT		(ULL(1) << HCR_RW_SHIFT)
@@ -952,6 +962,12 @@
 #define ERXPFGCTL_CDEN_BIT	(U(1) << 31)
 
 /*******************************************************************************
+ * Armv8.1 Registers - Privileged Access Never Registers
+ ******************************************************************************/
+#define PAN			S3_0_C4_C2_3
+#define PAN_BIT			BIT(22)
+
+/*******************************************************************************
  * Armv8.3 Pointer Authentication Registers
  ******************************************************************************/
 #define APIAKeyLo_EL1		S3_0_C2_C1_0
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index 15eb784..0ecd039 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -17,6 +17,12 @@
 	return true;
 }
 
+static inline bool is_armv8_1_pan_present(void)
+{
+	return ((read_id_aa64mmfr1_el1() >> ID_AA64MMFR1_EL1_PAN_SHIFT) &
+		ID_AA64MMFR1_EL1_PAN_MASK) != 0U;
+}
+
 static inline bool is_armv8_2_sve_present(void)
 {
 	return ((read_id_aa64pfr0_el1() >> ID_AA64PFR0_SVE_SHIFT) &
@@ -56,6 +62,12 @@
 	return (read_id_aa64isar1_el1() & mask) != 0U;
 }
 
+static inline bool is_armv8_4_dit_present(void)
+{
+	return ((read_id_aa64pfr0_el1() >> ID_AA64PFR0_DIT_SHIFT) &
+		ID_AA64PFR0_DIT_MASK) == 1U;
+}
+
 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 39f1e3b..801ad08 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -187,6 +187,8 @@
 DEFINE_SYSREG_READ_FUNC(CurrentEl)
 DEFINE_SYSREG_READ_FUNC(ctr_el0)
 DEFINE_SYSREG_RW_FUNCS(daif)
+DEFINE_SYSREG_RW_FUNCS(nzcv)
+DEFINE_SYSREG_READ_FUNC(spsel)
 DEFINE_SYSREG_RW_FUNCS(spsr_el1)
 DEFINE_SYSREG_RW_FUNCS(spsr_el2)
 DEFINE_SYSREG_RW_FUNCS(spsr_el3)
@@ -292,6 +294,7 @@
 DEFINE_SYSREG_READ_FUNC(midr_el1)
 DEFINE_SYSREG_READ_FUNC(mpidr_el1)
 DEFINE_SYSREG_READ_FUNC(id_aa64mmfr0_el1)
+DEFINE_SYSREG_READ_FUNC(id_aa64mmfr1_el1)
 
 DEFINE_SYSREG_RW_FUNCS(scr_el3)
 DEFINE_SYSREG_RW_FUNCS(hcr_el2)
@@ -451,6 +454,9 @@
 DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc0_el1, ERXMISC0_EL1)
 DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc1_el1, ERXMISC1_EL1)
 
+/* Armv8.1 Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(pan, PAN)
+
 /* Armv8.2 Registers */
 DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1)
 
@@ -479,6 +485,9 @@
 DEFINE_RENAME_SYSREG_RW_FUNCS(rgsr_el1, RGSR_EL1)
 DEFINE_RENAME_SYSREG_RW_FUNCS(gcr_el1, GCR_EL1)
 
+/* Armv8.4 Data Independent Timing */
+DEFINE_RENAME_SYSREG_RW_FUNCS(dit, DIT)
+
 /* Armv8.6 Fine Grained Virtualization Traps Registers */
 DEFINE_RENAME_SYSREG_RW_FUNCS(hfgrtr_el2,  HFGRTR_EL2)
 DEFINE_RENAME_SYSREG_RW_FUNCS(hfgwtr_el2,  HFGWTR_EL2)
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S b/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
index 74fe4a6..a887234 100644
--- a/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -13,6 +13,7 @@
 	.globl	sdei_entrypoint_resume
 	.globl	sdei_handler_done
 	.globl	sdei_rm_any_entrypoint
+	.globl	sdei_check_pstate_entrypoint
 
 	.local	event_handled
 	.comm	event_handled, PLATFORM_CORE_COUNT * 4, 8
@@ -126,6 +127,33 @@
 	b	.
 endfunc sdei_rm_any_entrypoint
 
+func sdei_check_pstate_entrypoint
+	stp	x2, x30, [sp, #-16]!
+
+	/* Dispatch to C handler */
+	bl	sdei_check_pstate_handler
+
+	/* Calculate address of event completion variable */
+	mrs	x0, mpidr_el1
+	mov_imm	x1, MPID_MASK
+	and	x0, x0, x1
+	bl	platform_get_core_pos
+	lsl	x0, x0, #2
+	adrp	x1, event_handled
+	add	x1, x1, :lo12:event_handled
+	add	x1, x0, x1
+
+	/* Mark event handling as complete so `sdei_handler_done` can return */
+	mov	w2, #1
+	str	w2, [x1]
+	sev
+
+	/* Populate `x0` and `x1` to prepare for SMC call */
+	ldp	x1, x30, [sp], #16
+	mov_imm	x0, SDEI_EVENT_COMPLETE_AND_RESUME
+	smc	#0
+endfunc sdei_check_pstate_entrypoint
+
 #else /* AARCH32 */
 func sdei_entrypoint
 	/* SDEI is not supported on AArch32. */
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_pstate.c b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_pstate.c
new file mode 100644
index 0000000..024352e
--- /dev/null
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_pstate.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <events.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <power_management.h>
+#include <sdei.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+#include <timer.h>
+
+#ifdef __aarch64__
+
+#define EV_COOKIE 0xDEADBEEF
+
+extern sdei_handler_t sdei_check_pstate_entrypoint;
+
+u_register_t daif;
+u_register_t sp;
+u_register_t pan;
+u_register_t dit;
+
+int sdei_check_pstate_handler(int ev, unsigned long long arg)
+{
+	printf("%s: handler fired\n", __func__);
+	daif = read_daif();
+	sp = read_spsel();
+	if (is_armv8_1_pan_present())
+		pan = read_pan();
+
+	if (is_armv8_4_dit_present())
+		dit = read_dit();
+
+	assert(arg == EV_COOKIE);
+	return 0;
+}
+
+static test_result_t sdei_event_check_pstate(void)
+{
+	long long ret;
+
+	ret = sdei_event_register(0, sdei_check_pstate_entrypoint, EV_COOKIE,
+		SDEI_REGF_RM_PE, read_mpidr_el1());
+	if (ret < 0) {
+		tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
+			ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	ret = sdei_event_enable(0);
+	if (ret < 0) {
+		tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
+		goto err0;
+	}
+
+	ret = sdei_pe_unmask();
+	if (ret < 0) {
+		tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
+		goto err1;
+	}
+
+	/* Check the common bits are set correctly */
+	ret = sdei_event_signal(read_mpidr_el1());
+	if (ret < 0) {
+		tftf_testcase_printf("SDEI event signal failed: 0x%llx\n", ret);
+		goto err2;
+	}
+	sdei_handler_done();
+
+	u_register_t all_interrupts_masked = 0x3c0;
+
+	if (daif != all_interrupts_masked) {
+		tftf_testcase_printf("Interrupts were not correctly masked " \
+				     "during SDEI event signal\n" \
+				     "Expected DAIF: 0x%lx, " \
+				     "Actual DAIF: 0x%lx\n",
+				     all_interrupts_masked, daif);
+		ret = -1;
+		goto err1;
+	}
+
+	u_register_t use_sp_elx = 0x1;
+
+	if (sp != use_sp_elx) {
+		tftf_testcase_printf("The SPSel PSTATE Bit was not set " \
+				     "correctly during SDEI event signal\n" \
+				     "Expected SPSel: 0x%lx, " \
+				     "Actual SPSel: 0x%lx\n",
+				     use_sp_elx, sp);
+		ret = -1;
+		goto err1;
+	}
+
+	if (is_armv8_1_pan_present()) {
+		printf("PAN Enabled so testing PAN PSTATE bit\n");
+		/*
+		 * Check that when the SPAN bit is 0
+		 * the PAN PSTATE bit is maintained
+		 */
+
+		/* When PAN bit is 0 */
+		u_register_t expected_pan = 0;
+
+		write_pan(expected_pan);
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (pan != expected_pan) {
+			tftf_testcase_printf("PAN PSTATE bit not maintained " \
+					     "during SDEI event signal\n" \
+					     "Expected PAN: 0x%lx, " \
+					     "Actual PAN: 0x%lx\n",
+					     expected_pan, pan);
+			ret = -1;
+			goto err1;
+		}
+
+		/* When PAN bit is 1 */
+		expected_pan = PAN_BIT;
+		write_pan(expected_pan);
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (pan != expected_pan) {
+			tftf_testcase_printf("PAN PSTATE bit not maintained " \
+					     "during SDEI event signal\n" \
+					     "Expected PAN: 0x%lx, " \
+					     "Actual PAN: 0x%lx\n",
+					     expected_pan, pan);
+			ret = -1;
+			goto err1;
+		}
+
+		/* Test that the SPAN condition is met */
+		/* Set the SPAN bit */
+		u_register_t old_sctlr = read_sctlr_el2();
+
+		write_sctlr_el2(old_sctlr & ~SCTLR_SPAN_BIT);
+
+		expected_pan = 0;
+		/*
+		 * Check that when the HCR_EL2.{E2H, TGE} != {1, 1}
+		 * the PAN bit is maintained
+		 */
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (pan != expected_pan) {
+			tftf_testcase_printf("PAN PSTATE bit not maintained " \
+					     "during SDEI event signal " \
+					     "when the SPAN bit is set and " \
+					     "HCR_EL2.{E2H, TGE} != {1, 1}\n" \
+					     "Expected PAN: 0x%lx, " \
+					     "Actual PAN: 0x%lx\n",
+					     expected_pan, pan);
+			ret = -1;
+			goto err1;
+		}
+
+		expected_pan = PAN_BIT;
+		write_pan(expected_pan);
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (pan != expected_pan) {
+			tftf_testcase_printf("PAN PSTATE bit not maintained " \
+					     "during SDEI event signal " \
+					     "when the SPAN bit is set and " \
+					     "HCR_EL2.{E2H, TGE} != {1, 1}\n" \
+					     "Expected PAN: 0x%lx, " \
+					     "Actual PAN: 0x%lx\n",
+					     expected_pan, pan);
+			ret = -1;
+			goto err1;
+		}
+
+		/*
+		 * Check that when the HCR_EL2.{E2H, TGE} = {1, 1}
+		 * PAN bit is forced to 1
+		 */
+		/* Set E2H Bit */
+		u_register_t old_hcr_el2 = read_hcr_el2();
+
+		write_hcr_el2(old_hcr_el2 | HCR_E2H_BIT);
+
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (pan != PAN_BIT) {
+			tftf_testcase_printf("PAN PSTATE bit was not forced " \
+					     "to 1 during SDEI event signal " \
+					     "when the SPAN bit is set and " \
+					     "HCR_EL2.{E2H, TGE} = {1, 1}\n");
+			ret = -1;
+			goto err1;
+		}
+
+		/*
+		 * Set the SCTLR and HCR_EL2 registers back to their old values
+		 */
+		write_sctlr_el2(old_sctlr);
+		write_hcr_el2(old_hcr_el2);
+	}
+
+	/* Check that the DIT PSTATE bit is maintained during event signal */
+	if (is_armv8_4_dit_present()) {
+		printf("DIT Enabled so testing DIT PSTATE bit\n");
+		/* When DIT bit is 0 */
+		u_register_t expected_dit = 0;
+
+		write_dit(expected_dit);
+		ret = sdei_event_signal(read_mpidr_el1());
+
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (dit != expected_dit) {
+			tftf_testcase_printf("DIT PSTATE bit not maintained " \
+					     "during SDEI event signal\n" \
+					     "Expected DIT: 0x%lx, " \
+					     "Actual DIT: 0x%lx\n",
+					     expected_dit, dit);
+			ret = -1;
+			goto err1;
+		}
+
+		/* When dit bit is 1 */
+		expected_dit = DIT_BIT;
+		write_dit(expected_dit);
+		ret = sdei_event_signal(read_mpidr_el1());
+		if (ret < 0) {
+			tftf_testcase_printf("SDEI event signal failed: " \
+					     "0x%llx\n", ret);
+			goto err2;
+		}
+		sdei_handler_done();
+		if (dit != expected_dit) {
+			tftf_testcase_printf("DIT PSTATE bit not maintained " \
+					     "during SDEI event signal\n" \
+					     "Expected DIT: 0x%lx, " \
+					     "Actual DIT: 0x%lx\n",
+					     expected_dit, dit);
+			ret = -1;
+			goto err1;
+		}
+	}
+
+err2:
+	sdei_pe_mask();
+err1:
+	sdei_event_disable(0);
+err0:
+	sdei_event_unregister(0);
+
+	if (ret < 0)
+		return TEST_RESULT_FAIL;
+
+	return TEST_RESULT_SUCCESS;
+}
+#endif /* __aarch64__ */
+
+/* Each core signals itself using SDEI event signalling. */
+test_result_t test_sdei_event_check_pstate(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifdef __aarch64__
+	long long ret;
+
+	ret = sdei_version();
+	if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
+		tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
+		return TEST_RESULT_SKIPPED;
+	}
+
+	disable_irq();
+	/* We only need to run these tests on the main CPU */
+	if (sdei_event_check_pstate() != TEST_RESULT_SUCCESS) {
+		ret = -1;
+		goto err0;
+	}
+
+err0:
+	enable_irq();
+	if (ret < 0)
+		return TEST_RESULT_FAIL;
+	return TEST_RESULT_SUCCESS;
+#endif /* __aarch64__ */
+}
diff --git a/tftf/tests/tests-sdei.mk b/tftf/tests/tests-sdei.mk
index 0c495d9..e73bfb7 100644
--- a/tftf/tests/tests-sdei.mk
+++ b/tftf/tests/tests-sdei.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2020, Arm Limited. All rights reserved.
+# Copyright (c) 2020-2021, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -10,4 +10,5 @@
 		test_sdei.c 							\
 		test_sdei_state.c 						\
 		test_sdei_rm_any.c 						\
+		test_sdei_pstate.c						\
 	)
diff --git a/tftf/tests/tests-sdei.xml b/tftf/tests/tests-sdei.xml
index 147835b..38c7c0d 100644
--- a/tftf/tests/tests-sdei.xml
+++ b/tftf/tests/tests-sdei.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright (c) 2020, Arm Limited. All rights reserved.
+  Copyright (c) 2020-2021, Arm Limited. All rights reserved.
 
   SPDX-License-Identifier: BSD-3-Clause
 -->
@@ -15,6 +15,7 @@
      <testcase name="SDEI event signaling: each core signals itself" function="test_sdei_event_signal_serial" />
      <testcase name="SDEI event signaling: one core signals all others" function="test_sdei_event_signal_all" />
      <testcase name="SDEI event routing all: SPI events routed to all CPUs" function="test_sdei_routing_any" />
+     <testcase name="SDEI event handler pstate testing" function="test_sdei_event_check_pstate" />
   </testsuite>
 
 </testsuites>