feat(realm): add test case for FEAT_DoubleFault2 support on TF-RMM

When FEAT_DoubleFault2 is supported, TF-RMM must take into
account bit SCTLR2_EL1.EASE in order to decide whether to inject
a SEA into the sync exception vector or into the serror one.

The test on this patch verifies that TF-RMM injects the SEA
to the right vector depending on SCTLR2.EASE bit.

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I6c976fecb04d123e3efb96c5973b1466e241097f
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index 28b1d4e..6b41e51 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.h
@@ -364,6 +364,13 @@
 		}							\
 	} while (false)
 
+#define SKIP_TEST_IF_DOUBLE_FAULT2_NOT_SUPPORTED()				\
+	do {									\
+		if (is_feat_double_fault2_present() == false) {			\
+			return TEST_RESULT_SKIPPED;				\
+		}								\
+	} while (false)
+
 /* Helper macro to verify if system suspend API is supported */
 #define is_psci_sys_susp_supported()	\
 		(tftf_get_psci_feature_info(SMC_PSCI_SYSTEM_SUSPEND)		\
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 50cb5f9..d3d884e 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -430,8 +430,6 @@
 #define NV2_IMPLEMENTED			ULL(0x2)
 
 /* ID_AA64MMFR3_EL1 definitions */
-#define ID_AA64MMFR3_EL1			S3_0_C0_C7_3
-
 #define ID_AA64MMFR3_EL1_S2POE_SHIFT		U(20)
 #define ID_AA64MMFR3_EL1_S2POE_MASK		ULL(0xf)
 #define ID_AA64MMFR3_EL1_S2POE_WIDTH		U(4)
@@ -452,12 +450,19 @@
 #define ID_AA64MMFR3_EL1_S1PIE_WIDTH		U(4)
 #define ID_AA64MMFR3_EL1_S1PIE_SUPPORTED	ULL(0x1)
 
+#define ID_AA64MMFR3_EL1_SCTLRX_SHIFT		U(4)
+#define ID_AA64MMFR3_EL1_SCTLRX_WIDTH		ULL(0x4)
+
 #define ID_AA64MMFR3_EL1_TCRX_SHIFT		U(0)
 #define ID_AA64MMFR3_EL1_TCRX_MASK		ULL(0xf)
 #define ID_AA64MMFR3_EL1_TCRX_WIDTH		U(4)
 #define ID_AA64MMFR3_EL1_TCR2_SUPPORTED		ULL(0x1)
 
 /* ID_AA64PFR1_EL1 definitions */
+#define ID_AA64PFR1_EL1_DF2_SHIFT		U(56)
+#define ID_AA64PFR1_EL1_DF2_WIDTH		U(4)
+#define ID_AA64PFR1_EL1_DF2_MASK		(0xf << ID_AA64PFR1_EL1_DF2_SHIFT)
+
 #define ID_AA64PFR1_EL1_GCS_SHIFT		U(44)
 #define ID_AA64PFR1_EL1_GCS_MASK		ULL(0xf)
 #define ID_AA64PFR1_EL1_GCS_WIDTH		U(4)
@@ -484,6 +489,7 @@
 #define ID_AA64PFR1_MPAM_FRAC_SHIFT		U(16)
 #define ID_AA64PFR1_MPAM_FRAC_MASK		ULL(0xf)
 
+#define ID_AA64PFR1_RAS_FRAC_MASK		ULL(0xf)
 #define ID_AA64PFR1_RAS_FRAC_SHIFT		U(12)
 #define ID_AA64PFR1_RAS_FRAC_MASK		ULL(0xf)
 #define ID_AA64PFR1_RAS_FRAC_WIDTH		U(4)
@@ -504,6 +510,9 @@
 #define ID_AA64PFR1_EL1_BT_MASK			ULL(0xf)
 #define BTI_IMPLEMENTED				ULL(1)	/* The BTI mechanism is implemented */
 
+#define ID_AA64PFR1_DF2_SHIFT			U(56)
+#define ID_AA64PFR1_DF2_WIDTH			ULL(0x4)
+
 /* ID_PFR1_EL1 definitions */
 #define ID_PFR1_VIRTEXT_SHIFT	U(12)
 #define ID_PFR1_VIRTEXT_MASK	U(0xf)
@@ -553,6 +562,15 @@
 #define SCTLR_DSSBS_BIT		(ULL(1) << 44)
 #define SCTLR_RESET_VAL		SCTLR_EL3_RES1
 
+/* SCTLR2_EL1 register definitions */
+#define SCTLR2_EL1		S3_0_C1_C0_3
+
+#define SCTLR2_NMEA_BIT		(UL(1) << 2)
+#define SCTLR2_EnADERR_BIT	(UL(1) << 3)
+#define SCTLR2_EnANERR_BIT	(UL(1) << 4)
+#define SCTLR2_EASE_BIT		(UL(1) << 5)
+#define SCTLR2_EnIDCP128_BIT	(UL(1) << 6)
+
 /* CPACR_El1 definitions */
 #define CPACR_EL1_FPEN(x)	((x) << 20)
 #define CPACR_EL1_FP_TRAP_EL0	U(0x1)
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index c7d824a..96d899a 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -525,4 +525,11 @@
 	return EXTRACT(ID_AA64PFR1_EL1_MTE, read_id_aa64pfr1_el1())
 		>= MTE_IMPLEMENTED_ELX;
 }
