feat(rme): test access outside PAR from Plane N

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I3c5069e14fdf27e6c36bd1e0651ceff4ee7396ef
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 744e14a..a698395 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -83,7 +83,8 @@
 	REALM_ATTESTATION_FAULT,
 	REALM_ENTER_PLANE_N_CMD,
 	REALM_PLANE_N_REG_RW_CMD,
-	REALM_WRITE_BRBCR_EL1
+	REALM_WRITE_BRBCR_EL1,
+	REALM_PLANE_N_INST_FETCH_ABORT
 };
 
 /*
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index 70687f1..248d191 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -336,6 +336,29 @@
 	return false;
 }
 
+static bool test_realm_plane_n_inst_fetch(void)
+{
+	u_register_t esr, far, test_ipa;
+
+	bool ret = test_realm_enter_plane_n();
+
+	if (!ret) {
+		ERROR("Failed to enter Plane N\n");
+		return false;
+	}
+
+	esr = run.exit.esr;
+	far = run.exit.far;
+	test_ipa = realm_shared_data_get_my_host_val(HOST_ARG2_INDEX);
+
+	if ((EC_BITS(esr) != EC_IABORT_LOWER_EL) || (far != test_ipa)) {
+		ERROR("Plane N: incorrect ESR=0x%lx FAR=0x%lx\n", esr, far);
+		return false;
+	}
+
+	return true;
+}
+
 static bool realm_exception_handler(void)
 {
 	u_register_t base, far, esr, elr;
@@ -348,11 +371,24 @@
 	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);
+		if (realm_is_plane0()) {
+			rsi_exit_to_host(HOST_CALL_EXIT_SUCCESS_CMD);
+		} else {
+			realm_shared_data_set_my_realm_val(HOST_ARG3_INDEX, far);
+			psi_exit_to_plane0(PSI_CALL_EXIT_SUCCESS_CMD,
+					0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
+		}
 	}
+
 	realm_printf("Realm Abort fail incorrect FAR=0x%lx ESR=0x%lx ELR=0x%lx\n",
 			far, esr, elr);
-	rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+
+	if (realm_is_plane0()) {
+		rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+	} else {
+		psi_exit_to_plane0(PSI_CALL_EXIT_FAILED_CMD,
+				0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
+	}
 
 	/* Should not return */
 	return false;
@@ -558,6 +594,9 @@
 		case REALM_WRITE_BRBCR_EL1:
 			test_succeed = test_realm_write_brbcr_el1_reg();
 			break;
+		case REALM_PLANE_N_INST_FETCH_ABORT:
+			test_succeed = test_realm_plane_n_inst_fetch();
+			break;
 		default:
 			realm_printf("%s() invalid cmd %u\n", __func__, cmd);
 			break;
diff --git a/realm/realm_plane.c b/realm/realm_plane.c
index 656c688..a29a1ca 100644
--- a/realm/realm_plane.c
+++ b/realm/realm_plane.c
@@ -150,6 +150,10 @@
 			ret = realm_exit_to_host_as_plane_n(HOST_CALL_EXIT_PRINT_CMD, plane_index);
 			run->enter.gprs[0] = ret;
 			return PSI_RETURN_TO_PN;
+		case PSI_CALL_EXIT_SUCCESS_CMD:
+		case PSI_CALL_EXIT_FAILED_CMD:
+			run->exit.gprs[0] = hvc_id;
+			return PSI_RETURN_TO_P0;
 		case PSI_P0_CALL:
 		default:
 			return PSI_RETURN_TO_P0;
@@ -207,8 +211,8 @@
 
 		ret = handle_plane_exit(plane_index, perm_index, run);
 
