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/ffa_memory.c b/src/ffa_memory.c
index 048cca9..8c8ad8c 100644
--- a/src/ffa_memory.c
+++ b/src/ffa_memory.c
@@ -21,9 +21,6 @@
 #include "hf/std.h"
 #include "hf/vm.h"
 
-/** The maximum number of recipients a memory region may be sent to. */
-#define MAX_MEM_SHARE_RECIPIENTS 1
-
 /**
  * The maximum number of memory sharing handles which may be active at once. A
  * DONATE handle is active from when it is sent to when it is retrieved; a SHARE
@@ -522,14 +519,14 @@
  */
 static struct ffa_value ffa_send_check_transition(
 	struct vm_locked from, uint32_t share_func,
-	ffa_memory_access_permissions_t permissions, uint32_t *orig_from_mode,
+	struct ffa_memory_access *receivers, uint32_t receivers_count,
+	uint32_t *orig_from_mode,
 	struct ffa_memory_region_constituent **fragments,
 	uint32_t *fragment_constituent_counts, uint32_t fragment_count,
 	uint32_t *from_mode)
 {
 	const uint32_t state_mask =
 		MM_MODE_INVALID | MM_MODE_UNOWNED | MM_MODE_SHARED;
-	uint32_t required_from_mode;
 	struct ffa_value ret;
 
 	ret = constituents_get_mode(from, orig_from_mode, fragments,
@@ -555,15 +552,23 @@
 		return ffa_error(FFA_DENIED);
 	}
 
-	required_from_mode =
-		ffa_memory_permissions_to_mode(permissions, *orig_from_mode);
+	assert(receivers != NULL && receivers_count > 0U);
 
-	if ((*orig_from_mode & required_from_mode) != required_from_mode) {
-		dlog_verbose(
-			"Sender tried to send memory with permissions which "
-			"required mode %#x but only had %#x itself.\n",
-			required_from_mode, *orig_from_mode);
-		return ffa_error(FFA_DENIED);
+	for (uint32_t i = 0U; i < receivers_count; i++) {
+		ffa_memory_access_permissions_t permissions =
+			receivers[i].receiver_permissions.permissions;
+		uint32_t required_from_mode = ffa_memory_permissions_to_mode(
+			permissions, *orig_from_mode);
+
+		if ((*orig_from_mode & required_from_mode) !=
+		    required_from_mode) {
+			dlog_verbose(
+				"Sender tried to send memory with permissions "
+				"which "
+				"required mode %#x but only had %#x itself.\n",
+				required_from_mode, *orig_from_mode);
+			return ffa_error(FFA_DENIED);
+		}
 	}
 
 	/* Find the appropriate new mode. */
@@ -895,8 +900,9 @@
 	struct vm_locked from_locked,
 	struct ffa_memory_region_constituent **fragments,
 	uint32_t *fragment_constituent_counts, uint32_t fragment_count,
-	uint32_t share_func, ffa_memory_access_permissions_t permissions,
-	struct mpool *page_pool, bool clear, uint32_t *orig_from_mode_ret)
+	uint32_t share_func, struct ffa_memory_access *receivers,
+	uint32_t receivers_count, struct mpool *page_pool, bool clear,
+	uint32_t *orig_from_mode_ret)
 {
 	uint32_t i;
 	uint32_t orig_from_mode;
@@ -920,9 +926,9 @@
 	 * all constituents of a memory region being shared are at the same
 	 * state.
 	 */
-	ret = ffa_send_check_transition(from_locked, share_func, permissions,
-					&orig_from_mode, fragments,
-					fragment_constituent_counts,
+	ret = ffa_send_check_transition(from_locked, share_func, receivers,
+					receivers_count, &orig_from_mode,
+					fragments, fragment_constituent_counts,
 					fragment_count, &from_mode);
 	if (ret.func != FFA_SUCCESS_32) {
 		dlog_verbose("Invalid transition for send.\n");
@@ -1323,7 +1329,7 @@
 		from_locked, share_state->fragments,
 		share_state->fragment_constituent_counts,
 		share_state->fragment_count, share_state->share_func,
-		memory_region->receivers[0].receiver_permissions.permissions,
+		memory_region->receivers, memory_region->receiver_count,
 		page_pool, memory_region->flags & FFA_MEMORY_REGION_FLAG_CLEAR,
 		orig_from_mode_ret);
 	if (ret.func != FFA_SUCCESS_32) {
@@ -1388,7 +1394,7 @@
 static struct ffa_value ffa_memory_send_validate(
 	struct vm_locked from_locked, struct ffa_memory_region *memory_region,
 	uint32_t memory_share_length, uint32_t fragment_length,
-	uint32_t share_func, ffa_memory_access_permissions_t *permissions)
+	uint32_t share_func)
 {
 	struct ffa_composite_memory_region *composite;
 	uint32_t receivers_length;
@@ -1399,14 +1405,6 @@
 	enum ffa_instruction_access instruction_access;
 	struct ffa_value ret;
 
-	assert(permissions != NULL);
-
-	/*
-	 * This should already be checked by the caller, just making the
-	 * assumption clear here.
-	 */
-	assert(memory_region->receiver_count == 1);
-
 	/* The sender must match the message sender. */
 	if (memory_region->sender != from_locked.vm->id) {
 		dlog_verbose("Invalid sender %d.\n", memory_region->sender);
@@ -1473,59 +1471,99 @@
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
-	/* Check that the permissions are valid. */
-	*permissions =
-		memory_region->receivers[0].receiver_permissions.permissions;
-	data_access = ffa_get_data_access_attr(*permissions);
-	instruction_access = ffa_get_instruction_access_attr(*permissions);
-	if (data_access == FFA_DATA_ACCESS_RESERVED ||
-	    instruction_access == FFA_INSTRUCTION_ACCESS_RESERVED) {
-		dlog_verbose("Reserved value for receiver permissions %#x.\n",
-			     *permissions);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-	if (instruction_access != FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED) {
-		dlog_verbose(
-			"Invalid instruction access permissions %#x for "
-			"sending memory.\n",
-			*permissions);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-	if (share_func == FFA_MEM_SHARE_32) {
-		if (data_access == FFA_DATA_ACCESS_NOT_SPECIFIED) {
-			dlog_verbose(
-				"Invalid data access permissions %#x for "
-				"sharing memory.\n",
-				*permissions);
+	/* Check that the permissions are valid, for each specified receiver. */
+	for (uint32_t i = 0U; i < memory_region->receiver_count; i++) {
+		ffa_memory_access_permissions_t permissions =
+			memory_region->receivers[i]
+				.receiver_permissions.permissions;
+		ffa_vm_id_t receiver_id =
+			memory_region->receivers[i]
+				.receiver_permissions.receiver;
+
+		if (memory_region->sender == receiver_id) {
+			dlog_verbose("Can't share memory with itself.\n");
 			return ffa_error(FFA_INVALID_PARAMETERS);
 		}
-		/*
-		 * According to section 5.11.3 of the FF-A 1.0 spec NX is
-		 * required for share operations (but must not be specified by
-		 * the sender) so set it in the copy that we store, ready to be
-		 * returned to the retriever.
-		 */
-		ffa_set_instruction_access_attr(permissions,
-						FFA_INSTRUCTION_ACCESS_NX);
-		memory_region->receivers[0].receiver_permissions.permissions =
-			*permissions;
-	}
-	if (share_func == FFA_MEM_LEND_32 &&
-	    data_access == FFA_DATA_ACCESS_NOT_SPECIFIED) {
-		dlog_verbose(
-			"Invalid data access permissions %#x for lending "
-			"memory.\n",
-			*permissions);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
 
-	if (share_func == FFA_MEM_DONATE_32 &&
-	    data_access != FFA_DATA_ACCESS_NOT_SPECIFIED) {
-		dlog_verbose(
-			"Invalid data access permissions %#x for donating "
-			"memory.\n",
-			*permissions);
-		return ffa_error(FFA_INVALID_PARAMETERS);
+		for (uint32_t j = i + 1; j < memory_region->receiver_count;
+		     j++) {
+			if (receiver_id ==
+			    memory_region->receivers[j]
+				    .receiver_permissions.receiver) {
+				dlog_verbose(
+					"Repeated receiver(%x) in memory send "
+					"operation.\n",
+					memory_region->receivers[j]
+						.receiver_permissions.receiver);
+				return ffa_error(FFA_INVALID_PARAMETERS);
+			}
+		}
+
+		if (composite_memory_region_offset !=
+		    memory_region->receivers[i]
+			    .composite_memory_region_offset) {
+			dlog_verbose(
+				"All ffa_memory_access should point to the "
+				"same composite memory region offset.\n");
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+
+		data_access = ffa_get_data_access_attr(permissions);
+		instruction_access =
+			ffa_get_instruction_access_attr(permissions);
+		if (data_access == FFA_DATA_ACCESS_RESERVED ||
+		    instruction_access == FFA_INSTRUCTION_ACCESS_RESERVED) {
+			dlog_verbose(
+				"Reserved value for receiver permissions "
+				"%#x.\n",
+				permissions);
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+		if (instruction_access !=
+		    FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED) {
+			dlog_verbose(
+				"Invalid instruction access permissions %#x "
+				"for sending memory.\n",
+				permissions);
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+		if (share_func == FFA_MEM_SHARE_32) {
+			if (data_access == FFA_DATA_ACCESS_NOT_SPECIFIED) {
+				dlog_verbose(
+					"Invalid data access permissions %#x "
+					"for sharing memory.\n",
+					permissions);
+				return ffa_error(FFA_INVALID_PARAMETERS);
+			}
+			/*
+			 * According to section 10.10.3 of the FF-A v1.1 EAC0
+			 * spec, NX is required for share operations (but must
+			 * not be specified by the sender) so set it in the
+			 * copy that we store, ready to be returned to the
+			 * retriever.
+			 */
+			ffa_set_instruction_access_attr(
+				&permissions, FFA_INSTRUCTION_ACCESS_NX);
+			memory_region->receivers[i]
+				.receiver_permissions.permissions = permissions;
+		}
+		if (share_func == FFA_MEM_LEND_32 &&
+		    data_access == FFA_DATA_ACCESS_NOT_SPECIFIED) {
+			dlog_verbose(
+				"Invalid data access permissions %#x for "
+				"lending memory.\n",
+				permissions);
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+
+		if (share_func == FFA_MEM_DONATE_32 &&
+		    data_access != FFA_DATA_ACCESS_NOT_SPECIFIED) {
+			dlog_verbose(
+				"Invalid data access permissions %#x for "
+				"donating memory.\n",
+				permissions);
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
 	}
 
 	/*
@@ -1694,7 +1732,6 @@
 				 uint32_t fragment_length, uint32_t share_func,
 				 struct mpool *page_pool)
 {
-	ffa_memory_access_permissions_t permissions;
 	struct ffa_value ret;
 	struct share_states_locked share_states;
 	struct ffa_memory_share_state *share_state;
@@ -1706,7 +1743,7 @@
 	 */
 	ret = ffa_memory_send_validate(from_locked, memory_region,
 				       memory_share_length, fragment_length,
-				       share_func, &permissions);
+				       share_func);
 	if (ret.func != FFA_SUCCESS_32) {
 		mpool_free(page_pool, memory_region);
 		return ret;
@@ -1781,7 +1818,6 @@
 	struct ffa_memory_region *memory_region, uint32_t memory_share_length,
 	uint32_t fragment_length, uint32_t share_func, struct mpool *page_pool)
 {
-	ffa_memory_access_permissions_t permissions;
 	struct ffa_value ret;
 
 	/*
@@ -1791,7 +1827,7 @@
 	 */
 	ret = ffa_memory_send_validate(from_locked, memory_region,
 				       memory_share_length, fragment_length,
-				       share_func, &permissions);
+				       share_func);
 	if (ret.func != FFA_SUCCESS_32) {
 		goto out;
 	}
@@ -1813,7 +1849,8 @@
 		ret = ffa_send_check_update(
 			from_locked, &constituents,
 			&composite->constituent_count, 1, share_func,
-			permissions, &local_page_pool,
+			memory_region->receivers, memory_region->receiver_count,
+			&local_page_pool,
 			memory_region->flags & FFA_MEMORY_REGION_FLAG_CLEAR,
 			&orig_from_mode);
 		if (ret.func != FFA_SUCCESS_32) {