cactus: testing deadlock by FF-A direct message

Added command CACTUS_DEADLOCK_CMD to file cactus_test_cmds.h to create
a deadlock scenario using FF-A direct message interfaces.
Added command CACTUS_REQ_DEADLOCK_CMD to trigger the sequence
of CACTUS_DEADLOCK_CMD necessary for the deadlock to occur.
Handled both commands in cactus message loop.
The purpose is to verify a deadlock by FF-A direct messaging cannot
happen in Hafnium implementation.

Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: Ia5d6e92a955cd73d8997edaeef055f7b8184850e
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 7900e7f..da7e913 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -44,6 +44,8 @@
 	uint32_t sp_response;
 	ffa_vm_id_t source;
 	ffa_vm_id_t destination;
+	uint64_t cactus_cmd;
+
 
 	/*
 	* This initial wait call is necessary to inform SPMD that
@@ -80,7 +82,9 @@
 
 		PRINT_CMD(ffa_ret);
 
-		switch (CACTUS_GET_CMD(ffa_ret)) {
+		cactus_cmd = CACTUS_GET_CMD(ffa_ret);
+
+		switch (cactus_cmd) {
 		case FFA_MEM_SHARE_SMC32:
 		case FFA_MEM_LEND_SMC32:
 		case FFA_MEM_DONATE_SMC32:
@@ -133,6 +137,67 @@
 					    CACTUS_ERROR_RESP(vm_id, source);
 			break;
 		}
+		case CACTUS_DEADLOCK_CMD:
+		case CACTUS_REQ_DEADLOCK_CMD:
+		{
+			ffa_vm_id_t deadlock_dest =
+				CACTUS_DEADLOCK_GET_NEXT_DEST(ffa_ret);
+			ffa_vm_id_t deadlock_next_dest = source;
+
+			if (cactus_cmd == CACTUS_DEADLOCK_CMD) {
+				VERBOSE("%x is creating deadlock. next: %x\n",
+					source, deadlock_dest);
+			} else if (cactus_cmd == CACTUS_REQ_DEADLOCK_CMD) {
+				VERBOSE(
+				"%x requested deadlock with %x and %x\n",
+				source, deadlock_dest, deadlock_next_dest);
+
+				deadlock_next_dest =
+					CACTUS_DEADLOCK_GET_NEXT_DEST2(ffa_ret);
+			}
+
+			ffa_ret = CACTUS_DEADLOCK_SEND_CMD(vm_id, deadlock_dest,
+							   deadlock_next_dest);
+
+			/*
+			 * Should be true for the last partition to attempt
+			 * an FF-A direct message, to the first partition.
+			 */
+			bool is_deadlock_detected =
+				(ffa_ret.ret0 == FFA_ERROR) &&
+				(ffa_ret.ret2 == FFA_ERROR_BUSY);
+
+			/*
+			 * Should be true after the deadlock has been detected
+			 * and after the first response has been sent down the
+			 * request chain.
+			 */
+			bool is_returning_from_deadlock =
+				(ffa_ret.ret0 == FFA_MSG_SEND_DIRECT_RESP_SMC32)
+				&& (CACTUS_IS_SUCCESS_RESP(ffa_ret));
+
+			if (is_deadlock_detected) {
+				NOTICE("Attempting dealock but got error %lx\n",
+					ffa_ret.ret2);
+			}
+
+			if (is_deadlock_detected ||
+			    is_returning_from_deadlock) {
+				/*
+				 * This is not the partition, that would have
+				 * created the deadlock. As such, reply back
+				 * to the partitions.
+				 */
+				ffa_ret = CACTUS_SUCCESS_RESP(vm_id, source);
+				break;
+			}
+
+			/* Shouldn't get to this point */
+			ERROR("Deadlock test went wrong!\n");
+			ffa_ret = CACTUS_ERROR_RESP(vm_id, source);
+
+			break;
+		}
 		default:
 			/*
 			 * Currently direct message test is handled here.