feat(rme): add tests for FEAT_MPAM on Realms

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I6e138cbf121793bdaaa3a44824c0dbff74daced1
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index 124cc4e..8ca4cdf 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -431,6 +431,13 @@
 		}								\
 	} while (false)
 
+#define SKIP_TEST_IF_FEAT_MPAM_NOT_SUPPORTED()					\
+	do {									\
+		if (is_feat_mpam_supported() == 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_helpers.h b/include/lib/aarch64/arch_helpers.h
index 35d2454..da1794d 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -522,6 +522,9 @@
 DEFINE_RENAME_SYSREG_RW_FUNCS(mpam3_el3, MPAM3_EL3)
 DEFINE_RENAME_SYSREG_RW_FUNCS(mpam2_el2, MPAM2_EL2)
 DEFINE_RENAME_SYSREG_RW_FUNCS(mpamhcr_el2, MPAMHCR_EL2)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(mpamidr_el1, MPAMIDR_EL1)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(mpam0_el1, MPAM0_EL1)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(mpam1_el1, MPAM1_EL1)
 
 DEFINE_RENAME_SYSREG_RW_FUNCS(scxtnum_el2, SCXTNUM_EL2)
 DEFINE_RENAME_SYSREG_RW_FUNCS(scxtnum_el1, SCXTNUM_EL1)
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 fecb27b..0d75cbb 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -45,6 +45,8 @@
 enum realm_cmd {
 	REALM_SLEEP_CMD = 1U,
 	REALM_LOOP_CMD,
+	REALM_MPAM_ACCESS,
+	REALM_MPAM_PRESENT,
 	REALM_MULTIPLE_REC_PSCI_DENIED_CMD,
 	REALM_MULTIPLE_REC_MULTIPLE_CPU_CMD,
 	REALM_GET_RSI_VERSION,
diff --git a/realm/include/realm_helpers.h b/realm/include/realm_helpers.h
index 7ed8f9b..c5af1be 100644
--- a/realm/include/realm_helpers.h
+++ b/realm/include/realm_helpers.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -12,6 +12,20 @@
 
 /* Generate 64-bit random number */
 unsigned long long realm_rand64(void);
+
+/* Reset the undefined aborts counter */
+void realm_reset_undef_abort_count(void);
+
+/* Return the undefined aborts counter value */
+unsigned int realm_get_undef_abort_count(void);
+
+/*
+ * Sync exception handler.
+ * If the exception is an undefined abort, it increases the value
+ * of the abort counter and returns 'true'. Otherwise, it returns 'false'
+ */
+bool realm_sync_exception_handler(void);
+
 /*
  * Function to enter Aux Plane from Primary Plane
  * arg1 == plane index
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index 2c1d3a1..0f8a984 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2023-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -30,6 +30,7 @@
 bool test_realm_sctlr2_ease(void);
 bool test_realm_attestation(void);
 bool test_realm_attestation_fault(void);
+bool test_realm_mpam_undef_abort(void);
 
 #endif /* REALM_TESTS_H */
 
diff --git a/realm/realm.mk b/realm/realm.mk
index 7d21ed5..6281779 100644
--- a/realm/realm.mk
+++ b/realm/realm.mk
@@ -41,6 +41,7 @@
 	realm_rsi.c							\
 	realm_shared_data.c						\
 	realm_simd.c							\
+	realm_mpam.c							\
 	)
 
 REALM_SOURCES += lib/${ARCH}/cache_helpers.S				\
diff --git a/realm/realm_helpers.c b/realm/realm_helpers.c
index bf47efc..f11e2f3 100644
--- a/realm/realm_helpers.c
+++ b/realm/realm_helpers.c
@@ -1,15 +1,19 @@
 /*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <debug.h>
 #include <stdlib.h>
+
 #include <realm_helpers.h>
 #include <realm_psi.h>
 #include <realm_rsi.h>
 #include <smccc.h>
 
+static unsigned int volatile realm_got_undef_abort;
+
 /* Generate 64-bit random number */
 unsigned long long realm_rand64(void)
 {
@@ -39,3 +43,27 @@
 
 	return host_cal.gprs[0];
 }
+
+bool realm_sync_exception_handler(void)
+{
+	uint64_t esr_el1 = read_esr_el1();
+
+	if (EC_BITS(esr_el1) == EC_UNKNOWN) {
+		realm_printf("received undefined abort. "
+			     "ESR_EL1: 0x%llx ELR_EL1: 0x%llx\n",
+			     esr_el1, read_elr_el1());
+		realm_got_undef_abort++;
+	}
+
+	return true;
+}
+
+void realm_reset_undef_abort_count(void)
+{
+	realm_got_undef_abort = 0U;
+}
+
+unsigned int realm_get_undef_abort_count(void)
+{
+	return realm_got_undef_abort;
+}
diff --git a/realm/realm_mpam.c b/realm/realm_mpam.c
new file mode 100644
index 0000000..913e451
--- /dev/null
+++ b/realm/realm_mpam.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <sync.h>
+#include <realm_helpers.h>
+
+/* Check if Realm gets undefined abort when it access MPAM registers */
+bool test_realm_mpam_undef_abort(void)
+{
+	realm_reset_undef_abort_count();
+
+	/* Install exception handler to catch undefined abort */
+	register_custom_sync_exception_handler(realm_sync_exception_handler);
+	write_mpam0_el1(0UL);
+	unregister_custom_sync_exception_handler();
+
+	return (realm_get_undef_abort_count() != 0UL);
+}
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index aeadb9e..1e24f21 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022-2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -390,6 +390,13 @@
 		case REALM_PLANE_N_REG_RW_CMD:
 			test_succeed = test_realm_enter_plane_n_reg_rw();
 			break;
+		case REALM_MPAM_ACCESS:
+			test_succeed = test_realm_mpam_undef_abort();
+			break;
+		case REALM_MPAM_PRESENT:
+			/* FEAT_MPAM must be hidden to the Realm */
+			test_succeed = !is_feat_mpam_supported();
+			break;
 		case REALM_MULTIPLE_REC_PSCI_DENIED_CMD:
 			test_succeed = test_realm_multiple_rec_psci_denied_cmd();
 			break;
diff --git a/realm/realm_simd.c b/realm/realm_simd.c
index 2eb3eab..0ff1710 100644
--- a/realm/realm_simd.c
+++ b/realm/realm_simd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2023-2025, Arm Limited. All rights reserved.
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
@@ -9,9 +9,11 @@
 #include <assert.h>
 #include <debug.h>
 #include <stdlib.h>
+
 #include <sync.h>
 #include <lib/extensions/fpu.h>
 #include <lib/extensions/sve.h>
+#include <realm_helpers.h>
 
 #include <host_realm_simd.h>
 #include <host_shared_data.h>
@@ -34,8 +36,6 @@
 static fpu_cs_regs_t rl_fpu_cs_regs_write;
 static fpu_cs_regs_t rl_fpu_cs_regs_read;
 
-static int volatile realm_got_undef_abort;
-
 /* Returns the maximum supported VL. This test is called only by sve Realm */
 bool test_realm_sve_rdvl(void)
 {
@@ -188,35 +188,17 @@
 	return rc;
 }
 
-static bool realm_sync_exception_handler(void)
-{
-	uint64_t esr_el1 = read_esr_el1();
-
-	if (EC_BITS(esr_el1) == EC_UNKNOWN) {
-		realm_printf("received undefined abort. "
-			     "esr_el1: 0x%llx elr_el1: 0x%llx\n",
-			     esr_el1, read_elr_el1());
-		realm_got_undef_abort++;
-	}
-
-	return true;
-}
-
 /* Check if Realm gets undefined abort when it accesses SVE functionality */
 bool test_realm_sve_undef_abort(void)
 {
-	realm_got_undef_abort = 0UL;
+	realm_reset_undef_abort_count();
 
-	/* install exception handler to catch undef abort */
+	/* Install exception handler to catch undefined abort */
 	register_custom_sync_exception_handler(&realm_sync_exception_handler);
 	(void)sve_rdvl_1();
 	unregister_custom_sync_exception_handler();
 
-	if (realm_got_undef_abort == 0UL) {
-		return false;
-	}
-
-	return true;
+	return (realm_get_undef_abort_count() != 0U);
 }
 
 /* Reads and returns the ID_AA64PFR1_EL1 and ID_AA64SMFR0_EL1 registers */
@@ -240,16 +222,12 @@
 /* Check if Realm gets undefined abort when it access SME functionality */
 bool test_realm_sme_undef_abort(void)
 {
-	realm_got_undef_abort = 0UL;
+	realm_reset_undef_abort_count();
 
-	/* install exception handler to catch undef abort */
+	/* Install exception handler to catch undefined abort */
 	register_custom_sync_exception_handler(&realm_sync_exception_handler);
 	(void)read_svcr();
 	unregister_custom_sync_exception_handler();
 
-	if (realm_got_undef_abort == 0UL) {
-		return false;
-	}
-
-	return true;
+	return (realm_get_undef_abort_count() != 0U);
 }
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_mpam_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_mpam_tests.c
new file mode 100644
index 0000000..7967828
--- /dev/null
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_mpam_tests.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <host_realm_helper.h>
+#include <host_realm_mem_layout.h>
+#include <host_shared_data.h>
+#include <test_helpers.h>
+
+static test_result_t realm_test_feat_mpam(enum realm_cmd cmd)
+{
+	bool ret1, ret2;
+	u_register_t rec_flag[] = {RMI_RUNNABLE};
+	struct realm realm;
+	u_register_t feature_flag = 0UL;
+	long sl = RTT_MIN_LEVEL;
+
+	assert((cmd >= REALM_MPAM_ACCESS) && (cmd <= REALM_MPAM_PRESENT));
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+	SKIP_TEST_IF_FEAT_MPAM_NOT_SUPPORTED();
+
+	if (is_feat_52b_on_4k_2_supported()) {
+		feature_flag = RMI_FEATURE_REGISTER_0_LPA2;
+		sl = RTT_MIN_LEVEL_LPA2;
+	}
+
+	if (!host_create_activate_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			feature_flag, sl, rec_flag, 1U, 0U)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	ret1 = host_enter_realm_execute(&realm, cmd, RMI_EXIT_HOST_CALL, 0U);
+	ret2 = host_destroy_realm(&realm);
+
+	if (!ret1 || !ret2) {
+		ERROR("%s(): enter=%u destroy=%u\n",
+		__func__, ret1, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	return true;
+}
+
+/*
+ * @Test_Aim@ Test that FEAT_MPAM is hidden to the realm
+ */
+test_result_t host_realm_hide_feat_mpam(void)
+{
+	return realm_test_feat_mpam(REALM_MPAM_PRESENT);
+}
+
+/*
+ * @Test_Aim@ Test that access to MPAM registers triggers an undefined abort
+ * taken into the realm.
+ */
+test_result_t host_realm_mpam_undef_abort(void)
+{
+	return realm_test_feat_mpam(REALM_MPAM_ACCESS);
+}
diff --git a/tftf/tests/tests-realm-payload.mk b/tftf/tests/tests-realm-payload.mk
index b6833e1..1fe01ed 100644
--- a/tftf/tests/tests-realm-payload.mk
+++ b/tftf/tests/tests-realm-payload.mk
@@ -16,6 +16,7 @@
 		host_realm_spm.c					\
 		host_realm_payload_simd_tests.c				\
 		host_realm_lpa2_tests.c					\
+		host_realm_mpam_tests.c					\
 	)
 
 TESTS_SOURCES	+=							\
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index 7cc7866..2c905fb 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -144,5 +144,10 @@
 	  <!-- Invoke RMI calls related to PDEV management -->
 	  <testcase name="Invoke RMI PDEV calls "
 	  function="host_test_rmi_pdev_calls" />
+	  <!-- Test cases related to FEAT_MPAM -->
+	  <testcase name="Test that FEAT_MPAM is hidden to the Realm"
+	  function="host_realm_hide_feat_mpam" />
+	  <testcase name="Test that access to FEAT_MPAM from Realm causes an undef abort taken to the Realm"
+	  function="host_realm_mpam_undef_abort" />
   </testsuite>
 </testsuites>