feat(indirect message): add RX_ACQUIRE ABI

In FF-A v1.1 both Hypervisor and SPMC are producers of VM's RX buffer,
and they could contend for the same buffer.
SPMC owns VM's RX buffer after it's mapped in its translation regime.
This ABI should be used by the Hypervisor to get the ownership of a
VM's RX buffer.
This patch covers two uses cases: indirect messages (through v1.1
FFA_MSG_SEND2) and FFA_PARTITION_INFO_GET. Memory sharing interfaces
should make use of RX_ACQUIRE when they will be updated to FF-A v1.1.

Change-Id: Id33cbc7e500adf55316783a48e7acfcb52b9e305
Signed-off-by: Federico Recanati <federico.recanati@arm.com>
diff --git a/src/api.c b/src/api.c
index 8daf10d..94af06d 100644
--- a/src/api.c
+++ b/src/api.c
@@ -367,6 +367,7 @@
 	uint32_t version = vm->ffa_version;
 	uint32_t partition_info_size;
 	uint32_t buffer_size;
+	struct ffa_value ret;
 
 	if (msg_receiver_busy(vm_locked, NULL, false)) {
 		/*
@@ -377,6 +378,12 @@
 		return ffa_error(FFA_BUSY);
 	}
 
+	/* Acquire receiver's RX buffer. */
+	if (!plat_ffa_acquire_receiver_rx(vm_locked, &ret)) {
+		dlog_verbose("Failed to acquire RX buffer for VM %x\n", vm->id);
+		return ret;
+	}
+
 	if (version == MAKE_FFA_VERSION(1, 0)) {
 		struct ffa_partition_info_v1_0 *recv_mailbox = vm->mailbox.recv;
 
@@ -1724,6 +1731,12 @@
 		goto out;
 	}
 
+	/* Acquire receiver's RX buffer. */
+	if (!plat_ffa_acquire_receiver_rx(to_locked, &ret)) {
+		dlog_error("Failed to acquire RX buffer for VM %#x\n", to->id);
+		goto out;
+	}
+
 	/* Check the size of transfer. */
 	msg_size = FFA_RXTX_HEADER_SIZE + header.size;
 	if ((msg_size > FFA_PARTITION_MSG_PAYLOAD_MAX) ||
@@ -1986,6 +1999,60 @@
 }
 
 /**
+ * Acquire ownership of an RX buffer before writing to it. Both
+ * Hypervisor and SPMC are producers of VM's RX buffer, and they
+ * could contend for the same buffer. SPMC owns VM's RX buffer after
+ * it's mapped in its translation regime. This ABI should be
+ * used by the Hypervisor to get the ownership of a VM's RX buffer
+ * from the SPMC solving the aforementioned possible contention.
+ *
+ * Returns:
+ * - FFA_DENIED: callee cannot relinquish ownership of RX buffer.
+ * - FFA_INVALID_PARAMETERS: there is no buffer pair registered for the VM.
+ * - FFA_NOT_SUPPORTED: function not implemented at the FF-A instance.
+ */
+struct ffa_value api_ffa_rx_acquire(ffa_vm_id_t receiver_id,
+				    struct vcpu *current)
+{
+	struct vm_locked receiver_locked;
+	struct vm *receiver;
+	struct ffa_value ret;
+
+	if ((current->vm->id != HF_HYPERVISOR_VM_ID) ||
+	    !plat_ffa_is_vm_id(receiver_id)) {
+		dlog_error(
+			"FFA_RX_ACQUIRE not supported at this FF-A "
+			"instance.\n");
+		return ffa_error(FFA_NOT_SUPPORTED);
+	}
+
+	receiver_locked = plat_ffa_vm_find_locked(receiver_id);
+	receiver = receiver_locked.vm;
+
+	if (receiver == NULL || receiver->mailbox.recv == NULL) {
+		dlog_error("Cannot retrieve RX buffer for VM ID %#x.\n",
+			   receiver_id);
+		ret = ffa_error(FFA_INVALID_PARAMETERS);
+		goto out;
+	}
+
+	if (receiver->mailbox.state != MAILBOX_STATE_EMPTY) {
+		dlog_error("Mailbox busy for VM ID %#x.\n", receiver_id);
+		ret = ffa_error(FFA_DENIED);
+		goto out;
+	}
+
+	receiver->mailbox.state = MAILBOX_STATE_RECEIVED;
+
+	ret = (struct ffa_value){.func = FFA_SUCCESS_32};
+
+out:
+	vm_unlock(&receiver_locked);
+
+	return ret;
+}
+
+/**
  * Enables or disables a given interrupt ID for the calling vCPU.
  *
  * Returns 0 on success, or -1 if the intid is invalid.
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 13ab1eb..b30fb8d 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -507,6 +507,9 @@
 	case FFA_RXTX_UNMAP_32:
 		*args = api_ffa_rxtx_unmap(args->arg1, current);
 		return true;
+	case FFA_RX_ACQUIRE_32:
+		*args = api_ffa_rx_acquire(ffa_receiver(*args), current);
+		return true;
 	case FFA_YIELD_32:
 		*args = api_yield(current, next);
 		return true;
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index d3d9264..1dcf5fb 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -129,6 +129,15 @@
 	return false;
 }
 
+bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
+				  struct ffa_value *ret)
+{
+	(void)to_locked;
+	(void)ret;
+
+	return false;
+}
+
 bool plat_ffa_is_indirect_msg_supported(struct vm_locked sender_locked,
 					struct vm_locked receiver_locked)
 {
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 1ae5089..1dcc522 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -243,6 +243,30 @@
 	return false;
 }
 
+/**
+ * Acquire the RX buffer of a VM from the SPM.
+ *
+ * VM RX/TX buffers must have been previously mapped in the SPM either
+ * by forwarding VM's RX_TX_MAP API or another way if buffers were
+ * declared in manifest.
+ */
+bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
+				  struct ffa_value *ret)
+{
+	if (!ffa_tee_enabled) {
+		return true;
+	}
+
+	if (to_locked.vm->ffa_version < MAKE_FFA_VERSION(1, 1)) {
+		return true;
+	}
+
+	*ret = arch_other_world_call((struct ffa_value){
+		.func = FFA_RX_ACQUIRE_32, .arg1 = to_locked.vm->id});
+
+	return ret->func == FFA_SUCCESS_32;
+}
+
 bool plat_ffa_is_indirect_msg_supported(struct vm_locked sender_locked,
 					struct vm_locked receiver_locked)
 {
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index baa6e77..9729445 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -349,6 +349,15 @@
 	return false;
 }
 
+bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
+				  struct ffa_value *ret)
+{
+	(void)to_locked;
+	(void)ret;
+
+	return true;
+}
+
 /**
  * Check that sender and receiver support indirect messages, in accordance
  * to their configurations in the respective partition's FF-A manifest.
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 9eb3686..2537dc0 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -99,6 +99,15 @@
 	return false;
 }
 
+bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
+				  struct ffa_value *ret)
+{
+	(void)to_locked;
+	(void)ret;
+
+	return false;
+}
+
 bool plat_ffa_is_indirect_msg_supported(struct vm_locked sender_locked,
 					struct vm_locked receiver_locked)
 {