feat: FFA_FEATURES update for FF-A v1.1 features

Update to handling of FFA_FEATURES to encompass:
- Notifications func IDs.
- Query of interrupt IDs for Schedule Receiver Interrupt, Notification
Pending Interrupt, and Managed Exit Interrupt.
- Tests to FFA_FEATURES to validate aforementioned points.

Change-Id: If6810ea222ce30e40ab0d004e3631ca9fae316a8
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/inc/hf/api.h b/inc/hf/api.h
index b31d7b5..aae2298 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -61,6 +61,7 @@
 					    const struct ffa_uuid *uuid);
 struct ffa_value api_ffa_id_get(const struct vcpu *current);
 struct ffa_value api_ffa_spm_id_get(void);
+struct ffa_value api_ffa_feature_success(uint32_t arg2);
 struct ffa_value api_ffa_features(uint32_t function_id);
 struct ffa_value api_ffa_run(ffa_vm_id_t vm_id, ffa_vcpu_index_t vcpu_idx,
 			     struct vcpu *current, struct vcpu **next);
diff --git a/inc/hf/arch/plat/ffa.h b/inc/hf/arch/plat/ffa.h
index 8ce3d10..99e9f6f 100644
--- a/inc/hf/arch/plat/ffa.h
+++ b/inc/hf/arch/plat/ffa.h
@@ -51,7 +51,7 @@
 };
 
 /** Returns information on features that are specific to the platform. */
-struct ffa_value plat_ffa_features(uint32_t function_id);
+struct ffa_value plat_ffa_features(uint32_t function_feature_id);
 /** Returns the SPMC ID. */
 struct ffa_value plat_ffa_spmc_id_get(void);
 
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index f3d9d15..903bb3e 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -93,6 +93,24 @@
 
 /* clang-format on */
 
+/**
+ * FF-A Feature ID, to be used with interface FFA_FEATURES.
+ * As defined in the FF-A v1.1 Beta specification, table 13.10, in section
+ * 13.2.
+ */
+
+#define FFA_FEATURES_FUNC_ID_MASK UINT32_C(0x1 << 31)
+#define FFA_FEATURES_FEATURE_ID_MASK UINT32_C(0x7F)
+
+/* Query interrupt ID of Notification Pending Interrupt. */
+#define FFA_FEATURE_NPI 0x1U
+
+/* Query interrupt ID of Schedule Receiver Interrupt. */
+#define FFA_FEATURE_SRI 0x2U
+
+/* Query interrupt ID of the Managed Exit Interrupt. */
+#define FFA_FEATURE_MEI 0x3U
+
 /* FF-A function specific constants. */
 #define FFA_MSG_RECV_BLOCK 0x1
 #define FFA_MSG_RECV_BLOCK_MASK 0x1
@@ -330,6 +348,11 @@
 	return (args.arg4 >> 16) & 0xffff;
 }
 
+static inline uint32_t ffa_feature_intid(struct ffa_value args)
+{
+	return (uint32_t)args.arg2;
+}
+
 /**
  * Holds the UUID in a struct that is mappable directly to the SMCC calling
  * convention, which is used for FF-A calls.
@@ -405,8 +428,6 @@
 
 #define MAX_FFA_NOTIFICATIONS 64U
 
-#define FFA_SCHEDULE_RECEIVER_INTERRUPT_ID 8
-
 /**
  * Flag for notification bind and set, to specify call is about per-vCPU
  * notifications.
diff --git a/inc/vmapi/hf/types.h b/inc/vmapi/hf/types.h
index f611bc9..d8a9e3f 100644
--- a/inc/vmapi/hf/types.h
+++ b/inc/vmapi/hf/types.h
@@ -55,8 +55,11 @@
 /** The virtual interrupt ID used for managed exit. */
 #define HF_MANAGED_EXIT_INTID 4
 
-/** The virtual interrupt ID used for notification pending interrupt */
-#define HF_NOTIFICATION_PENDING_INTERRUPT_INTID 5
+/** The virtual interrupt ID used for notification pending interrupt. */
+#define HF_NOTIFICATION_PENDING_INTID 5
+
+/** The physical interrupt ID use for the schedule receiver interrupt. */
+#define HF_SCHEDULE_RECEIVER_INTID 8
 
 /** Type of interrupts */
 enum interrupt_type {
diff --git a/src/api.c b/src/api.c
index e353bd9..21bec1d 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1811,12 +1811,32 @@
 }
 
 /**
+ * Helper for success return of FFA_FEATURES, for when it is used to query
+ * an interrupt ID.
+ */
+struct ffa_value api_ffa_feature_success(uint32_t arg2)
+{
+	return (struct ffa_value){
+		.func = FFA_SUCCESS_32, .arg1 = 0U, .arg2 = arg2};
+}
+
+/**
  * Discovery function returning information about the implementation of optional
  * FF-A interfaces.
  */
