fix(interrupts): deny new direct request message from endpoint

Please refer to detailed description in the comment preceding the
helper function introduced in cactus_interrupt.c file.

Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: Ie70d68514dac44211767d611db6d84991a8b48c6
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
index 35fdf30..16785db 100644
--- a/spm/cactus/cactus_interrupt.c
+++ b/spm/cactus/cactus_interrupt.c
@@ -59,6 +59,46 @@
 	spin_unlock(&sp_handler_lock[intid]);
 }
 
+/*
+ * Cactus SP does not implement application threads. Hence, once the Cactus SP
+ * sends the managed exit response to the direct request originator, execution
+ * is still frozen in interrupt handler context.
+ * Though it moves to WAITING state, it is not able to accept new direct request
+ * message from any endpoint. It can only receive a direct request message with
+ * the command CACTUS_RESUME_AFTER_MANAGED_EXIT from the originator of the
+ * suspended direct request message in order to return from the interrupt
+ * handler context and resume the processing of suspended request.
+ */
+void send_managed_exit_response(void)
+{
+	struct ffa_value ffa_ret;
+	bool waiting_resume_after_managed_exit;
+
+	/* Send managed exit response. */
+	ffa_ret = cactus_response(g_ffa_id, g_dir_req_source_id,
+			MANAGED_EXIT_INTERRUPT_ID);
+	waiting_resume_after_managed_exit = true;
+
+	while (waiting_resume_after_managed_exit) {
+
+		waiting_resume_after_managed_exit =
+			(ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC32 &&
+			 ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64) ||
+			 ffa_dir_msg_source(ffa_ret) != g_dir_req_source_id ||
+			 cactus_get_cmd(ffa_ret) != CACTUS_RESUME_AFTER_MANAGED_EXIT;
+
+		if (waiting_resume_after_managed_exit) {
+			ERROR("Expected a direct message request from endpoint"
+			      " %x with command CACTUS_RESUME_AFTER_MANAGED_EXIT\n",
+			       g_dir_req_source_id);
+			ffa_ret = cactus_error_resp(g_ffa_id,
+						    ffa_dir_msg_source(ffa_ret),
+						    CACTUS_ERROR_TEST);
+		}
+	}
+	VERBOSE("Resuming the suspended command\n");
+}
+
 void cactus_interrupt_handler_irq(void)
 {
 	uint32_t intid = spm_interrupt_get();
@@ -72,8 +112,7 @@
 		 */
 		VERBOSE("vIRQ: Sending ME response to %x\n",
 			g_dir_req_source_id);
-		cactus_response(g_ffa_id, g_dir_req_source_id,
-				managed_exit_interrupt_id);
+		send_managed_exit_response();
 	} else {
 		switch (intid) {
 		case IRQ_TWDOG_INTID:
@@ -114,8 +153,7 @@
 		 */
 		VERBOSE("vFIQ: Sending ME response to %x\n",
 			g_dir_req_source_id);
-		cactus_response(g_ffa_id, g_dir_req_source_id,
-				MANAGED_EXIT_INTERRUPT_ID);
+		send_managed_exit_response();
 		break;
 	default:
 		/*
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
index c438050..7f3de0c 100644
--- a/spm/cactus/cactus_tests/cactus_test_interrupts.c
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -82,8 +82,8 @@
 			 */
 			VERBOSE("SP%x: received Managed Exit as response\n",
 				vm_id);
-			ffa_ret = ffa_msg_send_direct_req64(vm_id, fwd_dest,
-							    0, 0, 0, 0, 0);
+			ffa_ret = cactus_resume_after_managed_exit(vm_id,
+								   fwd_dest);
 		}
 	}
 
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
index fa850c7..01fa2ee 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
@@ -75,10 +75,12 @@
  * 6. Check whether the pending non-secure timer interrupt successfully got
  *    handled in TFTF.
  *
