feat(notifications): forward notification info get to SPMC

The hypervisor forward FFA_NOTIFICATION_INFO_GET to the SPMC, to
retrieve the pending notifications info from SWd. It includes
notifications to SPs, and notifications from SPs to VMs.

Change-Id: I46549cc5ef7eb49addace546946222792d2be25b
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/api.c b/src/api.c
index 8feb745..cb27a58 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2972,6 +2972,16 @@
 		return ffa_error(FFA_NOT_SUPPORTED);
 	}
 
+	/*
+	 * Forward call to the other world, and fill the arrays used to assemble
+	 * return.
+	 */
+	plat_ffa_notification_info_get_forward(
+		ids, &ids_count, lists_sizes, &lists_count,
+		FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
+
+	list_is_full = ids_count == FFA_NOTIFICATIONS_INFO_GET_MAX_IDS;
+
 	/* Get notifications' info from this world */
 	for (ffa_vm_count_t index = 0; index < vm_get_count() && !list_is_full;
 	     ++index) {
@@ -2992,6 +3002,8 @@
 	}
 
 	if (ids_count == 0) {
+		dlog_verbose(
+			"Notification info get has no data to retrieve.\n");
 		return ffa_error(FFA_NO_DATA);
 	}
 
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 4eecac1..baeee11 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -272,3 +272,16 @@
 
 	return ffa_error(FFA_NOT_SUPPORTED);
 }
+
+void plat_ffa_notification_info_get_forward(const uint16_t *ids,
+					    const uint32_t *ids_count,
+					    const uint32_t *lists_sizes,
+					    const uint32_t *lists_count,
+					    const uint32_t ids_count_max)
+{
+	(void)ids;
+	(void)ids_count;
+	(void)lists_sizes;
+	(void)lists_count;
+	(void)ids_count_max;
+}
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 8a38579..dbc061b 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -13,6 +13,7 @@
 #include "hf/dlog.h"
 #include "hf/ffa.h"
 #include "hf/ffa_internal.h"
+#include "hf/std.h"
 #include "hf/vcpu.h"
 #include "hf/vm.h"
 
@@ -354,6 +355,78 @@
 	return vm_id_is_current_world(vm_id);
 }
 
+void plat_ffa_notification_info_get_forward(uint16_t *ids, uint32_t *ids_count,
+					    uint32_t *lists_sizes,
+					    uint32_t *lists_count,
+					    const uint32_t ids_count_max)
+{
+	CHECK(ids != NULL);
+	CHECK(ids_count != NULL);
+	CHECK(lists_sizes != NULL);
+	CHECK(lists_count != NULL);
+	CHECK(ids_count_max == FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
+
+	uint32_t local_lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS];
+	struct ffa_value ret;
+
+	dlog_verbose("Forwarding notification info get to SPMC.\n");
+
+	ret = arch_other_world_call((struct ffa_value){
+		.func = FFA_NOTIFICATION_INFO_GET_64,
+	});
+
+	if (ret.func == FFA_ERROR_32) {
+		dlog_verbose("No notifications returned by SPMC.\n");
+		return;
+	}
+
+	*lists_count = ffa_notification_info_get_lists_count(ret);
+
+	if (*lists_count > ids_count_max) {
+		*lists_count = 0;
+		return;
+	}
+
+	/*
+	 * The count of ids should be at least the number of lists, to encompass
+	 * for at least the ids of the FF-A endpoints.
+	 * List sizes will be between 0 and 3, and relates to the counting of
+	 * vCPU of the endpoint that have pending notifications.
+	 * If `lists_count` is already ids_count_max, each list size must be 0.
+	 */
+	*ids_count = *lists_count;
+
+	for (uint32_t i = 0; i < *lists_count; i++) {
+		local_lists_sizes[i] =
+			ffa_notification_info_get_list_size(ret, i + 1);
+
+		/*
+		 * ... sum the counting of each list size that are part of the
+		 * main list.
+		 */
+		*ids_count += local_lists_sizes[i];
+	}
+
+	/*
+	 * Sanity check returned `lists_count` and determined `ids_count`.
+	 * If something wrong, reset arguments to 0 such that hypervisor's
+	 * handling of FFA_NOTIFICATION_INFO_GET can proceed without SPMC's
+	 * values.
+	 */
+	if (*ids_count > ids_count_max) {
+		*ids_count = 0;
+		return;
+	}
+
+	/* Copy now lists sizes, as return sizes have been validated. */
+	memcpy_s(lists_sizes, sizeof(lists_sizes[0]) * ids_count_max,
+		 local_lists_sizes, FFA_NOTIFICATIONS_INFO_GET_MAX_IDS);
+
+	/* Unpack the notifications info from the return. */
+	memcpy_s(ids, sizeof(ids[0]) * ids_count_max, &ret.arg3,
+		 sizeof(ret.arg3) * FFA_NOTIFICATIONS_INFO_GET_REGS_RET);
+}
+
 bool plat_ffa_notifications_get_from_sp(struct vm_locked receiver_locked,
 					ffa_vcpu_index_t vcpu_id,
 					ffa_notifications_bitmap_t *from_sp,
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index b48b7c5..23cc039 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -267,6 +267,18 @@
 		!vm_id_is_current_world(receiver_id));
 }
 
+void plat_ffa_notification_info_get_forward(  // NOLINTNEXTLINE
+	uint16_t *ids, uint32_t *ids_count,   // NOLINTNEXTLINE
+	uint32_t *lists_sizes, uint32_t *lists_count,
+	const uint32_t ids_count_max)
+{
+	(void)ids;
+	(void)ids_count;
+	(void)lists_sizes;
+	(void)lists_count;
+	(void)ids_count_max;
+}
+
 ffa_memory_handle_t plat_ffa_memory_handle_make(uint64_t index)
 {
 	return (index & ~FFA_MEMORY_HANDLE_ALLOCATOR_MASK) |
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 1f6b587..8736afa 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -244,3 +244,15 @@
 
 	return true;
 }
+
+void plat_ffa_notification_info_get_forward(  // NOLINTNEXTLINE
+	uint16_t *ids, uint32_t *ids_count,   // NOLINTNEXTLINE
+	uint32_t *lists_sizes, uint32_t *lists_count,
+	const uint32_t ids_count_max)
+{
+	(void)ids;
+	(void)ids_count;
+	(void)lists_sizes;
+	(void)lists_count;
+	(void)ids_count_max;
+}