test(rmm-eac4): add testcase for CPU_ON denied

- Testcase creates multiple rec
- Host receives CPU_ON request from realm
- Host calls PSCI_CCMPLETE with denied status

Signed-off-by: Shruti Gupta <shruti.gupta@arm.com>
Change-Id: Ie89b7a3b9603916275913a273751210350075e96
diff --git a/Makefile b/Makefile
index 54293b5..e104814 100644
--- a/Makefile
+++ b/Makefile
@@ -121,6 +121,7 @@
 
 # Set flags for Realm Payload Tests
 ifeq (${ENABLE_REALM_PAYLOAD_TESTS},1)
+ARM_ARCH_MINOR := 5
 BRANCH_PROTECTION := 2
 endif
 
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 c8f9238..83b21bf 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -41,6 +41,7 @@
  */
 enum realm_cmd {
 	REALM_SLEEP_CMD = 1U,
+	REALM_MULTIPLE_REC_PSCI_DENIED_CMD,
 	REALM_GET_RSI_VERSION,
 	REALM_PMU_CYCLE,
 	REALM_PMU_EVENT,
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index 1351415..ac839ac 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -22,6 +22,7 @@
 bool test_realm_sve_fill_regs(void);
 bool test_realm_sve_cmp_regs(void);
 bool test_realm_sve_undef_abort(void);
+bool test_realm_multiple_rec_psci_denied_cmd(void);
 
 #endif /* REALM_TESTS_H */
 
diff --git a/realm/realm.mk b/realm/realm.mk
index debbda2..9b0a230 100644
--- a/realm/realm.mk
+++ b/realm/realm.mk
@@ -28,6 +28,7 @@
 	aarch64/realm_exceptions.S					\
 	realm_debug.c							\
 	realm_interrupt.c						\
+	realm_multiple_rec.c						\
 	realm_pauth.c							\
 	realm_payload_main.c						\
 	realm_pmuv3.c							\
diff --git a/realm/realm_multiple_rec.c b/realm/realm_multiple_rec.c
new file mode 100644
index 0000000..bc357bc
--- /dev/null
+++ b/realm/realm_multiple_rec.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <fpu.h>
+#include <host_realm_helper.h>
+#include <host_shared_data.h>
+#include <psci.h>
+#include "realm_def.h"
+#include <realm_rsi.h>
+#include <realm_tests.h>
+#include <realm_psci.h>
+#include <tftf_lib.h>
+
+#define CXT_ID_MAGIC 0x100
+static uint64_t is_secondary_cpu_booted;
+
+static void rec1_handler(u_register_t cxt_id)
+{
+	realm_printf("Realm: running on CPU = 0x%lx cxt_id= 0x%lx\n",
+			read_mpidr_el1() & MPID_MASK, cxt_id);
+	if (cxt_id < CXT_ID_MAGIC || cxt_id > CXT_ID_MAGIC + MAX_REC_COUNT) {
+		realm_printf("Realm: Wrong cxt_id\n");
+		rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+	}
+	is_secondary_cpu_booted++;
+	realm_cpu_off();
+}
+
+static void rec2_handler(u_register_t cxt_id)
+{
+	rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+}
+
+bool test_realm_multiple_rec_psci_denied_cmd(void)
+{
+	u_register_t ret;
+
+	is_secondary_cpu_booted = 0U;
+	ret = realm_cpu_on(1U, (uintptr_t)rec1_handler, 0x100);
+	if (ret != PSCI_E_DENIED) {
+		return false;
+	}
+
+	if (is_secondary_cpu_booted != 0U) {
+		rsi_exit_to_host(HOST_CALL_EXIT_FAILED_CMD);
+	}
+
+	ret = realm_psci_affinity_info(1U, MPIDR_AFFLVL0);
+	if (ret != PSCI_STATE_OFF) {
+		realm_printf("CPU 1 should have been off\n");
+		return false;
+	}
+
+	ret = realm_cpu_on(2U, (uintptr_t)rec2_handler, 0x102);
+	if (ret != PSCI_E_ALREADY_ON) {
+		realm_printf("CPU 2 should have been already on\n");
+		return false;
+	}
+	return true;
+}
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index 43cbf2d..2271221 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -74,6 +74,9 @@
 			realm_sleep_cmd();
 			test_succeed = true;
 			break;
+		case REALM_MULTIPLE_REC_PSCI_DENIED_CMD:
+			test_succeed = test_realm_multiple_rec_psci_denied_cmd();
+			break;
 		case REALM_PAUTH_SET_CMD:
 			test_succeed = test_realm_pauth_set_cmd();
 			break;
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 bd120ca..1208d62 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
@@ -59,3 +59,140 @@
 
 	return TEST_RESULT_SUCCESS;
 }
