feat(indirect message): forward FFA_RX_RELEASE to SPMC
Within FF-A v1.1 indirect messaging, the SPMC owns a VM's RX buffers;
the Hypervisor must acquire ownership through FFA_RX_ACQUIRE ABI when
it needs to deliver a message to a VM, and notify the SPMC when the VM
releases it, by forwarding FFA_RX_RELEASE messages.
SPMC's view of a VM mailbox may not follow current mailbox
FSM (empty->received->read->empty), SPMC may not be aware of VM
message delivery and the consequent received->read mailbox transition.
Change-Id: I391054410573cc80549905f1a4bd2bbd19ac17fe
Signed-off-by: Federico Recanati <federico.recanati@arm.com>
diff --git a/src/api.c b/src/api.c
index f696a14..35ae4c2 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1962,31 +1962,94 @@
* will overwrite the old and will arrive asynchronously.
*
* Returns:
+ * - FFA_ERROR FFA_INVALID_PARAMETERS if message is forwarded to SPMC but
+ * there's no buffer pair mapped.
* - FFA_ERROR FFA_DENIED on failure, if the mailbox hasn't been read.
* - FFA_SUCCESS on success if no further action is needed.
* - FFA_RX_RELEASE if it was called by the primary VM and the primary VM now
* needs to wake up or kick waiters. Waiters should be retrieved by calling
* hf_mailbox_waiter_get.
*/
-struct ffa_value api_ffa_rx_release(struct vcpu *current, struct vcpu **next)
+struct ffa_value api_ffa_rx_release(ffa_vm_id_t receiver_id,
+ struct vcpu *current, struct vcpu **next)
{
- struct vm *vm = current->vm;
- struct vm_locked locked;
+ struct vm *current_vm = current->vm;
+ struct vm *vm;
+ struct vm_locked vm_locked;
+ ffa_vm_id_t current_vm_id = current_vm->id;
+ ffa_vm_id_t release_vm_id;
struct ffa_value ret;
- locked = vm_lock(vm);
+ /* `receiver_id` can be set only at Non-Secure Physical interface. */
+ if (vm_id_is_current_world(current_vm_id) && (receiver_id != 0)) {
+ dlog_error("Invalid `receiver_id`, must be zero.\n");
+ return ffa_error(FFA_INVALID_PARAMETERS);
+ }
+
+ /*
+ * VM ID to be released: `receiver_id` if message has been forwarded by
+ * Hypervisor to release a VM's buffer, current VM ID otherwise.
+ */
+ if (vm_id_is_current_world(current_vm_id) || (receiver_id == 0)) {
+ release_vm_id = current_vm_id;
+ } else {
+ release_vm_id = receiver_id;
+ }
+
+ vm_locked = plat_ffa_vm_find_locked(release_vm_id);
+ vm = vm_locked.vm;
+ if (vm == NULL) {
+ dlog_error("No buffer registered for VM ID %#x.\n",
+ release_vm_id);
+ return ffa_error(FFA_INVALID_PARAMETERS);
+ }
+
+ if (!plat_ffa_rx_release_forward(vm_locked, &ret)) {
+ dlog_verbose("RX_RELEASE forward failed for VM ID %#x.\n",
+ release_vm_id);
+ goto out;
+ }
+
+ /*
+ * When SPMC owns a VM's RX buffer, the Hypervisor's view can be out of
+ * sync: reset it to empty and exit.
+ */
+ if (plat_ffa_rx_release_forwarded(vm_locked)) {
+ ret = (struct ffa_value){.func = FFA_SUCCESS_32};
+ goto out;
+ }
+
switch (vm->mailbox.state) {
case MAILBOX_STATE_EMPTY:
- case MAILBOX_STATE_RECEIVED:
ret = ffa_error(FFA_DENIED);
break;
+ case MAILBOX_STATE_RECEIVED:
+ if (release_vm_id == current_vm_id) {
+ /*
+ * VM requesting to release its own RX buffer,
+ * must be in READ state.
+ */
+ ret = ffa_error(FFA_DENIED);
+ } else {
+ /*
+ * Forwarded message from Hypervisor to release a VM
+ * RX buffer, SPMC's mailbox view can be still in
+ * RECEIVED state.
+ */
+ ret = (struct ffa_value){.func = FFA_SUCCESS_32};
+ vm->mailbox.state = MAILBOX_STATE_EMPTY;
+ }
+ break;
+
case MAILBOX_STATE_READ:
- ret = api_waiter_result(locked, current, next);
+ ret = api_waiter_result(vm_locked, current, next);
vm->mailbox.state = MAILBOX_STATE_EMPTY;
break;
}
- vm_unlock(&locked);
+
+out:
+ vm_unlock(&vm_locked);
return ret;
}