feat(memory share): specify multiple receivers
First patch to support multiple receivers to a FFA_MEM_SHARE or
FFA_MEM_LEND operation.
Extends validation of the memory transaction descriptor to all the
specified borrowers.
The FFA_MEM_DONATE remains restricted to one borrower, as the sender
relinquishes ownership of the memory region.
At this point, expected to work for endpoints from the same world.
Change-Id: I72e2695d3a4715ee82bd775151e07a7fc3d6ccc3
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/api.c b/src/api.c
index 788db42..deb2249 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2465,6 +2465,7 @@
const void *from_msg;
struct ffa_memory_region *memory_region;
struct ffa_value ret;
+ bool targets_other_world = false;
if (ipa_addr(address) != 0 || page_count != 0) {
/*
@@ -2535,11 +2536,27 @@
goto out;
}
- if (memory_region->receiver_count != 1) {
- /* Hafnium doesn't support multi-way memory sharing for now. */
+ if (memory_region->receiver_count == 0U) {
+ dlog_verbose("Receiver count can't be 0.\n");
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ goto out;
+ }
+
+ if (share_func == FFA_MEM_DONATE_32 &&
+ memory_region->receiver_count != 1U) {
dlog_verbose(
- "Multi-way memory sharing not supported (got %d "
- "endpoint memory access descriptors, expected 1).\n",
+ "FFA_MEM_DONATE only supports one recipient. "
+ "Specified %u\n",
+ memory_region->receiver_count);
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ goto out;
+ }
+
+ if (memory_region->receiver_count > MAX_MEM_SHARE_RECIPIENTS) {
+ dlog_verbose(
+ "Max number of recipients supported is %u "
+ "specified %u\n",
+ MAX_MEM_SHARE_RECIPIENTS,
memory_region->receiver_count);
ret = ffa_error(FFA_INVALID_PARAMETERS);
goto out;
@@ -2547,20 +2564,38 @@
/*
* Ensure that the receiver VM exists and isn't the same as the sender.
+ * If there is a receiver from the other world, track it for later
+ * forwarding if needed.
*/
- to = vm_find(memory_region->receivers[0].receiver_permissions.receiver);
- if (to == NULL || to == from) {
- dlog_verbose("Invalid receiver.\n");
- ret = ffa_error(FFA_INVALID_PARAMETERS);
- goto out;
+ for (uint32_t i = 0U; i < memory_region->receiver_count; i++) {
+ ffa_vm_id_t receiver_id =
+ memory_region->receivers[i]
+ .receiver_permissions.receiver;
+ to = vm_find(receiver_id);
+
+ if (vm_id_is_current_world(receiver_id) &&
+ (to == NULL || to == from)) {
+ dlog_verbose("%s: invalid receiver.\n", __func__);
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ goto out;
+ }
+
+ if (!plat_ffa_is_memory_send_valid(receiver_id, share_func)) {
+ ret = ffa_error(FFA_DENIED);
+ goto out;
+ }
+
+ /* Capture if any of the receivers is from the other world. */
+ if (!targets_other_world) {
+ targets_other_world =
+ !vm_id_is_current_world(receiver_id);
+ }
}
- if (!plat_ffa_is_memory_send_valid(to->id, share_func)) {
- ret = ffa_error(FFA_DENIED);
- goto out;
- }
-
- if (to->id == HF_TEE_VM_ID) {
+ /* Allow for one memory region to be shared to the TEE. */
+ if (targets_other_world) {
+ assert(memory_region->receiver_count == 1 &&
+ to->id == HF_TEE_VM_ID);
/*
* The 'to' VM lock is only needed in the case that it is the
* TEE VM.