+
+static inline bool is_feat_double_fault2_present(void)
+{
+	return (EXTRACT(ID_AA64PFR1_EL1_DF2,
+		read_id_aa64pfr1_el1()) == 1UL);
+}
+
 #endif /* ARCH_FEATURES_H */
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index c906181..a765548 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -322,6 +322,8 @@
 DEFINE_SYSREG_RW_FUNCS(sctlr_el2)
 DEFINE_SYSREG_RW_FUNCS(sctlr_el3)
 
+DEFINE_RENAME_SYSREG_RW_FUNCS(sctlr2_el1, SCTLR2_EL1)
+
 DEFINE_SYSREG_RW_FUNCS(actlr_el1)
 DEFINE_SYSREG_RW_FUNCS(actlr_el2)
 DEFINE_SYSREG_RW_FUNCS(actlr_el3)
diff --git a/include/runtime_services/host_realm_managment/host_shared_data.h b/include/runtime_services/host_realm_managment/host_shared_data.h
index 7cfffe6..3e934f1 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -70,7 +70,8 @@
 	REALM_PAUTH_FAULT,
 	REALM_DIT_CHECK_CMD,
 	REALM_SME_ID_REGISTERS,
-	REALM_SME_UNDEF_ABORT
+	REALM_SME_UNDEF_ABORT,
+	REALM_FEAT_DOUBLEFAULT2_TEST
 };
 
 /*
diff --git a/lib/exceptions/aarch64/serror.c b/lib/exceptions/aarch64/serror.c
index 9c35712..437c5d0 100644
--- a/lib/exceptions/aarch64/serror.c
+++ b/lib/exceptions/aarch64/serror.c
@@ -23,9 +23,28 @@
 
 bool tftf_serror_handler(void)
 {
+	uint64_t elr_elx = IS_IN_EL2() ? read_elr_el2() : read_elr_el1();
+	bool resume = false;
+
 	if (custom_serror_handler == NULL) {
 		return false;
 	}
 
-	return custom_serror_handler();
+	resume = custom_serror_handler();
+
+	/*
+	 * TODO: if there is a test exepecting an Aync EA and expects to resume,
+	 * then there needs to be additional info from test handler as to whether
+	 * elr can be incremented or not.
+	 */
+	if (resume) {
+		/* Move ELR to next instruction to allow tftf to continue */
+		if (IS_IN_EL2()) {
+			write_elr_el2(elr_elx + 4U);
+		} else {
+			write_elr_el1(elr_elx + 4U);
+		}
+	}
+
+	return resume;
 }
diff --git a/realm/aarch64/realm_exceptions.S b/realm/aarch64/realm_exceptions.S
index 210dd3e..c1d1c0c 100644
--- a/realm/aarch64/realm_exceptions.S
+++ b/realm/aarch64/realm_exceptions.S
@@ -43,8 +43,9 @@
 	b	interrupt_vector_entry
 end_vector_entry fiq_spx
 
