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/inc/hf/api.h b/inc/hf/api.h
index 371fb3e..50b95fa 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -48,6 +48,8 @@
 struct ffa_value api_ffa_msg_recv(bool block, struct vcpu *current,
 				  struct vcpu **next);
 struct ffa_value api_ffa_rx_release(struct vcpu *current, struct vcpu **next);
+struct ffa_value api_ffa_rx_acquire(ffa_vm_id_t receiver_id,
+				    struct vcpu *current);
 struct ffa_value api_vm_configure_pages(
 	struct mm_stage1_locked mm_stage1_locked, struct vm_locked vm_locked,
 	ipaddr_t send, ipaddr_t recv, uint32_t page_count,
diff --git a/inc/hf/arch/plat/ffa.h b/inc/hf/arch/plat/ffa.h
index 162cfff..d4a163e 100644
--- a/inc/hf/arch/plat/ffa.h
+++ b/inc/hf/arch/plat/ffa.h
@@ -74,6 +74,9 @@
 				     struct ffa_value args,
 				     struct ffa_value *ret);
 
+bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
+				  struct ffa_value *ret);
+
 bool plat_ffa_is_indirect_msg_supported(struct vm_locked sender_locked,
 					struct vm_locked receiver_locked);
 
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)
 {
diff --git a/test/vmapi/ffa_secure_partitions/setup_and_discovery.c b/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
index 0d9d76d..d003f82 100644
--- a/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
+++ b/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
@@ -180,6 +180,12 @@
 	struct ffa_uuid uuid;
 	uint32_t version;
 
+	/* Set version 1.1 to forward RX/TX buffers to SPMC. */
+	ffa_version(MAKE_FFA_VERSION(1, 1));
+
+	/* Setup the mailbox (which holds the RX buffer). */
+	mb = set_up_mailbox();
+
 	/*
 	 * First call FF-A version to tell the SPMC our version
 	 * is v1.0.
@@ -187,8 +193,6 @@
 	version = ffa_version(MAKE_FFA_VERSION(1, 0));
 	EXPECT_EQ(version, FFA_VERSION_COMPILED);
 
-	/* Setup the mailbox (which holds the RX buffer). */
-	mb = set_up_mailbox();
 	partitions_v1_0 = mb.recv;
 
 	/*