Merge "test(realm): add test for enabling pmu with multiple rec"
diff --git a/include/runtime_services/host_realm_managment/host_realm_helper.h b/include/runtime_services/host_realm_managment/host_realm_helper.h
index d7c70a9..9f6d934 100644
--- a/include/runtime_services/host_realm_managment/host_realm_helper.h
+++ b/include/runtime_services/host_realm_managment/host_realm_helper.h
@@ -10,6 +10,21 @@
 #include <host_realm_rmi.h>
 #include <tftf_lib.h>
 
+/*
+ * Creates realm, initializes heap and creates RTTs
+ */
+bool host_prepare_realm_payload(struct realm *realm_ptr,
+		u_register_t realm_payload_adr,
+		u_register_t plat_mem_pool_adr,
+		u_register_t realm_pages_size,
+		u_register_t feature_flag,
+		const u_register_t *rec_flag,
+		unsigned int rec_count);
+
+/*
+ * Creates realm, initializes heap, creates RTTs and also
+ * Creates recs
+ */
 bool host_create_realm_payload(struct realm *realm_ptr,
 		u_register_t realm_payload_adr,
 		u_register_t plat_mem_pool_adr,
@@ -17,6 +32,11 @@
 		u_register_t feature_flag,
 		const u_register_t *rec_flag,
 		unsigned int rec_count);
+
+/*
+ * Creates realm, initializes heap, creates RTTs,
+ * creates recs and activate realm
+ */
 bool host_create_activate_realm_payload(struct realm *realm_ptr,
 		u_register_t realm_payload_adr,
 		u_register_t plat_mem_pool_adr,
diff --git a/include/runtime_services/host_realm_managment/host_realm_pmu.h b/include/runtime_services/host_realm_managment/host_realm_pmu.h
index 48089f3..844bb29 100644
--- a/include/runtime_services/host_realm_managment/host_realm_pmu.h
+++ b/include/runtime_services/host_realm_managment/host_realm_pmu.h
@@ -20,7 +20,7 @@
 #define PMU_CLEAR_ALL	0x1FFFFFFFF
 
 /* Number of event counters implemented */
-#define GET_CNT_NUM	\
+#define GET_PMU_CNT	\
 	((read_pmcr_el0() >> PMCR_EL0_N_SHIFT) & PMCR_EL0_N_MASK)
 
 void host_set_pmu_state(void);
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 632fdaa..7cfffe6 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -50,6 +50,7 @@
 	REALM_INSTR_FETCH_CMD,
 	REALM_DATA_ACCESS_CMD,
 	REALM_PMU_CYCLE,
+	REALM_PMU_COUNTER,
 	REALM_PMU_EVENT,
 	REALM_PMU_PRESERVE,
 	REALM_PMU_INTERRUPT,
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index 3016a4d..b58949b 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -9,6 +9,7 @@
 #define REALM_TESTS_H
 
 bool test_pmuv3_cycle_works_realm(void);
+bool test_pmuv3_counter(void);
 bool test_pmuv3_event_works_realm(void);
 bool test_pmuv3_rmm_preserves(void);
 bool test_pmuv3_overflow_interrupt(void);
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index ce25f43..5eec5b8 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -252,6 +252,9 @@
 		case REALM_PMU_CYCLE:
 			test_succeed = test_pmuv3_cycle_works_realm();
 			break;
+		case REALM_PMU_COUNTER:
+			test_succeed = test_pmuv3_counter();
+			break;
 		case REALM_PMU_EVENT:
 			test_succeed = test_pmuv3_event_works_realm();
 			break;
diff --git a/realm/realm_pmuv3.c b/realm/realm_pmuv3.c
index 3ae597d..0d4782a 100644
--- a/realm/realm_pmuv3.c
+++ b/realm/realm_pmuv3.c
@@ -162,6 +162,21 @@
 	return false;
 }
 