-struct ffa_value api_ffa_features(uint32_t function_id)
+struct ffa_value api_ffa_features(uint32_t feature_function_id)
 {
-	switch (function_id) {
+	/*
+	 * According to table 13.8 of FF-A v1.1 Beta 0 spec, bits [30:8] MBZ
+	 * if using a feature ID.
+	 */
+	if ((feature_function_id & FFA_FEATURES_FUNC_ID_MASK) == 0U &&
+	    (feature_function_id & ~FFA_FEATURES_FEATURE_ID_MASK) != 0) {
+		return ffa_error(FFA_NOT_SUPPORTED);
+	}
+
+	switch (feature_function_id) {
+	/* Check support of the given Function ID. */
 	case FFA_ERROR_32:
 	case FFA_SUCCESS_32:
 	case FFA_INTERRUPT_32:
@@ -1843,10 +1863,26 @@
 #if (MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED)
 	/* FF-A v1.1 features. */
 	case FFA_SPM_ID_GET_32:
+	case FFA_NOTIFICATION_BITMAP_CREATE_32:
+	case FFA_NOTIFICATION_BITMAP_DESTROY_32:
+	case FFA_NOTIFICATION_BIND_32:
+	case FFA_NOTIFICATION_UNBIND_32:
+	case FFA_NOTIFICATION_SET_32:
+	case FFA_NOTIFICATION_GET_32:
+	case FFA_NOTIFICATION_INFO_GET_64:
 #endif
 		return (struct ffa_value){.func = FFA_SUCCESS_32};
+
+#if (MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED)
+	/* Check support of a feature provided respective feature ID. */
+	case FFA_FEATURE_NPI:
+		return api_ffa_feature_success(HF_NOTIFICATION_PENDING_INTID);
+	case FFA_FEATURE_SRI:
+		return api_ffa_feature_success(HF_SCHEDULE_RECEIVER_INTID);
+#endif
+	/* Platform specific feature support. */
 	default:
-		return arch_ffa_features(function_id);
+		return arch_ffa_features(feature_function_id);
 	}
 }
 
@@ -2833,9 +2869,9 @@
 			vm_get_vcpu(receiver_locked.vm, vcpu_id);
 
 		dlog_verbose("Per-vCPU notification, pending NPI.\n");
-		internal_interrupt_inject(
-			target_vcpu, HF_NOTIFICATION_PENDING_INTERRUPT_INTID,
-			current, NULL);
+		internal_interrupt_inject(target_vcpu,
+					  HF_NOTIFICATION_PENDING_INTID,
+					  current, NULL);
 	}
 
 	ret = (struct ffa_value){.func = FFA_SUCCESS_32};
diff --git a/src/arch/aarch64/hypervisor/ffa.c b/src/arch/aarch64/hypervisor/ffa.c
index 363ac85..b74cabb 100644
--- a/src/arch/aarch64/hypervisor/ffa.c
+++ b/src/arch/aarch64/hypervisor/ffa.c
@@ -20,9 +20,9 @@
 /**
  * Returns information for features with arch specific implementation.
  */
-struct ffa_value arch_ffa_features(uint32_t function_id)
+struct ffa_value arch_ffa_features(uint32_t function_feature_id)
 {
-	return plat_ffa_features(function_id);
+	return plat_ffa_features(function_feature_id);
 }
 
 /**
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 1aafad2..df0ccb2 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -11,9 +11,9 @@
 #include "hf/vcpu.h"
 #include "hf/vm.h"
 
-struct ffa_value plat_ffa_features(uint32_t function_id)
+struct ffa_value plat_ffa_features(uint32_t function_feature_id)
 {
-	(void)function_id;
+	(void)function_feature_id;
 	return ffa_error(FFA_NOT_SUPPORTED);
 }
 
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index b4f6df9..86407c2 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -28,9 +28,9 @@
 alignas(FFA_PAGE_SIZE) static uint8_t other_world_recv_buffer[HF_MAILBOX_SIZE];
 
 /** Returns information on features specific to the NWd. */
