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.
diff --git a/spm/cactus/cactus_test_cmds.h b/spm/cactus/cactus_test_cmds.h
index 52aa55b..3e16e57 100644
--- a/spm/cactus/cactus_test_cmds.h
+++ b/spm/cactus/cactus_test_cmds.h
@@ -64,6 +64,36 @@
 #define CACTUS_REQ_ECHO_GET_ECHO_DEST(smc_ret) smc_ret.ret5
 
 /**
+ * Command to create a cyclic dependency between SPs, which could result in
+ * a deadlock. This aims at proving such scenario cannot happen.
+ * If the deadlock happens, the system will just hang.
+ * If the deadlock is prevented, the last partition to use the command will
+ * send response CACTUS_SUCCESS.
+ *
+ * The id is the hex representation of the string 'dead'.
+ */
+#define CACTUS_DEADLOCK_CMD U(0x64656164)
+
+#define CACTUS_DEADLOCK_SEND_CMD(source, dest, next_dest)		\
+		CACTUS_SEND_CMD(source, dest, CACTUS_DEADLOCK_CMD, next_dest, \
+				0, 0, 0)
+
+#define CACTUS_DEADLOCK_GET_NEXT_DEST(smc_ret) smc_ret.ret4
+
+/**
+ * Command to request a sequence CACTUS_DEADLOCK_CMD between the partitions
+ * of specified IDs.
+ */
+#define CACTUS_REQ_DEADLOCK_CMD (CACTUS_DEADLOCK_CMD + 1)
+
+#define CACTUS_REQ_DEADLOCK_SEND_CMD(source, dest, next_dest1, next_dest2)  \
+		CACTUS_SEND_CMD(source, dest, CACTUS_REQ_DEADLOCK_CMD,	 \
+				next_dest1, next_dest2, 0, 0)
+
+/*To get next_dest1 use CACTUS_DEADLOCK_GET_NEXT_DEST*/
+#define CACTUS_DEADLOCK_GET_NEXT_DEST2(smc_ret) smc_ret.ret5
+
+/**
  * Command to notify cactus of a memory management operation. The cmd value
  * should be the memory management smc function id.
  */