+/* Test if max counter available is same as that programmed by host */
+bool test_pmuv3_counter(void)
+{
+	uint64_t num_cnts, num_cnts_host;
+
+	num_cnts_host = realm_shared_data_get_my_host_val(HOST_ARG1_INDEX);
+	num_cnts = GET_PMU_CNT;
+	realm_printf("CPU=%u num_cnts=%lu num_cnts_host=%lu\n", read_mpidr_el1() & MPID_MASK,
+			num_cnts, num_cnts_host);
+	if (num_cnts == num_cnts_host) {
+		return true;
+	}
+	return false;
+}
+
 /*
  * Try an event counter with some NOPs to see if it works.
  */
@@ -170,7 +185,7 @@
 	u_register_t evcounter_start;
 	u_register_t evcounter_end;
 
-	if (GET_CNT_NUM == 0) {
+	if (GET_PMU_CNT == 0) {
 		realm_printf("no event counters implemented\n");
 		return false;
 	}
@@ -208,7 +223,7 @@
 	u_register_t ctr_end[MAX_COUNTERS] = {0};
 	u_register_t ctr_cfg_end[MAX_COUNTERS] = {0};
 	u_register_t pmu_cfg_end[3];
-	unsigned int impl_ev_ctrs = GET_CNT_NUM;
+	unsigned int impl_ev_ctrs = GET_PMU_CNT;
 
 	realm_printf("testing %u event counters\n", impl_ev_ctrs);
 
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_pmuv3.c b/tftf/tests/runtime_services/host_realm_managment/host_pmuv3.c
index 042132e..ceca36d 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_pmuv3.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_pmuv3.c
@@ -106,7 +106,7 @@
 {
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 	struct pmu_registers *pmu_ptr = &pmu_state[core_pos];
-	unsigned int num_cnts = GET_CNT_NUM;
+	unsigned int num_cnts = GET_PMU_CNT;
 	unsigned long val;
 
 	val = read_pmcr_el0() | PMCR_EL0_DP_BIT;
@@ -185,7 +185,7 @@
 {
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 	struct pmu_registers *pmu_ptr = &pmu_state[core_pos];
-	unsigned int num_cnts = GET_CNT_NUM;
+	unsigned int num_cnts = GET_PMU_CNT;
 	unsigned long val, read_val;
 
 	CHECK_PMREG(pmcr_el0);
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
index 59a0012..2c58919 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
@@ -105,13 +105,13 @@
 	return true;
 }
 
-bool host_create_realm_payload(struct realm *realm_ptr,
-			       u_register_t realm_payload_adr,
-			       u_register_t plat_mem_pool_adr,
-			       u_register_t realm_pages_size,
-			       u_register_t feature_flag,
-			       const u_register_t *rec_flag,
-			       unsigned int rec_count)
+bool host_prepare_realm_payload(struct realm *realm_ptr,
+				u_register_t realm_payload_adr,
+				u_register_t plat_mem_pool_adr,
+				u_register_t realm_pages_size,
+				u_register_t feature_flag,
+				const u_register_t *rec_flag,
+				unsigned int rec_count)
 {
 	int8_t value;
 
@@ -250,12 +250,6 @@
 		return false;
 	}
 
-	if (host_realm_init_ipa_state(realm_ptr, 0U, 0U, 1ULL << 32)
-		!= RMI_SUCCESS) {
-		ERROR("%s() failed\n", "host_realm_init_ipa_state");
-		goto destroy_realm;
-	}
-
 	/* RTT map Realm image */
 	if (host_realm_map_payload_image(realm_ptr, realm_payload_adr) !=
 			REALM_SUCCESS) {
@@ -263,12 +257,6 @@
 		goto destroy_realm;
 	}
 
-	/* Create REC */
-	if (host_realm_rec_create(realm_ptr) != REALM_SUCCESS) {
-		ERROR("%s() failed\n", "host_realm_rec_create");
-		goto destroy_realm;
-	}
-
 	realm_ptr->payload_created = true;
 
 	return true;
@@ -283,6 +271,48 @@
 	return false;
 }
 
+bool host_create_realm_payload(struct realm *realm_ptr,
+			       u_register_t realm_payload_adr,
+			       u_register_t plat_mem_pool_adr,
+			       u_register_t realm_pages_size,
+			       u_register_t feature_flag,
+			       const u_register_t *rec_flag,
+			       unsigned int rec_count)
+{
+	bool ret;
+
+	ret = host_prepare_realm_payload(realm_ptr,
+			realm_payload_adr,
+			plat_mem_pool_adr,
+			realm_pages_size,
+			feature_flag,
+			rec_flag,
+			rec_count);
+	if (!ret) {
+		goto destroy_realm;
+	} else {
+		/* Create REC */
+		if (host_realm_rec_create(realm_ptr) != REALM_SUCCESS) {
+			ERROR("%s() failed\n", "host_realm_rec_create");
+			goto destroy_realm;
+		}
+
+		if (host_realm_init_ipa_state(realm_ptr, 0U, 0U, 1ULL << 32)
+			!= RMI_SUCCESS) {
+			ERROR("%s() failed\n", "host_realm_init_ipa_state");
+			goto destroy_realm;
+		}
+	}
+	return true;
+
+destroy_realm:
+	if (host_realm_destroy(realm_ptr) != REALM_SUCCESS) {
+		ERROR("%s() failed\n", "host_realm_destroy");
+	}
+	realm_ptr->payload_created = false;
+	return false;
+}
+
 bool host_create_activate_realm_payload(struct realm *realm_ptr,
 			u_register_t realm_payload_adr,
 			u_register_t plat_mem_pool_adr,
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
index d308784..4774dfc 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
@@ -21,6 +21,7 @@
 
 static uint64_t is_secondary_cpu_on;
 static struct realm realm;
+static struct realm realm1;
 
 /*
  * Test tries to create max Rec
@@ -544,3 +545,248 @@
 	}
 	return ret3;
 }
+
+/*
+ * Test PMU counters available to each REC matches that programmed by host
+ * Test PMU counters are preserved for each rec
+ */
+static test_result_t cpu_on_handler_pmu(void)
+{
+	bool ret1;
+	unsigned int i;
+
+	spin_lock(&secondary_cpu_lock);
+	i = is_secondary_cpu_on++;
+	spin_unlock(&secondary_cpu_lock);
+	ret1 = host_enter_realm_execute(&realm, REALM_PMU_COUNTER, RMI_EXIT_HOST_CALL, i);
+	if (!ret1) {
+		return TEST_RESULT_FAIL;
+	}
+	ret1 = host_enter_realm_execute(&realm1, REALM_PMU_COUNTER, RMI_EXIT_HOST_CALL, i);
+	if (!ret1) {
+		return TEST_RESULT_FAIL;
+	}
+	ret1 = host_enter_realm_execute(&realm, REALM_PMU_PRESERVE, RMI_EXIT_HOST_CALL, i);
+	if (!ret1) {
+		return TEST_RESULT_FAIL;
+	}
+	ret1 = host_enter_realm_execute(&realm1, REALM_PMU_PRESERVE, RMI_EXIT_HOST_CALL, i);
+	if (ret1) {
+		return TEST_RESULT_SUCCESS;
+	}
+	return TEST_RESULT_FAIL;
+}
+
+/*
+ * Test realm creation with more PMU counter than available, expect failure
+ * Test realm creation with 0 PMU counter
+ * expect failure if FEAT_HPMN0 is not supported
+ * expect success if FEAT_HPMN0 is supported
+ * Create 2 Realms first one with MAX PMU counters
+ * second realm with lesser PMU counter than available
+ * Schedule multiple rec on multiple CPU
+ * Test PMU counters available to each REC matches that programmed by host
+ * Test PMU counters are preserved for each rec
+ */
+test_result_t host_realm_pmuv3_mul_rec(void)
+{
+	u_register_t feature_flag;
+	u_register_t rmm_feat_reg0;
+	u_register_t rec_flag[8U] = {RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE,
+		RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE, RMI_RUNNABLE};
+	bool ret1 = 0U, ret2;
+	unsigned int num_cnts, i = 0U;
+	u_register_t other_mpidr, my_mpidr, ret;
+	int cpu_node;
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	/* Get Max PMU counter implemented through RMI_FEATURES */
+	if (host_rmi_features(0UL, &rmm_feat_reg0) != REALM_SUCCESS) {
+		ERROR("%s() failed\n", "host_rmi_features");
+		return TEST_RESULT_FAIL;
+	}
+
+	num_cnts = EXTRACT(RMI_FEATURE_REGISTER_0_PMU_NUM_CTRS, rmm_feat_reg0);
+	host_set_pmu_state();
+	is_secondary_cpu_on = 0;
+	my_mpidr = read_mpidr_el1() & MPID_MASK;
+
+	if (num_cnts == 0U) {
+		ERROR("No PMU counters implemented\n");
+		return TEST_RESULT_SKIPPED;
+	}
+
+	feature_flag = RMI_FEATURE_REGISTER_0_PMU_EN |
+			INPLACE(FEATURE_PMU_NUM_CTRS, num_cnts + 1U);
+
+
+	/* Request more PMU counter than total, expect failure */
+	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,
+			feature_flag, rec_flag, 1U)) {
+		ERROR("Realm create should have failed\n");
+		host_destroy_realm(&realm);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Request 0 PMU counter */
+	feature_flag = RMI_FEATURE_REGISTER_0_PMU_EN |
+			INPLACE(FEATURE_PMU_NUM_CTRS, 0U);
+
+	ret1 = 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,
+				feature_flag, rec_flag, 1U);
+
+	if (!get_feat_hpmn0_supported()) {
+		if (ret1) {
+			ERROR("Realm create with 0 PMU Counter should have failed\n");
+			host_destroy_realm(&realm);
+			return TEST_RESULT_FAIL;
+		}
+	} else {
+		if (!ret1) {
+			ERROR("Realm create with 0 PMU Counter should not have failed\n");
+			return TEST_RESULT_FAIL;
+		}
+		host_destroy_realm(&realm);
+	}
+
+	/* Test 2 create first realm with max PMU counters */
+	feature_flag = RMI_FEATURE_REGISTER_0_PMU_EN |
+			INPLACE(FEATURE_PMU_NUM_CTRS, num_cnts);
+
+	/* Prepare realm0, create recs for realm0 later */
+	if (!host_prepare_realm_payload(&realm, (u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE,
+			(u_register_t)PAGE_POOL_MAX_SIZE,
+			feature_flag, rec_flag, MAX_REC_COUNT)) {
+		goto test_exit;
+		return TEST_RESULT_FAIL;
+	}
+	if (!host_create_shared_mem(&realm, NS_REALM_SHARED_MEM_BASE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		goto test_exit;
+	}
+
+	/* Second realm with less num of PMU counters */
+	feature_flag = RMI_FEATURE_REGISTER_0_PMU_EN |
+			INPLACE(FEATURE_PMU_NUM_CTRS, num_cnts - 1U);
+
+	if (!host_create_activate_realm_payload(&realm1, (u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE + PAGE_POOL_MAX_SIZE,
+			(u_register_t)PAGE_POOL_MAX_SIZE,
+			feature_flag, rec_flag, MAX_REC_COUNT)) {
+		goto test_exit2;
+	}
+	if (!host_create_shared_mem(&realm1, NS_REALM_SHARED_MEM_BASE + NS_REALM_SHARED_MEM_SIZE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		goto test_exit2;
+	}
+
+	/* create realm0 recs, activate realm0 */
+	if (host_realm_rec_create(&realm) != REALM_SUCCESS) {
+		ERROR("%s() failed\n", "host_realm_rec_create");
+		goto test_exit2;
+	}
+
+	if (host_realm_init_ipa_state(&realm, 0U, 0U, 1ULL << 32)
+		!= RMI_SUCCESS) {
+		ERROR("%s() failed\n", "host_realm_init_ipa_state");
+		goto test_exit2;
+	}
+
+	if (host_realm_activate(&realm) != REALM_SUCCESS) {
+		ERROR("%s() failed\n", "host_realm_activate");
+		goto test_exit2;
+	}
+	INFO("MAX PMU Counter=%u\n", num_cnts);
+
+	/* Pass num of PMU counters programmed to realm */
+	for (unsigned int j = 0U; j < MAX_REC_COUNT; j++) {
+		host_shared_data_set_host_val(&realm, j, HOST_ARG1_INDEX, num_cnts);
+		host_shared_data_set_host_val(&realm1, j, HOST_ARG1_INDEX, num_cnts - 1U);
+	}
+
+	/*
+	 * Enter realm0 rec0 test PMU counters available is same as that programmed by host
+	 * Validation is done by the Realm and will return error if the count does not match
+	 */
+	ret1 = host_enter_realm_execute(&realm, REALM_PMU_COUNTER, RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		goto test_exit2;
+	}
+
+	/* Enter realm1 rec0 test PMU counters available is same as that programmed by host */
+	ret1 = host_enter_realm_execute(&realm1, REALM_PMU_COUNTER, RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		goto test_exit2;
+	}
+
+	/* Test if Realm0 rec0 entering/exiting preserves PMU state */
+	ret1 = host_enter_realm_execute(&realm, REALM_PMU_PRESERVE, RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		goto test_exit2;
+	}
+
+	/* Test if Realm1 rec0 entering/exiting preserves PMU state */
+	ret1 = host_enter_realm_execute(&realm1, REALM_PMU_PRESERVE, RMI_EXIT_HOST_CALL, 0U);
+	if (!ret1) {
+		goto test_exit2;
+	}
+
+	/* Turn on all CPUs */
+	for_each_cpu(cpu_node) {
+		if (i == (MAX_REC_COUNT - 1U)) {
+			break;
+		}
+		other_mpidr = tftf_get_mpidr_from_node(cpu_node);
+		if (other_mpidr == my_mpidr) {
+			continue;
+		}
+
+		/* Power on the other CPU */
+		ret = tftf_try_cpu_on(other_mpidr, (uintptr_t)cpu_on_handler_pmu, 0);
+		if (ret != PSCI_E_SUCCESS) {
+			ERROR("TFTF CPU ON failed\n");
+			goto test_exit2;
+		}
+		i++;
+	}
+
+	/* Wait for all CPU to power up */
+	while (is_secondary_cpu_on != MAX_REC_COUNT - 1U) {
+		waitms(100);
+	}
+
+	/* Wait for all CPU to power down */
+	for_each_cpu(cpu_node) {
+		other_mpidr = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+		if (other_mpidr == my_mpidr) {
+			continue;
+		}
+		while (tftf_psci_affinity_info(other_mpidr, MPIDR_AFFLVL0) != PSCI_STATE_OFF) {
+			continue;
+		}
+	}
+
+test_exit2:
+	ret2 = host_destroy_realm(&realm1);
+	if (!ret1 || !ret2) {
+		ERROR("%s() enter=%u destroy=%u\n", __func__, ret1, ret2);
+	}
+test_exit:
+	ret2 = host_destroy_realm(&realm);
+	if (!ret1 || !ret2) {
+		ERROR("%s() enter=%u destroy=%u\n", __func__, ret1, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	if (!host_check_pmu_state()) {
+		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 9948ff1..51cb09f 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -58,6 +58,8 @@
 	  function="host_realm_pmuv3_rmm_preserves" />
 	  <testcase name="PMUv3 overflow interrupt"
 	  function="host_realm_pmuv3_overflow_interrupt" />
+	  <testcase name="PMUv3 multiple rec validations"
+	  function="host_realm_pmuv3_mul_rec" />
 	  <testcase name="Test Secure interrupt can preempt Realm EL1"
 	  function="host_realm_sec_interrupt_can_preempt_rl" />
 	  <testcase name="Check that FPU state registers context is preserved in RL/SE/NS"