feat(notifications): get framework notifications

Get framework notifications in FFA_NOTIFICATION_GET handler. If flag
for SPM notifications is set:
- SPMC returns SPM framework notifications set for the receiver VM.
- Hypervisor forwards call to SPMC.
If flag for Hypervisor notifications is set:
- SPMC returns Hypervisor framework notifications set for the VM;
- Hypervisor retrieves Hypervisor framework notifications set in the
  SPMC and merge with its own bitmap.

Change-Id: If371accc35bf2bd6e0425fadd5e1901286eaadc4
Signed-off-by: J-Alves <joao.alves@arm.com>
Signed-off-by: Federico Recanati <federico.recanati@arm.com>
diff --git a/src/api.c b/src/api.c
index 79095f9..7424b24 100644
--- a/src/api.c
+++ b/src/api.c
@@ -3233,6 +3233,18 @@
 			receiver_locked, true, vcpu_id);
 	}
 
+	if ((flags & FFA_NOTIFICATION_FLAG_BITMAP_HYP) != 0U ||
+	    (flags & FFA_NOTIFICATION_FLAG_BITMAP_SPM) != 0U) {
+		if (!plat_ffa_notifications_get_framework_notifications(
+			    receiver_locked, &framework_notifications, flags,
+			    vcpu_id, &ret)) {
+			dlog_verbose(
+				"Failed to get notifications from "
+				"framework.\n");
+			goto out;
+		}
+	}
+
 	ret = api_ffa_notification_get_success_return(
 		sp_notifications, vm_notifications, framework_notifications);
 
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index acd3eda..ca8a080 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -205,6 +205,19 @@
 	return false;
 }
 
+bool plat_ffa_notifications_get_framework_notifications(
+	struct vm_locked receiver_locked,
+	ffa_notifications_bitmap_t *from_fwk,  // NOLINT
+	uint32_t flags, struct ffa_value *ret)
+{
+	(void)receiver_locked;
+	(void)from_fwk;
+	(void)flags;
+	(void)ret;
+
+	return false;
+}
+
 bool plat_ffa_notification_set_forward(ffa_vm_id_t sender_vm_id,
 				       ffa_vm_id_t receiver_vm_id,
 				       uint32_t flags,
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 995d322..cd473fd 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -502,10 +502,10 @@
 					ffa_notifications_bitmap_t *from_sp,
 					struct ffa_value *ret)
 {
-	CHECK(from_sp != NULL && ret != NULL);
-
 	ffa_vm_id_t receiver_id = receiver_locked.vm->id;
 
+	assert(from_sp != NULL && ret != NULL);
+
 	*ret = arch_other_world_call((struct ffa_value){
 		.func = FFA_NOTIFICATION_GET_32,
 		.arg1 = (vcpu_id << 16) | receiver_id,
@@ -521,6 +521,40 @@
 	return true;
 }
 
+bool plat_ffa_notifications_get_framework_notifications(
+	struct vm_locked receiver_locked, ffa_notifications_bitmap_t *from_fwk,
+	uint32_t flags, ffa_vcpu_index_t vcpu_id, struct ffa_value *ret)
+{
+	ffa_vm_id_t receiver_id = receiver_locked.vm->id;
+	ffa_notifications_bitmap_t spm_notifications = 0;
+
+	(void)flags;
+
+	assert(from_fwk != NULL);
+	assert(ret != NULL);
+
+	/* Get SPMC notifications. */
+	if (ffa_tee_enabled) {
+		*ret = arch_other_world_call((struct ffa_value){
+			.func = FFA_NOTIFICATION_GET_32,
+			.arg1 = (vcpu_id << 16) | receiver_id,
+			.arg2 = FFA_NOTIFICATION_FLAG_BITMAP_SPM,
+		});
+
+		if (ffa_func_id(*ret) == FFA_ERROR_32) {
+			return false;
+		}
+
+		spm_notifications = ffa_notification_get_from_framework(*ret);
+	}
+
+	/* Merge notifications from SPMC and Hypervisor. */
+	*from_fwk = spm_notifications |
+		    vm_notifications_framework_get_pending(receiver_locked);
+
+	return true;
+}
+
 bool plat_ffa_vm_notifications_info_get(     // NOLINTNEXTLINE
 	uint16_t *ids, uint32_t *ids_count,  // NOLINTNEXTLINE
 	uint32_t *lists_sizes,		     // NOLINTNEXTLINE
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 77bdbe7..e8bcfc8 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -660,6 +660,29 @@
 	return true;
 }
 
+bool plat_ffa_notifications_get_framework_notifications(
+	struct vm_locked receiver_locked, ffa_notifications_bitmap_t *from_fwk,
+	uint32_t flags, ffa_vcpu_index_t vcpu_id, struct ffa_value *ret)
+{
+	assert(from_fwk != NULL);
+	assert(ret != NULL);
+
+	(void)vcpu_id;
+
+	if (!vm_id_is_current_world(receiver_locked.vm->id) &&
+	    (flags & FFA_NOTIFICATION_FLAG_BITMAP_HYP) != 0U) {
+		dlog_error(
+			"Notification get flag from hypervisor in call to SPMC "
+			"MBZ.\n");
+		*ret = ffa_error(FFA_INVALID_PARAMETERS);
+		return false;
+	}
+
+	*from_fwk = vm_notifications_framework_get_pending(receiver_locked);
+
+	return true;
+}
+
 bool plat_ffa_vm_notifications_info_get(uint16_t *ids, uint32_t *ids_count,
 					uint32_t *lists_sizes,
 					uint32_t *lists_count,
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 2f54c71..a18b34d 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -207,6 +207,20 @@
 	return false;
 }
 
+bool plat_ffa_notifications_get_framework_notifications(
+	struct vm_locked receiver_locked,
+	ffa_notifications_bitmap_t *from_fwk,  // NOLINT
+	uint32_t flags, ffa_vcpu_index_t vcpu_id, struct ffa_value *ret)
+{
+	(void)receiver_locked;
+	(void)from_fwk;
+	(void)flags;
+	(void)vcpu_id;
+	(void)ret;
+
+	return false;
+}
+
 bool plat_ffa_notification_set_forward(ffa_vm_id_t sender_vm_id,
 				       ffa_vm_id_t receiver_vm_id,
 				       uint32_t flags,