refactor(memory share): reclaim from other world

The memory reclaim has been refactored to better differentiate
SPMC's and Hypervisor's implementation.
The functions that operate a memory reclaim targetting the other
world, are moved to the plat/ffa module:

- The SPMC returns ffa_error(FFA_INVALID_PARAMETER), as it considers
to have received an invalid memory handle.

- The Hypervisor delegates the tracking of the memory region to
the SPMC if there is a SP involved, either as the Sender or as
a receiver. As such, all functions doing memory reclaim with
the other world (i.e. the secure world) that were defined in
`ffa_memory.c` have been moved to `plat/ffa/hypervisor.c`.
Few functions from ffa_memory.c were made public to be
invoked in 'plat/ffa/hypervisor.c'.

Change-Id: Idff0dae89ca8b6c99d33632bb4401decc368b7e5
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/ffa_memory.c b/src/ffa_memory.c
index 7df1491..fd3030b 100644
--- a/src/ffa_memory.c
+++ b/src/ffa_memory.c
@@ -30,14 +30,6 @@
 static struct ffa_memory_share_state share_states[MAX_MEM_SHARES];
 
 /**
- * Buffer for retrieving memory region information from the other world for when
- * a region is reclaimed by a VM. Access to this buffer must be guarded by the
- * VM lock of the other world VM.
- */
-alignas(PAGE_SIZE) static uint8_t
-	other_world_retrieve_buffer[HF_MAILBOX_SIZE * MAX_FRAGMENTS];
-
-/**
  * Extracts the index from a memory handle allocated by Hafnium's current world.
  */
 uint64_t ffa_memory_handle_get_index(ffa_memory_handle_t handle)
@@ -569,7 +561,7 @@
  *   4) FFA_INVALID_PARAMETERS if the requested share type was not handled.
  *  Or FFA_SUCCESS on success.
  */
