refactor: forward ABI to secure world

Forward FFA_PARTITION_INFO_GET_REGS from normal world to secure world to
get information for all partitions. With this patch, normal world
partitions can get information for SPs as well.

Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
Change-Id: Id4f51d72f4c97d40ca15101ad9df616f75999cce
diff --git a/inc/hf/arch/plat/ffa.h b/inc/hf/arch/plat/ffa.h
index 5fa8c2e..5a9ff82 100644
--- a/inc/hf/arch/plat/ffa.h
+++ b/inc/hf/arch/plat/ffa.h
@@ -273,6 +273,11 @@
 	struct vcpu_locked next_locked, struct vcpu *current,
 	struct vm_locked receiver_locked);
 
+bool plat_ffa_partition_info_get_regs_forward(
+	const struct ffa_uuid *uuid, const uint16_t start_index,
+	const uint16_t tag, struct ffa_partition_info *partitions,
+	uint16_t partitions_len, ffa_vm_count_t *ret_count);
+
 void plat_ffa_partition_info_get_forward(const struct ffa_uuid *uuid,
 					 const uint32_t flags,
 					 struct ffa_partition_info *partitions,
diff --git a/src/api.c b/src/api.c
index de5b03c..623111f 100644
--- a/src/api.c
+++ b/src/api.c
@@ -471,7 +471,7 @@
 						 const uint16_t tag)
 {
 	struct vm *current_vm = current->vm;
-	static struct ffa_partition_info partitions[MAX_VMS];
+	static struct ffa_partition_info partitions[2 * MAX_VMS];
 	bool uuid_is_null = ffa_uuid_is_null(uuid);
 	ffa_vm_count_t vm_count = 0;
 	struct ffa_value ret = ffa_error(FFA_INVALID_PARAMETERS);
@@ -513,13 +513,32 @@
 	/* If UUID is Null vm_count must not be zero at this stage. */
 	CHECK(!uuid_is_null || vm_count != 0);
 
-	/* TODO: Forward to secure world */
+	/*
+	 * When running the Hypervisor:
+	 * - If UUID is Null the Hypervisor forwards the query to the SPMC for
+	 * it to fill with secure partitions information.
+	 * TODO: Note that for this ABI, forwarding on every invocation when
+	 * uuid is Null is inefficient,and if performance becomes a problem,
+	 * this would be a good place to optimize using strategies such as
+	 * caching info etc. For now, assuming this inefficiency is not a major
+	 * issue.
+	 * - 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.
+	 */
+	if (!plat_ffa_partition_info_get_regs_forward(
+		    uuid, start_index, tag, partitions, ARRAY_SIZE(partitions),
+		    &vm_count)) {
+		return ffa_error(FFA_DENIED);
+	}
 
 	/*
 	 * Unrecognized UUID: does not match any of the VMs (or SPs)
 	 * and is not Null.
 	 */
-	if (vm_count == 0 || vm_count > vm_get_count()) {
+	if (vm_count == 0 || vm_count > ARRAY_SIZE(partitions)) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 4f440d8..5699c22 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -432,6 +432,23 @@
 	return false;
 }
 
+bool plat_ffa_partition_info_get_regs_forward(	// NOLINTNEXTLINE
+	const struct ffa_uuid *uuid,
+	const uint16_t start_index,  // NOLINTNEXTLINE
+	const uint16_t tag,
+	struct ffa_partition_info *partitions,		     // NOLINTNEXTLINE
+	uint16_t partitions_len, ffa_vm_count_t *ret_count)  // NOLINTNEXTLINE
+{
+	(void)uuid;
+	(void)start_index;
+	(void)tag;
+	(void)partitions;
+	(void)partitions_len;
+	(void)ret_count;
+
+	return true;
+}
+
 void plat_ffa_parse_partition_manifest(struct mm_stage1_locked stage1_locked,
 				       paddr_t fdt_addr,
 				       size_t fdt_allocated_size,
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 11d5c79..7a5924d 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -852,6 +852,139 @@
 	return false;
 }
 
+static struct ffa_value plat_ffa_partition_info_get_regs(
+	const struct ffa_uuid *uuid, const uint16_t start_index,
+	const uint16_t tag)
+{
+	uint64_t arg1 = (uint64_t)uuid->uuid[1] << 32 | uuid->uuid[0];
+	uint64_t arg2 = (uint64_t)uuid->uuid[3] << 32 | uuid->uuid[2];
+	uint64_t arg3 = start_index | (uint64_t)tag << 16;
+
+	return arch_other_world_call((struct ffa_value){
+		.func = FFA_PARTITION_INFO_GET_REGS_64,
+		.arg1 = arg1,
+		.arg2 = arg2,
+		.arg3 = arg3,
+	});
+}
+
+static bool plat_ffa_fill_partition_info_from_regs(
+	struct ffa_value ret, uint16_t start_index,
+	struct ffa_partition_info *partitions, uint16_t partitions_len,
+	ffa_vm_count_t *ret_count)
+{
+	uint16_t vm_count = *ret_count;
+	uint16_t curr_index = 0;
+	uint8_t num_entries = 0;
+	uint8_t idx = 0;
+	/* list of pointers to args in return value */
+	uint64_t *arg_ptrs[15] = {
+		&ret.arg3,
+		&ret.arg4,
+		&ret.arg5,
+		&ret.arg6,
+		&ret.arg7,
+		&ret.extended_val.arg8,
+		&ret.extended_val.arg9,
+		&ret.extended_val.arg10,
+		&ret.extended_val.arg11,
+		&ret.extended_val.arg12,
+		&ret.extended_val.arg13,
+		&ret.extended_val.arg14,
+		&ret.extended_val.arg15,
+		&ret.extended_val.arg16,
+		&ret.extended_val.arg17,
+	};
+
+	if (vm_count > partitions_len) {
+		return false;
+	}
+	assert(ffa_partition_info_regs_get_tag(ret) == 0);
+	assert(ffa_partition_info_regs_get_desc_size(ret) ==
+	       sizeof(struct ffa_partition_info));
+
+	curr_index = ffa_partition_info_regs_get_curr_idx(ret);
+	assert(start_index <= curr_index);
+
+	num_entries = curr_index - start_index + 1;
+	if (num_entries > (partitions_len - vm_count) || num_entries > 5) {
+		return false;
+	}
+
+	while (num_entries) {
+		uint64_t info = *(arg_ptrs[(idx * 3)]);
+		uint64_t uuid_lo = *(arg_ptrs[(idx * 3) + 1]);
+		uint64_t uuid_high = *(arg_ptrs[(idx * 3) + 2]);
+
+		partitions[vm_count].vm_id = info & 0xFFFF;
+		partitions[vm_count].vcpu_count = (info >> 16) & 0xFFFF;
+		partitions[vm_count].properties = (info >> 32);
+		partitions[vm_count].uuid.uuid[0] = uuid_lo & 0xFFFFFFFF;
+		partitions[vm_count].uuid.uuid[1] =
+			(uuid_lo >> 32) & 0xFFFFFFFF;
+		partitions[vm_count].uuid.uuid[2] = uuid_high & 0xFFFFFFFF;
+		partitions[vm_count].uuid.uuid[3] =
+			(uuid_high >> 32) & 0xFFFFFFFF;
+		vm_count++;
+		idx++;
+		num_entries--;
+	}
+
+	*ret_count = vm_count;
+	return true;
+}
+
+bool plat_ffa_partition_info_get_regs_forward(
+	const struct ffa_uuid *uuid, const uint16_t start_index,
+	const uint16_t tag, struct ffa_partition_info *partitions,
+	uint16_t partitions_len, ffa_vm_count_t *ret_count)
+{
+	(void)start_index;
+	(void)tag;
+	struct ffa_value ret;
+	uint16_t last_index = 0;
+	uint16_t curr_index = 0;
+	uint16_t swd_start_index = 0;
+
+	/*
+	 * Allow forwarding from the Hypervisor if TEE or SPMC exists and
+	 * declared as such in the Hypervisor manifest.
+	 */
+	if (!ffa_tee_enabled) {
+		return true;
+	}
+
+	ret = plat_ffa_partition_info_get_regs(uuid, swd_start_index, 0);
+	if (ffa_func_id(ret) != FFA_SUCCESS_64) {
+		return false;
+	}
+
+	if (!plat_ffa_fill_partition_info_from_regs(ret, swd_start_index,
+						    partitions, partitions_len,
+						    ret_count)) {
+		return false;
+	}
+
+	last_index = ffa_partition_info_regs_get_last_idx(ret);
+	curr_index = ffa_partition_info_regs_get_curr_idx(ret);
+	swd_start_index = curr_index + 1;
+	while (swd_start_index <= last_index) {
+		ret = plat_ffa_partition_info_get_regs(uuid, swd_start_index,
+						       0);
+		if (ffa_func_id(ret) != FFA_SUCCESS_32) {
+			return false;
+		}
+
+		if (!plat_ffa_fill_partition_info_from_regs(
+			    ret, swd_start_index, partitions, partitions_len,
+			    ret_count)) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
 /*
  * Forward helper for FFA_PARTITION_INFO_GET.
  * Emits FFA_PARTITION_INFO_GET from Hypervisor to SPMC if allowed.
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 28f077b..ddf9c22 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -1953,6 +1953,22 @@
 	return ret;
 }
 
+bool plat_ffa_partition_info_get_regs_forward(	// NOLINTNEXTLINE
+	const struct ffa_uuid *uuid,
+	const uint16_t start_index,  // NOLINTNEXTLINE
+	const uint16_t tag,
+	struct ffa_partition_info *partitions,	// NOLINTNEXTLINE
+	uint16_t partitions_len, ffa_vm_count_t *ret_count)
+{
+	(void)uuid;
+	(void)start_index;
+	(void)tag;
+	(void)partitions;
+	(void)partitions_len;
+	(void)ret_count;
+	return true;
+}
+
 /** Forward helper for FFA_PARTITION_INFO_GET. */
 void plat_ffa_partition_info_get_forward(  // NOLINTNEXTLINE
 	const struct ffa_uuid *uuid,	   // NOLINTNEXTLINE
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 9ce473d..b6a6a1a 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -393,6 +393,22 @@
 	return false;
 }
 
+bool plat_ffa_partition_info_get_regs_forward(	// NOLINTNEXTLINE
+	const struct ffa_uuid *uuid,
+	const uint16_t start_index,  // NOLINTNEXTLINE
+	const uint16_t tag,
+	struct ffa_partition_info *partitions,	// NOLINTNEXTLINE
+	uint16_t partitions_len, ffa_vm_count_t *ret_count)
+{
+	(void)uuid;
+	(void)start_index;
+	(void)tag;
+	(void)partitions;
+	(void)partitions_len;
+	(void)ret_count;
+	return true;
+}
+
 void plat_ffa_partition_info_get_forward(  // NOLINTNEXTLINE
 	const struct ffa_uuid *uuid,	   // NOLINTNEXTLINE
 	const uint32_t flags,		   // NOLINTNEXTLINE