feat(rmm): add tests for FEAT_TCR2 on RMM

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I4dc16c5c6edcabf993ea30fe53eff5993b5af651
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 a698395..71777c4 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -84,7 +84,8 @@
 	REALM_ENTER_PLANE_N_CMD,
 	REALM_PLANE_N_REG_RW_CMD,
 	REALM_WRITE_BRBCR_EL1,
-	REALM_PLANE_N_INST_FETCH_ABORT
+	REALM_PLANE_N_INST_FETCH_ABORT,
+	REALM_TEST_FEAT_TCR2
 };
 
 /*
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index 248d191..a18e474 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -441,6 +441,42 @@
 	(void)test_realm_data_access_cmd();
 }
 
+static bool test_realm_feat_tcr2(void)
+{
+	/*
+	 * TCR2_EL1.PIE (bit 1) and TCR2_EL1.E0POE (bit 2) only make
+	 * effect when using stage 1 translation, which is not used by
+	 * this test, hence it is safe to write to them.
+	 */
+	u_register_t tcr2_el1_val = (1UL << 1U);
+
+	if (is_feat_tcr2_supported()) {
+		for (unsigned int i = 0U; i < 2U; i++) {
+			write_tcr2_el1(tcr2_el1_val);
+			realm_printf("Testing TCR2_EL1 = 0x%lx\n", read_tcr2_el1());
+
+			/* Test if TCR2_EL1 is preserved after HOST_CALL */
+			if (read_tcr2_el1() != tcr2_el1_val) {
+				return false;
+			}
+
+			tcr2_el1_val <<= 1U;
+		}
+
+		return true;
+	}
+
+	/* Fall back in case FEAT_TCR2 is not implemented */
+	realm_reset_undef_abort_count();
+
+	/* Install exception handler to catch undefined abort */
+	register_custom_sync_exception_handler(realm_sync_exception_handler);
+	write_tcr2_el1(tcr2_el1_val);
+	unregister_custom_sync_exception_handler();
+
+	return (realm_get_undef_abort_count() != 0UL);
+}
+
 /*
  * 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,
@@ -597,6 +633,9 @@
 		case REALM_PLANE_N_INST_FETCH_ABORT:
 			test_succeed = test_realm_plane_n_inst_fetch();
 			break;
+		case REALM_TEST_FEAT_TCR2:
+			test_succeed = test_realm_feat_tcr2();
+			break;
 		default:
 			realm_printf("%s() invalid cmd %u\n", __func__, 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 a598e16..2f864ca 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
@@ -3619,3 +3619,80 @@
 
 	return TEST_RESULT_SUCCESS;
 }
+
+/*
+ * @Test_Aim@ Test to check if FEAT_TCR2
+ */
+test_result_t host_realm_feat_tcr2(void)
+{
+	bool ret1, ret2;
+	struct realm realm;
+	u_register_t feature_flag0 = 0UL;
+	long sl = RTT_MIN_LEVEL;
+	u_register_t rec_flag[MAX_REC_COUNT], tcr2_el1_val;
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	if (is_feat_52b_on_4k_2_supported()) {
+		feature_flag0 = RMI_FEATURE_REGISTER_0_LPA2;
+		sl = RTT_MIN_LEVEL_LPA2;
+	}
+
+	/*
+	 * If FEAT_TCR2 is implemented, the test will write on bits
+	 * TCR2_EL1.PIE and TCR2_EL1.E0POE, so we also need FEAT_S1POE and
+	 * FEAT_S1PIE to be implemented
+	 */
+	if (is_feat_tcr2_supported() &&
+		!(is_feat_s1pie_present() && is_feat_s1poe_present())) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* Enable 2 RECs to run the test */
+	for (unsigned int i = 0U; i < 2; i++) {
+		rec_flag[i] = RMI_RUNNABLE;
+	}
+
+	if (!host_create_activate_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			feature_flag0, 0U, sl, rec_flag, 2, 0U)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	if (is_feat_tcr2_supported()) {
+		/*
+		 * Initialize TCR_EL2 with a starting value.
+		 * TCR2_EL1.PIE (bit 1) and TCR2_EL1.E0POE (bit 2) settings are only
+		 * used for stage 1 translations, which are not used by this test, hence
+		 * it is safe to set those bits here.
+		 */
+		tcr2_el1_val = ((1UL << 1U) | (1UL << 2U));
+		write_tcr2_el1(tcr2_el1_val);
+	}
+
+	for (unsigned int i = 0; i < 2; i++) {
+		ret1 = host_enter_realm_execute(&realm, REALM_TEST_FEAT_TCR2,
+				RMI_EXIT_HOST_CALL, i);
+		if (!ret1) {
+			break;
+		}
+	}
+
+	ret2 = host_destroy_realm(&realm);
+
+	if (is_feat_tcr2_supported()) {
+		if (read_tcr2_el1() != tcr2_el1_val) {
+			ERROR("Host TCR2_EL1 not preserved\n");
+			return TEST_RESULT_FAIL;
+		}
+
+		write_tcr2_el1(0UL);
+	}
+
+	if (!ret1 || !ret2) {
+		ERROR("%s(): enter=%d destroy=%d\n",
+		__func__, ret1, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index 5216a9a..22c0c76 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -91,6 +91,8 @@
 	  function="host_test_feat_doublefault2" />
 	  <testcase name="Realm Plane N accessing outside PAR"
 	  function="host_test_realm_pn_access_outside_par" />
+	  <testcase name="Realm FEAT_TCR2"
+	  function="host_realm_feat_tcr2" />
 	  <!-- 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" />