-unhandled_exception serr_spx
-
+vector_entry serr_spx
+	b	serr_exception_vector_entry
+end_vector_entry serr_spx
 	/*
 	 * Lower EL using AArch64 : 0x400 - 0x600.
 	 */
@@ -116,6 +117,22 @@
 	eret
 endfunc sync_exception_vector_entry
 
+func serr_exception_vector_entry
+	sub	sp, sp, #0x100
+	save_gp_regs
+	mov	x19, sp
+	bl	tftf_serror_handler
+	cbnz	x0, 0f
+	mov	x0, x19
+	/* Save original stack pointer value on the stack */
+	add	x1, x0, #0x100
+	str	x1, [x0, #0xf8]
+	b	realm_print_exception
+0:	restore_gp_regs
+	add	sp, sp, #0x100
+	eret
+endfunc serr_exception_vector_entry
+
 func interrupt_vector_entry
 	sub	sp, sp, #0x100
 	save_gp_regs
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index b58949b..a2cfc9b 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -27,6 +27,7 @@
 bool test_realm_multiple_rec_multiple_cpu_cmd(void);
 bool test_realm_sme_read_id_registers(void);
 bool test_realm_sme_undef_abort(void);
+bool test_realm_sctlr2_ease(void);
 
 #endif /* REALM_TESTS_H */
 
diff --git a/realm/realm.mk b/realm/realm.mk
index 305c007..b0536ca 100644
--- a/realm/realm.mk
+++ b/realm/realm.mk
@@ -43,6 +43,7 @@
 	lib/${ARCH}/misc_helpers.S					\
 	lib/smc/${ARCH}/asm_smc.S					\
 	lib/smc/${ARCH}/smc.c						\
+	lib/exceptions/${ARCH}/serror.c					\
 	lib/exceptions/${ARCH}/sync.c					\
 	lib/locks/${ARCH}/spinlock.S					\
 	lib/delay/delay.c						\
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index 47f9b9f..cf9cce3 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -16,11 +16,13 @@
 #include "realm_def.h"
 #include <realm_rsi.h>
 #include <realm_tests.h>
+#include <serror.h>
 #include <sync.h>
 #include <tftf_lib.h>
 
 static fpu_state_t rl_fpu_state_write;
 static fpu_state_t rl_fpu_state_read;
+
 /*
  * This function reads sleep time in ms from shared buffer and spins PE
  * in a loop for that time period.
@@ -176,7 +178,7 @@
 	/* Causes data abort */
 	realm_printf("Generate Data Abort\n");
 	*((volatile uint64_t *)base);
-	/* Should not return */
+
 	return false;
 }
 
@@ -200,6 +202,49 @@
 	return false;
 }
 
+static bool realm_serror_handler_doublefault(void)
+{
+	if ((read_sctlr2_el1() & SCTLR2_EASE_BIT) != 0UL) {
+		/* The serror exception should have been routed here */
+		return true;
+	}
+
+	rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+
+	/* Should have never get here */
+	return false;
+}
+
+static bool realm_sync_handler_doublefault(void)
+{
+	if ((read_sctlr2_el1() & SCTLR2_EASE_BIT) == 0UL) {
+		/* The sync exception should have been routed here */
+		return true;
+	}
+
+	rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+
+	/* Should have never get here */
+	return false;
+}
+
+static void test_realm_feat_doublefault2(void)
+{
+	u_register_t ease_bit = realm_shared_data_get_my_host_val(HOST_ARG2_INDEX);
+
+	unregister_custom_sync_exception_handler();
+	register_custom_sync_exception_handler(realm_sync_handler_doublefault);
+	register_custom_serror_handler(realm_serror_handler_doublefault);
+
+	if (ease_bit != 0UL) {
+		write_sctlr2_el1(read_sctlr2_el1() | SCTLR2_EASE_BIT);
+	} else {
+		write_sctlr2_el1(read_sctlr2_el1() & ~SCTLR2_EASE_BIT);
+	}
+
+	(void)test_realm_data_access_cmd();
+}
+
 /*
  * This is the entry function for Realm payload, it first requests the shared buffer
  * IPA address from Host using HOST_CALL/RSI, it reads the command to be executed,
@@ -213,7 +258,12 @@
 	bool test_succeed = false;
 
 	register_custom_sync_exception_handler(realm_exception_handler);
+
+	/* No serror handler registered by default */
+	unregister_custom_serror_handler();
+
 	realm_set_shared_structure((host_shared_data_t *)rsi_get_ns_buffer());