-struct ffa_value plat_ffa_features(uint32_t function_id)
+struct ffa_value plat_ffa_features(uint32_t function_feature_id)
 {
-	switch (function_id) {
+	switch (function_feature_id) {
 	case FFA_MSG_POLL_32:
 	case FFA_YIELD_32:
 	case FFA_MSG_SEND_32:
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 8b272b9..7f4c4e1 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -107,11 +107,23 @@
 }
 
 /** Returns information on features specific to the SWd. */
-struct ffa_value plat_ffa_features(uint32_t function_id)
+struct ffa_value plat_ffa_features(uint32_t function_feature_id)
 {
-	(void)function_id;
+	struct ffa_value ret;
+
+	switch (function_feature_id) {
+#if (MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED)
+	case FFA_FEATURE_MEI:
+		ret = api_ffa_feature_success(HF_MANAGED_EXIT_INTID);
+		break;
+#endif
+	default:
+		ret = ffa_error(FFA_NOT_SUPPORTED);
+		break;
+	}
+
 	/* There are no features only supported in the SWd */
-	return ffa_error(FFA_NOT_SUPPORTED);
+	return ret;
 }
 
 struct ffa_value plat_ffa_spmc_id_get(void)
@@ -1118,9 +1130,9 @@
 static void plat_ffa_send_schedule_receiver_interrupt(struct cpu *cpu)
 {
 	dlog_verbose("Setting Schedule Receiver SGI %d on core: %d\n",
-		     FFA_SCHEDULE_RECEIVER_INTERRUPT_ID, cpu_index(cpu));
+		     HF_SCHEDULE_RECEIVER_INTID, cpu_index(cpu));
 
-	plat_interrupts_send_sgi(FFA_SCHEDULE_RECEIVER_INTERRUPT_ID, false,
+	plat_interrupts_send_sgi(HF_SCHEDULE_RECEIVER_INTID, false,
 				 (1 << cpu_index(cpu)), false);
 }
 
@@ -1161,7 +1173,7 @@
 	/* TODO: when supported, make the interrupt driver use cpu structure. */
 	(void)cpu;
 
-	interrupt_desc_set_id(&sri_desc, FFA_SCHEDULE_RECEIVER_INTERRUPT_ID);
+	interrupt_desc_set_id(&sri_desc, HF_SCHEDULE_RECEIVER_INTID);
 	interrupt_desc_set_priority(&sri_desc, SRI_PRIORITY);
 	interrupt_desc_set_valid(&sri_desc, true);
 
@@ -1198,8 +1210,7 @@
 			struct vcpu_locked next_locked = vcpu_lock(next);
 
 			api_interrupt_inject_locked(
-				next_locked,
-				HF_NOTIFICATION_PENDING_INTERRUPT_INTID,
+				next_locked, HF_NOTIFICATION_PENDING_INTID,
 				current, NULL);
 
 			vcpu_unlock(&next_locked);
diff --git a/test/vmapi/ffa_secure_partition_only/secure_partition.c b/test/vmapi/ffa_secure_partition_only/secure_partition.c
index 6054ef7..b723e93 100644
--- a/test/vmapi/ffa_secure_partition_only/secure_partition.c
+++ b/test/vmapi/ffa_secure_partition_only/secure_partition.c
@@ -30,7 +30,7 @@
 }
 
 /** Ensures that FFA_FEATURES is reporting the expected interfaces. */
-TEST(ffa, ffa_features)
+TEST(ffa_features, succeeds_ffa_call_ids)
 {
 	struct ffa_value ret;
 
@@ -96,13 +96,56 @@
 
 	ret = ffa_features(FFA_MSG_SEND_DIRECT_RESP_32);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_BITMAP_DESTROY_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_BITMAP_DESTROY_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_SET_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_GET_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_BIND_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_UNBIND_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_NOTIFICATION_INFO_GET_64);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+}
+
+/** Validates return for FFA_FEATURES provided a valid feature ID. */
+TEST(ffa_features, succeeds_feature_ids)
+{
+	struct ffa_value ret = ffa_features(FFA_FEATURE_NPI);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	EXPECT_EQ(ffa_feature_intid(ret), HF_NOTIFICATION_PENDING_INTID);
+
+	ret = ffa_features(FFA_FEATURE_SRI);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	EXPECT_EQ(ffa_feature_intid(ret), HF_SCHEDULE_RECEIVER_INTID);
+
+	ret = ffa_features(FFA_FEATURE_MEI);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	EXPECT_EQ(ffa_feature_intid(ret), HF_MANAGED_EXIT_INTID);
+}
+
+/** Validates error return for FFA_FEATURES provided a wrongful feature ID. */
+TEST(ffa_features, fails_if_feature_id_wrong)
+{
+	EXPECT_FFA_ERROR(ffa_features(0x0FFFFF), FFA_NOT_SUPPORTED);
 }
 
 /**
  * Ensures that FFA_FEATURES returns not supported for a bogus FID or
  * currently non-implemented interfaces.
  */
-TEST(ffa, ffa_features_not_supported)
+TEST(ffa_features, fails_func_id_not_supported)
 {
 	struct ffa_value ret;