feat(memory sharing): v1.0 fragmented memory retrieve

To permit a fragmented memory retrieve from an FF-A v1.0 borrower,
change the validation to expected fragment offset.
For FF-A v1.0 borrower, determine the expected fragment offset by
calculating assuming how the retrieve response would have been provided
to the borrower.
For FF-A v1.1 borrowers, verification remains the same as before.

Change-Id: I769c798cafc6025fe7223fe176b7ecf3d3c6358f
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/ffa_memory.c b/src/ffa_memory.c
index a17000d..f435533 100644
--- a/src/ffa_memory.c
+++ b/src/ffa_memory.c
@@ -24,6 +24,8 @@
 
 #include "vmapi/hf/ffa_v1_0.h"
 
+#define RECEIVERS_COUNT_IN_RETRIEVE_RESP 1
+
 /**
  * All access to members of a `struct ffa_memory_share_state` must be guarded
  * by this lock.
@@ -1700,9 +1702,9 @@
 		struct ffa_memory_region_v1_0 *retrieve_response =
 			(struct ffa_memory_region_v1_0 *)response;
 
-		ffa_memory_region_init_header_v1_0(retrieve_response, sender,
-						   attributes, flags, handle, 0,
-						   1);
+		ffa_memory_region_init_header_v1_0(
+			retrieve_response, sender, attributes, flags, handle, 0,
+			RECEIVERS_COUNT_IN_RETRIEVE_RESP);
 
 		receiver = &retrieve_response->receivers[0];
 		receiver_count = retrieve_response->receiver_count;
@@ -2379,6 +2381,49 @@
 	return ret;
 }
 
+/**
+ * Determine expected fragment offset according to the FF-A version of
+ * the caller.
+ */
+static uint32_t ffa_memory_retrieve_expected_offset_per_ffa_version(
+	struct ffa_memory_region *memory_region,
+	uint32_t retrieved_constituents_count, uint32_t ffa_version)
+{
+	uint32_t expected_fragment_offset;
+	uint32_t composite_constituents_offset;
+
+	if (ffa_version == MAKE_FFA_VERSION(1, 1)) {
+		/*
+		 * Hafnium operates memory regions in FF-A v1.1 format, so we
+		 * can retrieve the constituents offset from descriptor.
+		 */
+		composite_constituents_offset =
+			ffa_composite_constituent_offset(memory_region, 0);
+	} else if (ffa_version == MAKE_FFA_VERSION(1, 0)) {
+		/*
+		 * If retriever is FF-A v1.0, determine the composite offset
+		 * as it is expected to have been configured in the
+		 * retrieve response.
+		 */
+		composite_constituents_offset =
+			sizeof(struct ffa_memory_region_v1_0) +
+			RECEIVERS_COUNT_IN_RETRIEVE_RESP *
+				sizeof(struct ffa_memory_access) +
+			sizeof(struct ffa_composite_memory_region);
+	} else {
+		panic("%s received an invalid FF-A version.\n", __func__);
+	}
+
+	expected_fragment_offset =
+		composite_constituents_offset +
+		retrieved_constituents_count *
+			sizeof(struct ffa_memory_region_constituent) -
+		sizeof(struct ffa_memory_access) *
+			(memory_region->receiver_count - 1);
+
+	return expected_fragment_offset;
+}
+
 struct ffa_value ffa_memory_retrieve_continue(struct vm_locked to_locked,
 					      ffa_memory_handle_t handle,
 					      uint32_t fragment_offset,
@@ -2500,12 +2545,10 @@
 	CHECK(memory_region->receiver_count > 0);
 
 	expected_fragment_offset =
-		ffa_composite_constituent_offset(memory_region,
-						 receiver_index) +
-		retrieved_constituents_count *
-			sizeof(struct ffa_memory_region_constituent) -
-		sizeof(struct ffa_memory_access) *
-			(memory_region->receiver_count - 1);
+		ffa_memory_retrieve_expected_offset_per_ffa_version(
+			memory_region, retrieved_constituents_count,
+			to_locked.vm->ffa_version);
+
 	if (fragment_offset != expected_fragment_offset) {
 		dlog_verbose("Fragment offset was %d but expected %d.\n",
 			     fragment_offset, expected_fragment_offset);