+
 	if (realm_get_my_shared_structure() != NULL) {
 		uint8_t cmd = realm_shared_data_get_my_realm_cmd();
 
@@ -232,6 +282,10 @@
 		case REALM_MULTIPLE_REC_MULTIPLE_CPU_CMD:
 			test_succeed = test_realm_multiple_rec_multiple_cpu_cmd();
 			break;
+		case REALM_FEAT_DOUBLEFAULT2_TEST:
+			test_realm_feat_doublefault2();
+			test_succeed = true;
+			break;
 		case REALM_INSTR_FETCH_CMD:
 			test_succeed = test_realm_instr_fetch_cmd();
 			break;
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
index e22a9f1..1f4f88d 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
@@ -2525,3 +2525,69 @@
 	}
 	return res;
 }
+
+/*
+ * Test aims to test that TF-RMM takes SCTLR2_EL1.EASE bit into account
+ * when injecting a SEA (Feat_DoubleFault2).
+ */
+test_result_t host_test_feat_doublefault2(void)
+{
+	bool ret;
+	u_register_t rec_flag;
+	u_register_t base;
+	struct realm realm;
+	struct rtt_entry rtt;
+	u_register_t feature_flag = 0UL;
+	long sl = RTT_MIN_LEVEL;
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+	SKIP_TEST_IF_DOUBLE_FAULT2_NOT_SUPPORTED();
+
+	if (is_feat_52b_on_4k_2_supported() == true) {
+		feature_flag = RMI_FEATURE_REGISTER_0_LPA2;
+		sl = RTT_MIN_LEVEL_LPA2;
+	}
+
+	rec_flag = RMI_RUNNABLE;
+
+	if (!host_create_activate_realm_payload(&realm,
+					(u_register_t)REALM_IMAGE_BASE,
+				feature_flag, sl, &rec_flag, 1U)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Allocate a page so we pass its address as first argument of
+	 * the test command. The test will attempt an instruction fetch
+	 * from that address, which will fail as the address will not
+	 * be mapped into the Realm.
+	 */
+	base = (u_register_t)page_alloc(PAGE_SIZE);
+
+	(void)host_rmi_rtt_readentry(realm.rd, base, 3L, &rtt);
+	if (rtt.state != RMI_UNASSIGNED ||
+			(rtt.ripas != RMI_EMPTY)) {
+		ERROR("wrong initial state\n");
+		host_destroy_realm(&realm);
+		return TEST_RESULT_FAIL;
+	}
+
+	host_shared_data_set_host_val(&realm, 0U, HOST_ARG1_INDEX, base);
+
+	for (unsigned int i = 0U; i < 2U; i++) {
+		host_shared_data_set_host_val(&realm, 0U, HOST_ARG2_INDEX,
+					      (unsigned long)i);
+
+		/* Rec0 expect IA due to SEA unassigned empty page */
+		ret = host_enter_realm_execute(&realm, REALM_FEAT_DOUBLEFAULT2_TEST,
+							RMI_EXIT_HOST_CALL, 0U);
+
+		if (!ret) {
+			host_destroy_realm(&realm);
+			return TEST_RESULT_FAIL;
+		}
+	}
+
+	host_destroy_realm(&realm);
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index f359e72..3c0dda4 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -76,6 +76,8 @@
 	  function="host_realm_set_ripas" />
 	  <testcase name="Realm reject set_ripas"
 	  function="host_realm_reject_set_ripas" />
+	  <testcase name="Realm FEAT_DoubleFault2"
+	  function="host_test_feat_doublefault2" />
 	  <!-- Test case related to SVE support and SIMD state -->
 	  <testcase name="Check RMI reports proper SVE VL"
 	  function="host_check_rmi_reports_proper_sve_vl" />