test(realm): add testcase for Synchronous external aborts

Add testcase to cause instruction or data abort in Realm by
accessing addr with
* HIPAS=UNASSIGNED and RIPAS=EMPTY
* HIPAS=ASSIGNED and RIPAS=EMPTY
* Unprotected IPA
* Host injected SEA after Data abort

Change-Id: I6be546c042b4983670fb7c27fca74649c68787be
Signed-off-by: Shruti Gupta <shruti.gupta@arm.com>
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 653dfd9..6a0d286 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -828,10 +828,12 @@
 #define EC_SERROR			U(0x2f)
 /* Data Fault Status code, not all error codes listed */
 #define ISS_DFSC_MASK			U(0x3f)
+#define DFSC_L0_ADR_SIZE_FAULT		U(0)
 #define DFSC_L0_TRANS_FAULT		U(4)
 #define DFSC_L1_TRANS_FAULT		U(5)
 #define DFSC_L2_TRANS_FAULT		U(6)
 #define DFSC_L3_TRANS_FAULT		U(7)
+#define DFSC_NO_WALK_SEA		U(0x10)
 #define DFSC_L0_SEA			U(0x14)
 #define DFSC_L1_SEA			U(0x15)
 #define DFSC_L2_SEA			U(0x16)
@@ -841,10 +843,12 @@
 
 /* Instr Fault Status code, not all error codes listed */
 #define ISS_IFSC_MASK			U(0x3f)
+#define IFSC_L0_ADR_SIZE_FAULT		U(0)
 #define IFSC_L0_TRANS_FAULT		U(4)
 #define IFSC_L1_TRANS_FAULT		U(5)
 #define IFSC_L2_TRANS_FAULT		U(6)
 #define IFSC_L3_TRANS_FAULT		U(7)
+#define IFSC_NO_WALK_SEA		U(0x10)
 #define IFSC_L0_SEA			U(0x24)
 #define IFSC_L1_SEA			U(0x25)
 #define IFSC_L2_SEA			U(0x26)
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 1ae1533..fc0b030 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -103,6 +103,12 @@
 		unsigned int rec_num, uint8_t index, u_register_t val);
 
 /*
+ * Get data shared from realm to Host
+ */
+u_register_t host_shared_data_get_realm_val(struct realm *realm_ptr,
+		unsigned int rec_num, uint8_t index);
+
+/*
  * Set command to be send from Host to realm
  */
 void host_shared_data_set_realm_cmd(struct realm *realm_ptr, uint8_t cmd,
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index 55b5f9a..93e2404 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -16,6 +16,7 @@
 #include "realm_def.h"
 #include <realm_rsi.h>
 #include <realm_tests.h>
+#include <sync.h>
 #include <tftf_lib.h>
 
 static fpu_state_t rl_fpu_state_write;
@@ -161,6 +162,26 @@
 	return false;
 }
 
