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;
}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index b30fb8d..0ee37d6 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -497,7 +497,7 @@
*args = api_ffa_features(args->arg1);
return true;
case FFA_RX_RELEASE_32:
- *args = api_ffa_rx_release(current, next);
+ *args = api_ffa_rx_release(ffa_receiver(*args), current, next);
return true;
case FFA_RXTX_MAP_64:
*args = api_ffa_rxtx_map(ipa_init(args->arg1),
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 1dcf5fb..ed19602 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -129,6 +129,22 @@
return false;
}
+bool plat_ffa_rx_release_forward(struct vm_locked vm_locked,
+ struct ffa_value *ret)
+{
+ (void)vm_locked;
+ (void)ret;
+
+ return false;
+}
+
+bool plat_ffa_rx_release_forwarded(struct vm_locked vm_locked)
+{
+ (void)vm_locked;
+
+ return false;
+}
+
bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
struct ffa_value *ret)
{
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 1dcc522..7f0666b 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -243,6 +243,48 @@
return false;
}
+bool plat_ffa_rx_release_forward(struct vm_locked vm_locked,
+ struct ffa_value *ret)
+{
+ struct vm *vm = vm_locked.vm;
+ ffa_vm_id_t vm_id = vm->id;
+
+ if (!ffa_tee_enabled || (vm->ffa_version < MAKE_FFA_VERSION(1, 1))) {
+ *ret = (struct ffa_value){.func = FFA_SUCCESS_32};
+ return true;
+ }
+
+ CHECK(vm_id_is_current_world(vm_id));
+
+ /* Hypervisor always forward VM's RX_RELEASE to SPMC. */
+ *ret = arch_other_world_call(
+ (struct ffa_value){.func = FFA_RX_RELEASE_32, .arg1 = vm_id});
+
+ return ret->func == FFA_SUCCESS_32;
+}
+
+/**
+ * In FF-A v1.1 with SPMC enabled the SPMC owns the RX buffers for NWd VMs,
+ * hence the SPMC is handling FFA_RX_RELEASE calls for NWd VMs too.
+ * The Hypervisor's view of a VM's RX buffer can be out of sync, reset it to
+ * 'empty' if the FFA_RX_RELEASE call has been successfully forwarded to the
+ * SPMC.
+ */
+bool plat_ffa_rx_release_forwarded(struct vm_locked vm_locked)
+{
+ struct vm *vm = vm_locked.vm;
+
+ if (ffa_tee_enabled && (vm->ffa_version > MAKE_FFA_VERSION(1, 0))) {
+ dlog_verbose(
+ "RX_RELEASE forwarded, reset MB state for VM ID %#x.\n",
+ vm->id);
+ vm->mailbox.state = MAILBOX_STATE_EMPTY;
+ return true;
+ }
+
+ return false;
+}
+
/**
* Acquire the RX buffer of a VM from the SPM.
*
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 9729445..4efeb9a 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -141,7 +141,12 @@
goto out;
}
+ /*
+ * Note: VM struct for Nwd VMs is only partially initialized, to the
+ * extend of what's currently used by the SPMC (VM ID, waiter list).
+ */
vm_locked.vm->id = vm_id;
+ list_init(&vm_locked.vm->mailbox.waiter_list);
out:
nwd_vms_unlock(&nwd_vms_locked);
@@ -349,6 +354,22 @@
return false;
}
+bool plat_ffa_rx_release_forward(struct vm_locked vm_locked,
+ struct ffa_value *ret)
+{
+ (void)vm_locked;
+ (void)ret;
+
+ return true;
+}
+
+bool plat_ffa_rx_release_forwarded(struct vm_locked vm_locked)
+{
+ (void)vm_locked;
+
+ return false;
+}
+
bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
struct ffa_value *ret)
{
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 2537dc0..252d174 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -99,6 +99,22 @@
return false;
}
+bool plat_ffa_rx_release_forward(struct vm_locked vm_locked,
+ struct ffa_value *ret)
+{
+ (void)vm_locked;
+ (void)ret;
+
+ return false;
+}
+
+bool plat_ffa_rx_release_forwarded(struct vm_locked vm_locked)
+{
+ (void)vm_locked;
+
+ return false;
+}
+
bool plat_ffa_acquire_receiver_rx(struct vm_locked to_locked,
struct ffa_value *ret)
{