feat(interrupts): intercept S-EL0 SP direct response message

Since FF-A v1.1 EAC0 spec only allows signaling a virtual secure
interrupt to an S-EL0 SP when in WAITING state, SPMC intercepts
a direct response message from the S-EL0 partition to proactively
signal the pending virtual secure interrupt and resume the vCPU
of the S-EL0 partition.

Further, once the secure interrupt is handled, SPMC resumes the
direct response message from current S-EL0 partition and takes
care of unwinding the call chain.

Change-Id: I55da462b5b6b7813f09a7b57c16fcd378972ac3a
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 2454a40..94392c7 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2630,6 +2630,43 @@
 }
 
 /**
+ * Resume the target vCPU after the current vCPU sent a direct response.
+ * Current vCPU moves to waiting state.
+ */
+void api_ffa_resume_direct_resp_target(struct vcpu *current, struct vcpu **next,
+				       ffa_vm_id_t receiver_vm_id,
+				       struct ffa_value to_ret,
+				       bool is_nwd_call_chain)
+{
+	if (!vm_id_is_current_world(receiver_vm_id)) {
+		*next = api_switch_to_other_world(current, to_ret,
+						  VCPU_STATE_WAITING);
+
+		/* End of NWd scheduled call chain. */
+		assert(!is_nwd_call_chain ||
+		       (current->call_chain.prev_node == NULL));
+	} else if (receiver_vm_id == HF_PRIMARY_VM_ID) {
+		*next = api_switch_to_primary(current, to_ret,
+					      VCPU_STATE_WAITING);
+
+		/* Removing a node from NWd scheduled call chain. */
+		if (is_nwd_call_chain) {
+			vcpu_call_chain_remove_node(current, *next);
+		}
+	} else if (vm_id_is_current_world(receiver_vm_id)) {
+		/*
+		 * It is expected the receiver_vm_id to be from an SP, otherwise
+		 * 'plat_ffa_is_direct_response_valid' should have
+		 * made function return error before getting to this point.
+		 */
+		*next = api_switch_to_vm(current, to_ret, VCPU_STATE_WAITING,
+					 receiver_vm_id);
+	} else {
+		panic("Invalid direct message response invocation");
+	}
+}
+
+/**
  * Send an FF-A direct message response.
  */
 struct ffa_value api_ffa_msg_send_direct_resp(ffa_vm_id_t sender_vm_id,
@@ -2640,6 +2677,8 @@
 {
 	struct vcpu_locked current_locked;
 	enum vcpu_state next_state = VCPU_STATE_WAITING;
+	struct ffa_value signal_interrupt =
+		(struct ffa_value){.func = FFA_INTERRUPT_32};
 
 	if (!api_ffa_dir_msg_is_arg2_zero(args)) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
@@ -2703,42 +2742,19 @@
 		}
 	}
 
+	if (plat_ffa_intercept_direct_response(current_locked, next, to_ret,
+					       &signal_interrupt)) {
+		vcpu_unlock(&current_locked);
+		return signal_interrupt;
+	}
+
 	/* Clear direct request origin for the caller. */
 	current->direct_request_origin_vm_id = HF_INVALID_VM_ID;
 
 	vcpu_unlock(&current_locked);
 
-	if (!vm_id_is_current_world(receiver_vm_id)) {
-		*next = api_switch_to_other_world(
-			current, to_ret,
-			/*
-			 * Current vcpu sent a direct response. It moves to
-			 * waiting state.
-			 */
-			VCPU_STATE_WAITING);
-	} else if (receiver_vm_id == HF_PRIMARY_VM_ID) {
-		*next = api_switch_to_primary(
-			current, to_ret,
-			/*
-			 * Current vcpu sent a direct response. It moves to
-			 * waiting state.
-			 */
-			VCPU_STATE_WAITING);
-	} else if (vm_id_is_current_world(receiver_vm_id)) {
-		/*
-		 * It is expected the receiver_vm_id to be from an SP, otherwise
-		 * 'plat_ffa_is_direct_response_valid' should have
-		 * made function return error before getting to this point.
-		 */
-		*next = api_switch_to_vm(current, to_ret,
-					 /*
-					  * current vcpu sent a direct response.
-					  * It moves to waiting state.
-					  */
-					 VCPU_STATE_WAITING, receiver_vm_id);
-	} else {
-		panic("Invalid direct message response invocation");
-	}
+	api_ffa_resume_direct_resp_target(current, next, receiver_vm_id, to_ret,
+					  false);
 
 	plat_ffa_unwind_call_chain_ffa_direct_resp(current, *next);