+
+/*
+ * Test creates 3 Rec
+ * Rec0 requests CPU ON for rec 1
+ * Host denies CPU On for rec 1
+ * Host tried to enter rec 1 and fails
+ * Host re-enters rec 0
+ * Rec 0 checks CPU ON is denied
+ * Rec0 requests CPU ON for rec 2
+ * Host denies CPU On which should fail as rec is runnable
+ * Host allows CPU ON and re-enters rec 0
+ * Rec 0 checks return already_on
+ */
+test_result_t host_realm_multi_rec_psci_denied(void)
+{
+	struct realm *realm_ptr;
+	bool ret1, ret2;
+	u_register_t ret;
+	unsigned int host_call_result;
+	u_register_t exit_reason;
+	unsigned int rec_num;
+	struct rmi_rec_run *run;
+	/* Create 3 rec Rec 0 and 2 are runnable, Rec 1 in not runnable */
+	u_register_t rec_flag[] = {RMI_RUNNABLE, RMI_NOT_RUNNABLE, RMI_RUNNABLE};
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	if (!host_create_realm_payload((u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE,
+			(u_register_t)(PAGE_POOL_MAX_SIZE +
+			NS_REALM_SHARED_MEM_SIZE),
+			(u_register_t)PAGE_POOL_MAX_SIZE,
+			0UL, rec_flag, 3U)) {
+		return TEST_RESULT_FAIL;
+	}
+	if (!host_create_shared_mem(NS_REALM_SHARED_MEM_BASE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	ret1 = host_enter_realm_execute(REALM_MULTIPLE_REC_PSCI_DENIED_CMD, &realm_ptr,
+			RMI_EXIT_PSCI, 0U);
+	run = (struct rmi_rec_run *)realm_ptr->run[0];
+
+	if (run->exit.gprs[0] != SMC_PSCI_CPU_ON_AARCH64) {
+		ERROR("Host did not receive CPU ON request\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+	rec_num = host_realm_find_rec_by_mpidr(run->exit.gprs[1], realm_ptr);
+	if (rec_num != 1U) {
+		ERROR("Invalid mpidr requested\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+	INFO("Requesting PSCI Complete Status Denied REC %d\n", rec_num);
+	ret = host_rmi_psci_complete(realm_ptr->rec[0], realm_ptr->rec[rec_num],
+			(unsigned long)PSCI_E_DENIED);
+	if (ret != RMI_SUCCESS) {
+		ERROR("host_rmi_psci_complete failed\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+
+	/* Enter rec1, should fail */
+	ret = host_realm_rec_enter(realm_ptr, &exit_reason, &host_call_result, 1U);
+	if (ret == RMI_SUCCESS) {
+		ERROR("Rec1 enter should have failed\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+	ret = host_realm_rec_enter(realm_ptr, &exit_reason, &host_call_result, 0U);
+
+	if (run->exit.gprs[0] != SMC_PSCI_AFFINITY_INFO_AARCH64) {
+		ERROR("Host did not receive PSCI_AFFINITY_INFO request\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+	rec_num = host_realm_find_rec_by_mpidr(run->exit.gprs[1], realm_ptr);
+	if (rec_num != 1U) {
+		ERROR("Invalid mpidr requested\n");
+		goto destroy_realm;
+	}
+
+	INFO("Requesting PSCI Complete Affinity Info REC %d\n", rec_num);
+	ret = host_rmi_psci_complete(realm_ptr->rec[0], realm_ptr->rec[rec_num],
+			(unsigned long)PSCI_E_SUCCESS);
+	if (ret != RMI_SUCCESS) {
+		ERROR("host_rmi_psci_complete failed\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+
+	/* Re-enter REC0 complete PSCI_AFFINITY_INFO */
+	ret = host_realm_rec_enter(realm_ptr, &exit_reason, &host_call_result, 0U);
+
+
+	if (run->exit.gprs[0] != SMC_PSCI_CPU_ON_AARCH64) {
+		ERROR("Host did not receive CPU ON request\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+	rec_num = host_realm_find_rec_by_mpidr(run->exit.gprs[1], realm_ptr);
+	if (rec_num != 2U) {
+		ERROR("Invalid mpidr requested\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+
+	INFO("Requesting PSCI Complete Status Denied REC %d\n", rec_num);
+	/* PSCI_DENIED should fail as rec2 is RMI_RUNNABLE */
+	ret = host_rmi_psci_complete(realm_ptr->rec[0], realm_ptr->rec[rec_num],
+			(unsigned long)PSCI_E_DENIED);
+	if (ret == RMI_SUCCESS) {
+		ret1 = false;
+		ERROR("host_rmi_psci_complete should have failed\n");
+		goto destroy_realm;
+	}
+
+	ret = host_realm_rec_enter(realm_ptr, &exit_reason, &host_call_result, 0U);
+	if (ret != RMI_SUCCESS) {
+		ERROR("Rec0 re-enter failed\n");
+		ret1 = false;
+		goto destroy_realm;
+	}
+
+destroy_realm:
+	ret2 = host_destroy_realm();
+
+	if (!ret1 || !ret2) {
+		ERROR("%s(): enter=%d destroy=%d\n",
+		__func__, ret1, ret2);
+		return TEST_RESULT_FAIL;
+	}
+
+	return host_cmp_result();
+}
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index 10cc04b..fb65837 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -12,6 +12,8 @@
 	  function="host_test_realm_create_enter" />
 	  <testcase name="Realm payload multi rec single cpu"
 	  function="host_realm_multi_rec_single_cpu" />
+	  <testcase name="Realm payload multi rec psci denied"
+	  function="host_realm_multi_rec_psci_denied" />
 	  <testcase name="Realm payload boot"
 	  function="host_realm_version_single_cpu" />
 	  <testcase name="Realm payload multi CPU request"