-static struct ffa_value ffa_retrieve_check_transition(
+struct ffa_value ffa_retrieve_check_transition(
 	struct vm_locked to, uint32_t share_func,
 	struct ffa_memory_region_constituent **fragments,
 	uint32_t *fragment_constituent_counts, uint32_t fragment_count,
@@ -1016,126 +1008,6 @@
 	return ret;
 }
 
-/**
- * Reclaims the given memory from the other world. To do this space is first
- * reserved in the <to> VM's page table, then the reclaim request is sent on to
- * the other world. then (if that is successful) the memory is mapped back into
- * the <to> VM's page table.
- *
- * This function requires the calling context to hold the <to> lock.
- *
- * Returns:
- *  In case of error, one of the following values is returned:
- *   1) FFA_INVALID_PARAMETERS - The endpoint provided parameters were
- *     erroneous;
- *   2) FFA_NO_MEMORY - Hafnium did not have sufficient memory to complete
- *     the request.
- *  Success is indicated by FFA_SUCCESS.
- */
-static struct ffa_value ffa_other_world_reclaim_check_update(
-	struct vm_locked to_locked, ffa_memory_handle_t handle,
-	struct ffa_memory_region_constituent *constituents,
-	uint32_t constituent_count, uint32_t memory_to_attributes, bool clear,
-	struct mpool *page_pool)
-{
-	uint32_t to_mode;
-	struct mpool local_page_pool;
-	struct ffa_value ret;
-	ffa_memory_region_flags_t other_world_flags;
-
-	/*
-	 * Make sure constituents are properly aligned to a 64-bit boundary. If
-	 * not we would get alignment faults trying to read (64-bit) values.
-	 */
-	if (!is_aligned(constituents, 8)) {
-		dlog_verbose("Constituents not aligned.\n");
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	/*
-	 * Check if the state transition is lawful for the recipient, and ensure
-	 * that all constituents of the memory region being retrieved are at the
-	 * same state.
-	 */
-	ret = ffa_retrieve_check_transition(to_locked, FFA_MEM_RECLAIM_32,
-					    &constituents, &constituent_count,
-					    1, memory_to_attributes, &to_mode);
-	if (ret.func != FFA_SUCCESS_32) {
-		dlog_verbose("Invalid transition.\n");
-		return ret;
-	}
-
-	/*
-	 * Create a local pool so any freed memory can't be used by another
-	 * thread. This is to ensure the original mapping can be restored if the
-	 * clear fails.
-	 */
-	mpool_init_with_fallback(&local_page_pool, page_pool);
-
-	/*
-	 * First reserve all required memory for the new page table entries in
-	 * the recipient page tables without committing, to make sure the entire
-	 * operation will succeed without exhausting the page pool.
-	 */
-	if (!ffa_region_group_identity_map(to_locked, &constituents,
-					   &constituent_count, 1, to_mode,
-					   page_pool, false)) {
-		/* TODO: partial defrag of failed range. */
-		dlog_verbose(
-			"Insufficient memory to update recipient page "
-			"table.\n");
-		ret = ffa_error(FFA_NO_MEMORY);
-		goto out;
-	}
-
-	/*
-	 * Forward the request to the other world, check if SPMC returned
-	 * FFA_SUCCESS_32. If not, terminate and return the error to caller
-	 * VM.
-	 */
-	other_world_flags = 0;
-	if (clear) {
-		other_world_flags |= FFA_MEMORY_REGION_FLAG_CLEAR;
-	}
-	ret = arch_other_world_call(
-		(struct ffa_value){.func = FFA_MEM_RECLAIM_32,
-				   .arg1 = (uint32_t)handle,
-				   .arg2 = (uint32_t)(handle >> 32),
-				   .arg3 = other_world_flags});
-
-	if (ret.func != FFA_SUCCESS_32) {
-		dlog_verbose(
-			"Got %#x (%d) from other world in response to "
-			"FFA_MEM_RECLAIM, "
-			"expected FFA_SUCCESS.\n",
-			ret.func, ret.arg2);
-		goto out;
-	}
-
-	/*
-	 * The other world was happy with it, so complete the reclaim by mapping
-	 * the memory into the recipient. This won't allocate because the
-	 * transaction was already prepared above, so it doesn't need to use the
-	 * `local_page_pool`.
-	 */
-	CHECK(ffa_region_group_identity_map(to_locked, &constituents,
-					    &constituent_count, 1, to_mode,
-					    page_pool, true));
-
-	ret = (struct ffa_value){.func = FFA_SUCCESS_32};
-
-out:
-	mpool_fini(&local_page_pool);
-
-	/*
-	 * Tidy up the page table by reclaiming failed mappings (if there was an
-	 * error) or merging entries into blocks where possible (on success).
-	 */
-	vm_ptable_defrag(to_locked, page_pool);
-
-	return ret;
-}
-
 static struct ffa_value ffa_relinquish_check_update(
 	struct vm_locked from_locked,
 	struct ffa_memory_region_constituent **fragments,
@@ -2820,155 +2692,3 @@
 	share_states_unlock(&share_states);
 	return ret;
 }
-
-/**
- * Validates that the reclaim transition is allowed for the memory
- * region with the given handle which was previously shared with the
- * other world. Tells the other world to mark it as reclaimed, and
- * updates the page table of the reclaiming VM.
- *
- * To do this information about the memory region is first fetched from
- * the other world.
- */
-struct ffa_value ffa_memory_other_world_reclaim(struct vm_locked to_locked,
-						struct vm_locked from_locked,
-						ffa_memory_handle_t handle,
-						ffa_memory_region_flags_t flags,
-						struct mpool *page_pool)
-{
-	uint32_t request_length = ffa_memory_lender_retrieve_request_init(
-		from_locked.vm->mailbox.recv, handle, to_locked.vm->id);
-	struct ffa_value other_world_ret;
-	uint32_t length;
-	uint32_t fragment_length;
-	uint32_t fragment_offset;
-	struct ffa_memory_region *memory_region;
-	struct ffa_composite_memory_region *composite;
-	uint32_t memory_to_attributes = MM_MODE_R | MM_MODE_W | MM_MODE_X;
-
-	CHECK(request_length <= HF_MAILBOX_SIZE);
-	CHECK(from_locked.vm->id == HF_OTHER_WORLD_ID);
-
-	/* Retrieve memory region information from the other world. */
-	other_world_ret = arch_other_world_call(
-		(struct ffa_value){.func = FFA_MEM_RETRIEVE_REQ_32,
-				   .arg1 = request_length,
-				   .arg2 = request_length});
-	if (other_world_ret.func == FFA_ERROR_32) {
-		dlog_verbose("Got error %d from EL3.\n", other_world_ret.arg2);
-		return other_world_ret;
-	}
-	if (other_world_ret.func != FFA_MEM_RETRIEVE_RESP_32) {
-		dlog_verbose(
-			"Got %#x from EL3, expected FFA_MEM_RETRIEVE_RESP.\n",
-			other_world_ret.func);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	length = other_world_ret.arg1;
-	fragment_length = other_world_ret.arg2;
-
-	if (fragment_length > HF_MAILBOX_SIZE || fragment_length > length ||
-	    length > sizeof(other_world_retrieve_buffer)) {
-		dlog_verbose("Invalid fragment length %d/%d (max %d/%d).\n",
-			     fragment_length, length, HF_MAILBOX_SIZE,
-			     sizeof(other_world_retrieve_buffer));
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	/*
-	 * Copy the first fragment of the memory region descriptor to an
-	 * internal buffer.
-	 */
-	memcpy_s(other_world_retrieve_buffer,
-		 sizeof(other_world_retrieve_buffer),
-		 from_locked.vm->mailbox.send, fragment_length);
-
-	/* Fetch the remaining fragments into the same buffer. */
-	fragment_offset = fragment_length;
-	while (fragment_offset < length) {
-		other_world_ret = arch_other_world_call(
-			(struct ffa_value){.func = FFA_MEM_FRAG_RX_32,
-					   .arg1 = (uint32_t)handle,
-					   .arg2 = (uint32_t)(handle >> 32),
-					   .arg3 = fragment_offset});
-		if (other_world_ret.func != FFA_MEM_FRAG_TX_32) {
-			dlog_verbose(
-				"Got %#x (%d) from other world in response to "
-				"FFA_MEM_FRAG_RX, expected FFA_MEM_FRAG_TX.\n",
-				other_world_ret.func, other_world_ret.arg2);
-			return other_world_ret;
-		}
-		if (ffa_frag_handle(other_world_ret) != handle) {
-			dlog_verbose(
-				"Got FFA_MEM_FRAG_TX for unexpected handle %#x "
-				"in response to FFA_MEM_FRAG_RX for handle "
-				"%#x.\n",
-				ffa_frag_handle(other_world_ret), handle);
-			return ffa_error(FFA_INVALID_PARAMETERS);
-		}
-		if (ffa_frag_sender(other_world_ret) != 0) {
-			dlog_verbose(
-				"Got FFA_MEM_FRAG_TX with unexpected sender %d "
-				"(expected 0).\n",
-				ffa_frag_sender(other_world_ret));
-			return ffa_error(FFA_INVALID_PARAMETERS);
-		}
-		fragment_length = other_world_ret.arg3;
-		if (fragment_length > HF_MAILBOX_SIZE ||
-		    fragment_offset + fragment_length > length) {
-			dlog_verbose(
-				"Invalid fragment length %d at offset %d (max "
-				"%d).\n",
-				fragment_length, fragment_offset,
-				HF_MAILBOX_SIZE);
-			return ffa_error(FFA_INVALID_PARAMETERS);
-		}
-		memcpy_s(other_world_retrieve_buffer + fragment_offset,
-			 sizeof(other_world_retrieve_buffer) - fragment_offset,
-			 from_locked.vm->mailbox.send, fragment_length);
-
-		fragment_offset += fragment_length;
-	}
-
-	memory_region = (struct ffa_memory_region *)other_world_retrieve_buffer;
-
-	if (memory_region->receiver_count != 1) {
-		/* Only one receiver supported by Hafnium for now. */
-		dlog_verbose(
-			"Multiple recipients not supported (got %d, "
-			"expected 1).\n",
-			memory_region->receiver_count);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	if (memory_region->handle != handle) {
-		dlog_verbose(
-			"Got memory region handle %#x from other world "
-			"but requested handle %#x.\n",
-			memory_region->handle, handle);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	/* The original sender must match the caller. */
-	if (to_locked.vm->id != memory_region->sender) {
-		dlog_verbose(
-			"VM %#x attempted to reclaim memory handle %#x "
-			"originally sent by VM %#x.\n",
-			to_locked.vm->id, handle, memory_region->sender);
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	composite = ffa_memory_region_get_composite(memory_region, 0);
-
-	/*
-	 * Validate that the reclaim transition is allowed for the given
-	 * memory region, forward the request to the other world and
-	 * then map the memory back into the caller's stage-2 page
-	 * table.
-	 */
-	return ffa_other_world_reclaim_check_update(
-		to_locked, handle, composite->constituents,
-		composite->constituent_count, memory_to_attributes,
-		flags & FFA_MEM_RECLAIM_CLEAR, page_pool);
-}