plat(ff-a): forward FFA_PARTITION_INFO_GET to SPMC

When a VM calls FFA_PARTITION_INFO_GET the Hypervisor looks up for the
passed UUID in the current list of VMs. If the UUID is unknown or NULL
then it forwards it to the SPMC to look up for the same UUID amongst
secure partitions.

Change-Id: I6ef9bf01da90ef18983e7bd8cb2ffbc90aa07ddc
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/src/api.c b/src/api.c
index 0d628bd..e01f2db 100644
--- a/src/api.c
+++ b/src/api.c
@@ -364,7 +364,7 @@
 	bool uuid_is_null = ffa_uuid_is_null(uuid);
 	struct ffa_value ret;
 	uint32_t size;
-	struct ffa_partition_info partitions[MAX_VMS];
+	struct ffa_partition_info partitions[2 * MAX_VMS];
 
 	/*
 	 * Iterate through the VMs to find the ones with a matching UUID.
@@ -388,7 +388,25 @@
 		}
 	}
 
-	/* Unrecognized UUID: does not match any of the VMs and is not Null. */
+	/* If UUID is Null vm_count must not be zero at this stage. */
+	CHECK(!uuid_is_null || vm_count != 0);
+
+	/*
+	 * When running the Hypervisor:
+	 * - If UUID is Null the Hypervisor forwards the query to the SPMC for
+	 * it to fill with secure partitions information.
+	 * - If UUID is non-Null vm_count may be zero because the UUID  matches
+	 * a secure partition and the query is forwarded to the SPMC.
+	 * When running the SPMC:
+	 * - If UUID is non-Null and vm_count is zero it means there is no such
+	 * partition identified in the system.
+	 */
+	plat_ffa_partition_info_get_forward(uuid, partitions, &vm_count);
+
+	/*
+	 * Unrecognized UUID: does not match any of the VMs (or SPs)
+	 * and is not Null.
+	 */
 	if (vm_count == 0) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
@@ -421,7 +439,9 @@
 	memcpy_s(current_vm->mailbox.recv, FFA_MSG_PAYLOAD_MAX, partitions,
 		 size);
 	current_vm->mailbox.recv_size = size;
-	current_vm->mailbox.recv_sender = HF_HYPERVISOR_VM_ID;
+
+	/* Sender is Hypervisor in the normal world (TEE in secure world). */
+	current_vm->mailbox.recv_sender = HF_VM_ID_BASE;
 	current_vm->mailbox.recv_func = FFA_PARTITION_INFO_GET_32;
 	current_vm->mailbox.state = MAILBOX_STATE_READ;
 
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index df0ccb2..7750cac 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -296,3 +296,12 @@
 	(void)next;
 	(void)current;
 }
+
+bool plat_ffa_partition_info_get_forward(const struct ffa_uuid *uuid,
+					 const ffa_vm_count_t *ret_count)
+{
+	(void)uuid;
+	(void)ret_count;
+
+	return false;
+}
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 2cdc3cc..b5a2307 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -555,3 +555,61 @@
 	(void)next;
 	(void)current;
 }
+
+/*
+ * Forward helper for FFA_PARTITION_INFO_GET.
+ * Emits FFA_PARTITION_INFO_GET from Hypervisor to SPMC if allowed.
+ */
+void plat_ffa_partition_info_get_forward(const struct ffa_uuid *uuid,
+					 struct ffa_partition_info *partitions,
+					 ffa_vm_count_t *ret_count)
+{
+	const struct vm *tee = vm_find(HF_TEE_VM_ID);
+	struct ffa_partition_info *tee_partitions;
+	ffa_vm_count_t tee_partitions_count;
+	ffa_vm_count_t vm_count = *ret_count;
+	struct ffa_value ret;
+
+	CHECK(tee != NULL);
+	CHECK(vm_count < MAX_VMS);
+
+	/*
+	 * Allow forwarding from the Hypervisor if TEE or SPMC exists and
+	 * declared as such in the Hypervisor manifest.
+	 */
+	if (!ffa_tee_enabled) {
+		return;
+	}
+
+	ret = arch_other_world_call(
+		(struct ffa_value){.func = FFA_PARTITION_INFO_GET_32,
+				   .arg1 = uuid->uuid[0],
+				   .arg2 = uuid->uuid[1],
+				   .arg3 = uuid->uuid[2],
+				   .arg4 = uuid->uuid[3]});
+	if (ffa_func_id(ret) != FFA_SUCCESS_32) {
+		dlog_verbose(
+			"Failed forwarding FFA_PARTITION_INFO_GET to "
+			"the SPMC.\n");
+		return;
+	}
+
+	tee_partitions_count = ffa_partition_info_get_count(ret);
+	if (tee_partitions_count == 0 || tee_partitions_count > MAX_VMS) {
+		dlog_verbose("Invalid number of SPs returned by the SPMC.\n");
+		return;
+	}
+
+	tee_partitions = (struct ffa_partition_info *)tee->mailbox.send;
+	for (ffa_vm_count_t index = 0; index < tee_partitions_count; index++) {
+		partitions[vm_count] = tee_partitions[index];
+		++vm_count;
+	}
+
+	/* Release the RX buffer. */
+	ret = arch_other_world_call(
+		(struct ffa_value){.func = FFA_RX_RELEASE_32});
+	CHECK(ret.func == FFA_SUCCESS_32);
+
+	*ret_count = vm_count;
+}
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 128574c..f53d9c7 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -1216,3 +1216,15 @@
 		vm_unlock(&target_vm_locked);
 	}
 }
+
+/** Forward helper for FFA_PARTITION_INFO_GET. */
+void plat_ffa_partition_info_get_forward(  // NOLINTNEXTLINE
+	const struct ffa_uuid *uuid,	   // NOLINTNEXTLINE
+	struct ffa_partition_info *partitions, ffa_vm_count_t *ret_count)
+{
+	/* The SPMC does not forward FFA_PARTITION_INFO_GET. */
+
+	(void)uuid;
+	(void)partitions;
+	(void)ret_count;
+}
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index d9bfc8b..cadebee 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -283,3 +283,12 @@
 	(void)next;
 	(void)current;
 }
+
+void plat_ffa_partition_info_get_forward(  // NOLINTNEXTLINE
+	const struct ffa_uuid *uuid,	   // NOLINTNEXTLINE
+	struct ffa_partition_info *partitions, ffa_vm_count_t *ret_count)
+{
+	(void)uuid;
+	(void)partitions;
+	(void)ret_count;
+}