+static bool realm_exception_handler(void)
+{
+	u_register_t base, far, esr;
+
+	base = realm_shared_data_get_my_host_val(HOST_ARG1_INDEX);
+	far = read_far_el1();
+	esr = read_esr_el1();
+
+	if (far == base) {
+		/* return ESR to Host */
+		realm_shared_data_set_my_realm_val(HOST_ARG2_INDEX, esr);
+		rsi_exit_to_host(HOST_CALL_EXIT_SUCCESS_CMD);
+	}
+	realm_printf("Realm Abort fail incorrect FAR=0x%lx ESR+0x%lx\n", far, esr);
+	rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+
+	/* Should not return. */
+	return false;
+}
+
 /*
  * 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,
@@ -173,6 +194,7 @@
 {
 	bool test_succeed = false;
 
+	register_custom_sync_exception_handler(realm_exception_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();
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 796d27e..175dc04 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
@@ -950,3 +950,258 @@
 
 	return res;
 }
+
+/*
+ * Test aims to generate SEA in Realm by accessing
+ * PAGE with HIPAS=assigned/unassigned and RIPAS=EMPTY
+ * Host creates and executes 4 recs to generate SEA
+ * Rec exception handler runs and returns back ESR to Host
+ * Host validates ESR
+ * Rec0 generated IA unassigned empty
+ * Rec1 generated DA unassigned empty
+ * Rec2 generated IA for assigned empty
+ * Rec3 generated DA for assigned empty
+ */
+test_result_t host_realm_sea_empty(void)
+{
+	bool ret1, ret2;
+	test_result_t res = TEST_RESULT_FAIL;
+	u_register_t ret, base, esr;
+	struct realm realm;
+	struct rtt_entry rtt;
+	u_register_t rec_flag[] = {RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE};
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	if (!host_create_activate_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE,
+			(u_register_t)PAGE_POOL_MAX_SIZE,
+			0UL, rec_flag, 4U)) {
+		return TEST_RESULT_FAIL;
+	}
+	if (!host_create_shared_mem(&realm, NS_REALM_SHARED_MEM_BASE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		goto destroy_realm;
+	}
+
+	base = (u_register_t)page_alloc(PAGE_SIZE);
+
+	ret = host_rmi_rtt_readentry(realm.rd, base, 3U, &rtt);
+	if (rtt.state != RMI_UNASSIGNED ||
+			(rtt.ripas != RMI_EMPTY)) {
+		ERROR("wrong initial state\n");
+		goto destroy_realm;
+	}
+	host_shared_data_set_host_val(&realm, 0U, HOST_ARG1_INDEX, base);
+	host_shared_data_set_host_val(&realm, 1U, HOST_ARG1_INDEX, base);
+	host_shared_data_set_host_val(&realm, 2U, HOST_ARG1_INDEX, base);
+	host_shared_data_set_host_val(&realm, 3U, HOST_ARG1_INDEX, base);
+
+	/* Rec0 expect IA due to SEA unassigned empty page */
+	ret1 = host_enter_realm_execute(&realm, REALM_INSTR_FETCH_CMD,
+			RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		ERROR("Rec0 did not fault\n");
+		goto destroy_realm;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 0U, HOST_ARG2_INDEX);
+	if (((esr & ISS_IFSC_MASK) != IFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_IABORT_CUR_EL)) {
+		ERROR("Rec0 incorrect ESR=0x%lx\n", esr);
+		goto destroy_realm;
+	}
+	INFO("Rec0 ESR=0x%lx\n", esr);
+
+	/* Rec1 expect DA due to SEA unassigned empty page */
+	ret1 = host_enter_realm_execute(&realm, REALM_DATA_ACCESS_CMD,
+			RMI_EXIT_HOST_CALL, 1U);
+	if (!ret1) {
+		ERROR("Rec1 did not fault\n");
+		goto destroy_realm;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 1U, HOST_ARG2_INDEX);
+	if (((esr & ISS_DFSC_MASK) != DFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_DABORT_CUR_EL)) {
+		ERROR("Rec1 incorrect ESR=0x%lx\n", esr);
+		goto destroy_realm;
+	}
+	INFO("Rec1 ESR=0x%lx\n", esr);
+
+	/* DATA_CREATE_UNKNOWN */
+	ret = host_realm_delegate_map_protected_data(true, &realm, base, PAGE_SIZE, 0U);
+	if (ret != RMI_SUCCESS) {
+		ERROR("host_realm_map_protected_data failed\n");
+		goto destroy_realm;
+	}
+	ret = host_rmi_rtt_readentry(realm.rd, base, 3U, &rtt);
+	if (rtt.state != RMI_ASSIGNED ||
+			(rtt.ripas != RMI_EMPTY)) {
+		ERROR("wrong state after DATA_CRATE_UNKNOWN\n");
+		goto undelegate_destroy;
+	}
+	INFO("state base = 0x%lx rtt.state=0x%lx rtt.ripas=0x%lx\n",
+			base, rtt.state, rtt.ripas);
+
+	/* Rec2 expect IA due to SEA assigned empty page */
+	ret1 = host_enter_realm_execute(&realm, REALM_INSTR_FETCH_CMD,
+		RMI_EXIT_HOST_CALL, 2U);
+
+	if (!ret1) {
+		ERROR("Rec2 did not fault\n");
+		goto undelegate_destroy;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 2U, HOST_ARG2_INDEX);
+	if (((esr & ISS_IFSC_MASK) != IFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_IABORT_CUR_EL)) {
+		ERROR("Rec2 incorrect ESR=0x%lx\n", esr);
+		goto destroy_realm;
+	}
+	INFO("Rec2 ESR=0x%lx\n", esr);
+
+	/* Rec3 expect DA due to SEA unassigned empty page */
+	ret1 = host_enter_realm_execute(&realm, REALM_DATA_ACCESS_CMD,
+			RMI_EXIT_HOST_CALL, 3U);
+	if (!ret1) {
+		ERROR("Rec3 did not fault\n");
+		goto undelegate_destroy;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 3U, HOST_ARG2_INDEX);
+	if (((esr & ISS_DFSC_MASK) != DFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_DABORT_CUR_EL)) {
+		ERROR("Rec3 incorrect ESR=0x%lx\n", esr);
+	}
+	INFO("Rec3 ESR=0x%lx\n", esr);
+	res = TEST_RESULT_SUCCESS;
+
+undelegate_destroy:
+	ret = host_rmi_granule_undelegate(base);
+destroy_realm:
+	ret2 = host_destroy_realm(&realm);
+
+	if (!ret2) {
+		ERROR("%s(): destroy=%d\n",
+		__func__, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	return res;
+}
+
+/*
+ * Test aims to generate SEA in Realm by
+ * executing instructions in unprotected IPA - Rec0
+ * In Rec 1 , when HIPAS=UNASSIGNED_NS, we expect to get a Data abort.
+ * Then Host will inject SEA to realm.
+ * Realm exception handler runs and returns ESR back to Host
+ * Host validates ESR
+ */
+test_result_t host_realm_sea_unprotected(void)
+{
+
+	bool ret1, ret2;
+	test_result_t res = TEST_RESULT_FAIL;
+	u_register_t ret, base, base_ipa, esr;
+	unsigned int host_call_result;
+	u_register_t exit_reason;
+	struct realm realm;
+	struct rtt_entry rtt;
+	struct rmi_rec_run *run;
+	u_register_t rec_flag[2U] = {RMI_RUNNABLE, RMI_RUNNABLE};
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	if (!host_create_activate_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE,
+			(u_register_t)PAGE_POOL_MAX_SIZE,
+			0UL, rec_flag, 2U)) {
+		return TEST_RESULT_FAIL;
+	}
+	if (!host_create_shared_mem(&realm, NS_REALM_SHARED_MEM_BASE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		goto destroy_realm;
+	}
+
+	/* Can choose any unprotected IPA adr, TFTF_BASE chosen for convenience */
+	base = TFTF_BASE;
+	base_ipa = base | (1UL << (EXTRACT(RMI_FEATURE_REGISTER_0_S2SZ,
+					realm.rmm_feat_reg0) - 1U));
+
+
+	ret = host_rmi_rtt_readentry(realm.rd, base, 3U, &rtt);
+	if (rtt.state != RMI_UNASSIGNED) {
+		ERROR("wrong state\n");
+		goto destroy_realm;
+	}
+
+	run = (struct rmi_rec_run *)realm.run[0];
+	host_shared_data_set_host_val(&realm, 0U, HOST_ARG1_INDEX, base_ipa);
+	host_shared_data_set_host_val(&realm, 1U, HOST_ARG1_INDEX, base_ipa);
+
+	/* Rec0 expect SEA in realm due to IA unprotected IPA page */
+	ret1 = host_enter_realm_execute(&realm, REALM_INSTR_FETCH_CMD,
+			RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		ERROR("Rec0 did not fault\n");
+		goto destroy_realm;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 0U, HOST_ARG2_INDEX);
+	if (((esr & ISS_IFSC_MASK) != IFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_IABORT_CUR_EL)) {
+		ERROR("Rec0 incorrect ESR=0x%lx\n", esr);
+		goto destroy_realm;
+	}
+	INFO("Rec0 ESR=0x%lx\n", esr);
+
+	run = (struct rmi_rec_run *)realm.run[1U];
+
+	/* Rec1 expect rec exit due to DA unprotected IPA page when HIPAS is UNASSIGNED_NS */
+	ret1 = host_enter_realm_execute(&realm, REALM_DATA_ACCESS_CMD,
+			RMI_EXIT_SYNC, 1U);
+
+	if (!ret1 || (run->exit.hpfar >> 4U) != (base_ipa >> PAGE_SIZE_SHIFT)
+		|| (EC_BITS(run->exit.esr) != EC_DABORT_LOWER_EL)
+		|| ((run->exit.esr & ISS_DFSC_MASK) < DFSC_L0_TRANS_FAULT)
+		|| ((run->exit.esr & ISS_DFSC_MASK) > DFSC_L3_TRANS_FAULT)
+		|| ((run->exit.esr & (1UL << ESR_ISS_EABORT_EA_BIT)) != 0U)) {
+		ERROR("Rec1 did not fault exit=0x%lx ret1=%d HPFAR=0x%lx esr=0x%lx\n",
+				run->exit.exit_reason, ret1, run->exit.hpfar, run->exit.esr);
+		goto destroy_realm;
+	}
+	INFO("Host DA FAR=0x%lx, HPFAR=0x%lx\n", run->exit.far, run->exit.hpfar);
+	INFO("Injecting SEA to Realm\n");
+
+	/* Inject SEA back to Realm */
+	run->entry.flags = REC_ENTRY_FLAG_INJECT_SEA;
+
+	/* Rec1 re-entry expect exception handler to run, return ESR */
+	ret = host_realm_rec_enter(&realm, &exit_reason, &host_call_result, 1U);
+	if (ret != RMI_SUCCESS || exit_reason != RMI_EXIT_HOST_CALL) {
+		ERROR("rec1 failed ret=0x%lx exit_reason=0x%lx", ret, run->exit.exit_reason);
+		goto destroy_realm;
+	}
+
+	/* get ESR set by Realm exception handler */
+	esr = host_shared_data_get_realm_val(&realm, 1U, HOST_ARG2_INDEX);
+	if (((esr & ISS_DFSC_MASK) != DFSC_NO_WALK_SEA) || (EC_BITS(esr) != EC_DABORT_CUR_EL)) {
+		ERROR("Rec1 incorrect ESR=0x%lx\n", esr);
+		goto destroy_realm;
+	}
+	INFO("Rec1 ESR=0x%lx\n", esr);
+	res = host_call_result;
+
+destroy_realm:
+	ret2 = host_destroy_realm(&realm);
+
+	if (!ret2) {
+		ERROR("%s(): destroy=%d\n",
+		__func__, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	return res;
+}
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index 934a34e..06c4aa7 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -16,6 +16,10 @@
 	  function="host_realm_multi_rec_multiple_cpu" />
 	  <testcase name="Realm payload multi rec validations"
 	  function="host_realm_multi_rec_multiple_cpu2" />
+	  <testcase name="Realm SEA Empty"
+	  function="host_realm_sea_empty" />
+	  <testcase name="Realm SEA Unprotected"
+	  function="host_realm_sea_unprotected" />
 	  <testcase name="Realm Abort Unassigned RAM"
 	  function="host_realm_abort_unassigned_ram" />
 	  <testcase name="Realm Abort Unassigned Destroyed"