- * 7. Send a direct message request command to resume Cactus's execution.
- *    It resumes in the sleep loop and completes it. It then returns with
- *    a direct message response. Check if time lapsed is greater than
- *    sleeping time.
+ * 7. Send a new sleep command to Cactus SP. An error response must be sent
+ *    back by the Cactus SP with CACTUS_ERROR_TEST as the error code.
+ *
+ * 8. Send a command asking the SP to resume after managed exit. SP resumes in
+ *    the suspended sleep loop and completes it. It then returns with a direct
+ *    message response. Check if time lapsed is greater than sleeping time.
  *
  */
 test_result_t test_ffa_ns_interrupt_managed_exit(void)
@@ -116,11 +118,19 @@
 	}
 
 	/*
-	 * Send a dummy direct message request to relinquish CPU cycles.
-	 * This resumes Cactus in the sleep routine.
+	 * Send a command asking the SP to resume after managed exit. This
+	 * effectively resumes the Cactus in the sleep routine. Note that
+	 * Cactus should return error if the current endpoint sent a new
+	 * command.
 	 */
-	ret_values = ffa_msg_send_direct_req64(SENDER, RECEIVER,
-					       0, 0, 0, 0, 0);
+	ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SLEEP_TIME);
+
+	if (cactus_get_response(ret_values) != CACTUS_ERROR &&
+	    cactus_error_code(ret_values) != CACTUS_ERROR_TEST) {
+		return TEST_RESULT_FAIL;
+	}
+
+	ret_values = cactus_resume_after_managed_exit(SENDER, RECEIVER);
 
 	if (!is_ffa_direct_response(ret_values)) {
 		return TEST_RESULT_FAIL;
@@ -320,11 +330,19 @@
 	}
 
 	/*
-	 * Send a dummy direct message request to relinquish CPU cycles.
-	 * This resumes Cactus in the sleep routine.
+	 * Send a command asking the SP to resume after managed exit. This
+	 * effectively resumes the Cactus in the sleep routine. Note that
+	 * Cactus should return error if the current endpoint sent a new
+	 * command.
 	 */
-	ret_values = ffa_msg_send_direct_req64(SENDER, RECEIVER,
-					       0, 0, 0, 0, 0);
+	ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SLEEP_TIME);
+
+	if (cactus_get_response(ret_values) != CACTUS_ERROR &&
+	    cactus_error_code(ret_values) != CACTUS_ERROR_TEST) {
+		return TEST_RESULT_FAIL;
+	}
+
+	ret_values = cactus_resume_after_managed_exit(SENDER, RECEIVER);
 
 	if (!is_ffa_direct_response(ret_values)) {
 		return TEST_RESULT_FAIL;
@@ -422,11 +440,19 @@
 	}
 
 	/*
-	 * Send a dummy direct message request to relinquish CPU cycles.
-	 * This resumes Cactus in the sleep routine.
+	 * Send a command asking the SP to resume after managed exit. This
+	 * effectively resumes the Cactus in the sleep routine. Note that
+	 * Cactus should return error if the current endpoint sent a new
+	 * command.
 	 */
-	ret_values = ffa_msg_send_direct_req64(SENDER, RECEIVER,
-					       0, 0, 0, 0, 0);
+	ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SLEEP_TIME);
+
+	if (cactus_get_response(ret_values) != CACTUS_ERROR &&
+	    cactus_error_code(ret_values) != CACTUS_ERROR_TEST) {
+		return TEST_RESULT_FAIL;
+	}
+
+	ret_values = cactus_resume_after_managed_exit(SENDER, RECEIVER);
 
 	if (!is_ffa_direct_response(ret_values)) {
 		return TEST_RESULT_FAIL;
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c b/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
index f00c803..ea264b4 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
@@ -1346,7 +1346,7 @@
 	/*
 	 * Resume setter Cactus in the handling of CACTUS_NOTIFICATIONS_SET_CMD.
 	 */
-	ret = ffa_msg_send_direct_req64(HYP_ID, sender, 0, 0, 0, 0, 0);
+	ret = cactus_resume_after_managed_exit(HYP_ID, sender);
 
 	/* Expected result to CACTUS_NOTIFICATIONS_SET_CMD. */
 	if (!is_expected_cactus_response(ret, CACTUS_SUCCESS, 0)) {