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/inc/hf/arch/plat/ffa.h b/inc/hf/arch/plat/ffa.h
index a4d0dc5..b26524f 100644
--- a/inc/hf/arch/plat/ffa.h
+++ b/inc/hf/arch/plat/ffa.h
@@ -220,3 +220,7 @@
 
 void plat_ffa_inject_notification_pending_interrupt_context_switch(
 	struct vcpu *next, struct vcpu *current);
+
+void plat_ffa_partition_info_get_forward(const struct ffa_uuid *uuid,
+					 struct ffa_partition_info *partitions,
+					 ffa_vm_count_t *ret_count);
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index 801d669..8cbf0dc 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -280,6 +280,11 @@
 	uint64_t arg7;
 };
 
+static inline uint32_t ffa_func_id(struct ffa_value args)
+{
+	return args.func;
+}
+
 static inline int32_t ffa_error_code(struct ffa_value val)
 {
 	return (int32_t)val.arg2;
@@ -305,6 +310,11 @@
 	return args.arg4;
 }
 
+static inline uint32_t ffa_partition_info_get_count(struct ffa_value args)
+{
+	return args.arg2;
+}
+
 static inline ffa_memory_handle_t ffa_assemble_handle(uint32_t a1, uint32_t a2)
 {
 	return (uint64_t)a1 | (uint64_t)a2 << 32;
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;
+}
diff --git a/test/vmapi/ffa_secure_partitions/partition_control.c b/test/vmapi/ffa_secure_partitions/partition_control.c
index 897fe04..27b16f6 100644
--- a/test/vmapi/ffa_secure_partitions/partition_control.c
+++ b/test/vmapi/ffa_secure_partitions/partition_control.c
@@ -15,6 +15,11 @@
 #include "test/hftest.h"
 #include "test/vmapi/ffa.h"
 
+TEAR_DOWN(ffa)
+{
+	EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED);
+}
+
 /**
  * Communicates with partition via direct messaging to validate functioning of
  * Direct Message interfaces.
@@ -113,3 +118,79 @@
 	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
 	EXPECT_EQ(sp_resp(res), SP_SUCCESS);
 }
+
+TEST(ffa, ffa_partition_info_get_uuid_null)
+{
+	struct mailbox_buffers mb;
+	struct ffa_value ret;
+	const struct ffa_partition_info *partitions;
+	struct ffa_uuid uuid;
+
+	/* Setup the mailbox (which holds the RX buffer). */
+	mb = set_up_mailbox();
+	partitions = mb.recv;
+
+	/*
+	 * A Null UUID requests information for all partitions
+	 * including VMs and SPs.
+	 */
+	ffa_uuid_init(0, 0, 0, 0, &uuid);
+
+	/* Check that expected partition information is returned. */
+	ret = ffa_partition_info_get(&uuid);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/* Expect two partitions. */
+	EXPECT_EQ(ret.arg2, 2);
+
+	/* Expect the PVM as first partition. */
+	EXPECT_EQ(partitions[0].vm_id, hf_vm_get_id());
+	EXPECT_EQ(partitions[0].vcpu_count, 8);
+
+	/* Expect a SP as second partition. */
+	EXPECT_EQ(partitions[1].vm_id, HF_SPMC_VM_ID + 1);
+	EXPECT_EQ(partitions[1].vcpu_count, 8);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+}
+
+TEST(ffa, ffa_partition_info_get_uuid_fixed)
+{
+	struct mailbox_buffers mb;
+	struct ffa_value ret;
+	const struct ffa_partition_info *partitions;
+	struct ffa_uuid uuid;
+
+	/* Setup the mailbox (which holds the RX buffer). */
+	mb = set_up_mailbox();
+	partitions = mb.recv;
+
+	/* Search for a known secure partition UUID. */
+	ffa_uuid_init(0xa609f132, 0x6b4f, 0x4c14, 0x9489, &uuid);
+
+	/* Check that the expected partition information is returned. */
+	ret = ffa_partition_info_get(&uuid);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/* Expect one partition. */
+	EXPECT_EQ(ret.arg2, 1);
+
+	/* Expect a secure partition. */
+	EXPECT_EQ(partitions[0].vm_id, HF_SPMC_VM_ID + 1);
+	EXPECT_EQ(partitions[0].vcpu_count, 8);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+}
+
+TEST(ffa, ffa_partition_info_get_uuid_unknown)
+{
+	struct ffa_value ret;
+	struct ffa_uuid uuid;
+
+	/* Search for a unknown partition UUID. */
+	ffa_uuid_init(1, 1, 1, 1, &uuid);
+
+	/* Expect no partition is found with such UUID. */
+	ret = ffa_partition_info_get(&uuid);
+	EXPECT_EQ(ret.func, FFA_ERROR_32);
+}
diff --git a/test/vmapi/ffa_secure_partitions/partition_manifest_services_sp.dts b/test/vmapi/ffa_secure_partitions/partition_manifest_services_sp.dts
index fa755ff..1e34f7a 100644
--- a/test/vmapi/ffa_secure_partitions/partition_manifest_services_sp.dts
+++ b/test/vmapi/ffa_secure_partitions/partition_manifest_services_sp.dts
@@ -14,7 +14,7 @@
 
 	/* Properties */
 	ffa-version = <0x00010001>; /* 31:16 - Major, 15:0 - Minor */
-	uuid = <0xa609f132 0x6b4f 0x4c14 0x9489 0x798457>;
+	uuid = <0xa609f132 0x6b4f 0x4c14 0x9489>;
 	execution-ctx-count = <8>;
 	exception-level = <2>; /* S-EL1 */
 	execution-state = <0>; /* AARCH64 */