-		if (ret != PSI_RETURN_TO_PN) {
-			return true;
+		if (ret == PSI_RETURN_TO_P0) {
+			return !(run->exit.gprs[0] == PSI_CALL_EXIT_FAILED_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 2478d4f..a598e16 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
@@ -96,6 +96,7 @@
 	}
 	return host_cmp_result();
 }
+
 /*
  * @Test_Aim@ Test realm payload creation with 3 Aux Planes, enter all Planes
  * Host cannot enter Aux Planes directly,
@@ -307,6 +308,146 @@
 }
 
 /*
+ * @Test_Aim@ Test that an access from Pn to an unprotected IPA ouside the PAR
+ * causes an undefined abort taken to P0.
+ */
+test_result_t host_test_realm_pn_access_outside_par(void)
+{
+	struct realm realm;
+	u_register_t feature_flag0;
+	u_register_t feature_reg0;
+	u_register_t esr, far;
+	u_register_t test_ipa;
+	unsigned long s2sz;
+	struct rmi_rec_run *run;
+	bool iaccess_pass = false, daccess_pass = false;
+	u_register_t rec_flag[] = {RMI_RUNNABLE, RMI_RUNNABLE};
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	/* Test is skipped if S2POE is not supported so to keep it simpler */
+	if (!(are_planes_supported() && is_single_rtt_supported())) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* Read Realm Feature Reg 0 */
+	if (host_rmi_features(0UL, &feature_reg0) != REALM_SUCCESS) {
+		ERROR("%s() failed\n", "host_rmi_features");
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Calculate the IPA range for the realm. This would be below the
+	 * maximum supported by the architecture.
+	 */
+	s2sz = EXTRACT(RMI_FEATURE_REGISTER_0_S2SZ, feature_reg0);
+	feature_flag0 = INPLACE(RMI_FEATURE_REGISTER_0_S2SZ, s2sz - 5U);
+
+	if (!host_create_activate_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			feature_flag0, 0U, RTT_MIN_LEVEL, rec_flag, 2U, 1U)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Generate IPA ouside the PAR to test */
+	test_ipa = TFTF_BASE | (1UL << (EXTRACT(RMI_FEATURE_REGISTER_0_S2SZ,
+						realm.rmm_feat_reg0) + 1U));
+
+	/* Instruct Plane0 to enter Plane1 on REC 0 */
+	host_realm_set_aux_plane_args(&realm, 1U, 0U);
+
+	/* Instruct Plane1 on Rec0 to execute REALM_DATA_ACCESS_CMD */
+	host_shared_data_set_realm_cmd(&realm, REALM_DATA_ACCESS_CMD, 1U, 0U);
+
+	/*
+	 * Argument for REALM_DATA_ACCESS_CMD on Plane 1: Try to do a data
+	 * access to the address @test_ipa.
+	 */
+	host_shared_data_set_host_val(&realm, 1U, 0U, HOST_ARG3_INDEX, test_ipa);
+
+	run = (struct rmi_rec_run *)realm.run[0U];
+
+	daccess_pass = host_enter_realm_execute(&realm,
+						REALM_PLANE_N_EXCEPTION_CMD,
+						RMI_EXIT_HOST_CALL, 0U);
+
+	if (!daccess_pass) {
+		ERROR("Couldn't enter to REC0\n");
+		goto destroy_realm;
+	}
+
+	/*
+	 * Data access from Plane N outside of the PAR causes an exception
+	 * taken to Plane N.
+	 *
+	 * Upon exception, Plane N will return back to Plane 0 passing esr
+	 * and far values, which in turn will be forwarded to the host for
+	 * further validation.
+	 */
+	esr = host_shared_data_get_realm_val(&realm, 1U, 0U, HOST_ARG2_INDEX);
+	far = host_shared_data_get_realm_val(&realm, 1U, 0U, HOST_ARG3_INDEX);
+
+	if (run->exit.exit_reason != RMI_EXIT_HOST_CALL) {
+		ERROR("Rec0 error exit=0x%lx HPFAR=0x%lx \
+				esr=0x%lx far=0x%lx\n",
+				run->exit.exit_reason,
+				run->exit.hpfar,
+				esr, far);
+		daccess_pass = false;
+		goto destroy_realm;
+	}
+
+	if ((EC_BITS(esr) != EC_DABORT_CUR_EL) || (far != test_ipa)) {
+		ERROR("Rec0, Plane 1: incorrect ESR=0x%lx FAR=0x%lx\n", esr, far);
+		daccess_pass = false;
+		goto destroy_realm;
+	}
+	INFO("Rec0 ESR=0x%lx\n", esr);
+
+	/*
+	 * Instruct Plane0 to enter Plane1 on REC 1.
+	 * We use REC 1 for convenience, as this avoids the need to
+	 * reboot REC 0, simplifying the test.
+	 */
+	host_realm_set_aux_plane_args(&realm, 1U, 1U);
+
+	/* Instruct Plane1 on Rec1 to execute REALM_INSTR_FETCH_CMD */
+	host_shared_data_set_realm_cmd(&realm, REALM_INSTR_FETCH_CMD, 1U, 1U);
+
+	/*
+	 * Argument for REALM_INSTR_FETCH_CMD on Plane 1: Address where to
+	 * attempt the instruction fetch.
+	 */
+	host_shared_data_set_host_val(&realm, 1U, 1U, HOST_ARG3_INDEX, test_ipa);
+
+	/*
+	 * Argument for REALM_PLANE_N_INST_FETCH_ABORT on Plane 0.
+	 *
+	 * An instruction fetch outside PAR from Plane N causes a Plane exit
+	 * due to instruction abort. We pass here the expected address at
+	 * which the attempted fetch took place so that Plane 0 can match it
+	 * against the address reported at run.exit.far.
+	 */
+	host_shared_data_set_host_val(&realm, 0U, 1U, HOST_ARG2_INDEX, test_ipa);
+
+	run = (struct rmi_rec_run *)realm.run[0U];
+
+	iaccess_pass = host_enter_realm_execute(&realm,
+						REALM_PLANE_N_INST_FETCH_ABORT,
+						RMI_EXIT_HOST_CALL, 1U);
+
+	if (!iaccess_pass) {
+		ERROR("Failed to cause instruction abort on Plane N\n");
+	}
+
+destroy_realm:
+
+	if (!host_destroy_realm(&realm) || !daccess_pass || !iaccess_pass) {
+		return TEST_RESULT_FAIL;
+	}
+	return host_cmp_result();
+}
+
+/*
  * @Test_Aim@ Test realm payload creation, execution and destruction iteratively
  */
 test_result_t host_test_realm_create_enter(void)
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index b9e83fe..3d88382 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -88,6 +88,8 @@
 	  function="host_realm_reject_set_ripas" />
 	  <testcase name="Realm FEAT_DoubleFault2"
 	  function="host_test_feat_doublefault2" />
+	  <testcase name="Realm Plane N accessing outside PAR"
+	  function="host_test_realm_pn_access_outside_par" />
 	  <!-- 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" />