feat(memory share): FFA_FEATURES(FFA_MEM_RETRIEVE_REQ)

Table 13.14 in the FF-A v.1.1 REL0 specification requires bits 31:2
and bit 0 of the input parameter to be 0 (MBZ) when querying
`FFA_FEATURES` for `FFA_MEM_RETRIEVE_REQ`.
It also requires that the return parameter must report support for
dynamically allocated buffers (bit 0), support for the NS bit (bit 1),
and support for retrieval by the hypervisor (bit 2).

This patch modifies `api_ffa_features` to validate the input parameters
and report feature support when the function id is
`FFA_MEM_RETRIEVE_REQ`

Signed-off-by: Karl Meakin <karl.meakin@arm.com>
Change-Id: Ie415083ba0e5cd506a1162ff9649e3e188e83737
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 45622fa..974ebdf 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -75,7 +75,8 @@
 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_features(uint32_t function_id, uint32_t input_property,
+				  uint32_t ffa_version);
 struct ffa_value api_ffa_msg_wait(struct vcpu *current, struct vcpu **next,
 				  struct ffa_value *args);
 struct ffa_value api_ffa_run(ffa_vm_id_t vm_id, ffa_vcpu_index_t vcpu_idx,
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 4776421..602b147 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -442,10 +442,29 @@
  *  - FFA_ERROR in .func if the optional interface with function_id is not
  * implemented.
  */
-static inline struct ffa_value ffa_features(uint32_t function_id)
+static inline struct ffa_value ffa_features(uint64_t function_id)
 {
-	return ffa_call((struct ffa_value){.func = FFA_FEATURES_32,
-					   .arg1 = function_id});
+	return ffa_call((struct ffa_value){
+		.func = FFA_FEATURES_32,
+		.arg1 = function_id,
+	});
+}
+
+/**
+ * Discovery function returning information about the implementation of optional
+ * FF-A interfaces which require an extra input property
+ *
+ * Returns:
+ *  - FFA_SUCCESS in .func if the optional interface with function_id is
+ * implemented.
+ *  - FFA_ERROR in .func if the optional interface with function_id is not
+ * implemented.
+ */
+static inline struct ffa_value ffa_features_with_input_property(
+	uint64_t function_id, uint64_t param)
+{
+	return ffa_call((struct ffa_value){
+		.func = FFA_FEATURES_32, .arg1 = function_id, .arg2 = param});
 }
 
 static inline struct ffa_value ffa_msg_send_direct_req(
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index 4db2c14..5717aa6 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -107,6 +107,21 @@
 #define FFA_FEATURES_FUNC_ID_MASK (UINT32_C(1) << 31)
 #define FFA_FEATURES_FEATURE_ID_MASK UINT32_C(0x7F)
 
+/**
+ * Defined in Table 13.14 in the FF-A v.1.1 REL0 specification.
+ * Bits[31:2] and Bit[0] of input are reserved (must be zero).
+ * Bit[0]: dynamically allocated buffer support.
+ * Bit[1]: NS bit handling.
+ * Bit[2]: support for retrieval by hypervisor.
+ */
+#define FFA_FEATURES_MEM_RETRIEVE_REQ_BUFFER_SUPPORT 0
+#define FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT (UINT32_C(1) << 1)
+#define FFA_FEATURES_MEM_RETRIEVE_REQ_HYPERVISOR_SUPPORT (UINT32_C(1) << 2)
+#define FFA_FEATURES_MEM_RETRIEVE_REQ_MBZ_MASK            \
+	(~(FFA_FEATURES_MEM_RETRIEVE_REQ_BUFFER_SUPPORT | \
+	   FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT |     \
+	   FFA_FEATURES_MEM_RETRIEVE_REQ_HYPERVISOR_SUPPORT))
+
 /* Query interrupt ID of Notification Pending Interrupt. */
 #define FFA_FEATURE_NPI 0x1U
 
diff --git a/src/api.c b/src/api.c
index 18e2f97..9ceb743 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2385,7 +2385,8 @@
  * Discovery function returning information about the implementation of optional
  * FF-A interfaces.
  */
-struct ffa_value api_ffa_features(uint32_t feature_function_id)
+struct ffa_value api_ffa_features(uint32_t feature_function_id,
+				  uint32_t input_property, uint32_t ffa_version)
 {
 	/*
 	 * According to table 13.8 of FF-A v1.1 Beta 0 spec, bits [30:8] MBZ
@@ -2396,6 +2397,14 @@
 		return ffa_error(FFA_NOT_SUPPORTED);
 	}
 
+	if (feature_function_id != FFA_MEM_RETRIEVE_REQ_32 &&
+	    input_property != 0U) {
+		dlog_verbose(
+			"input_property must be zero.\ninput_property = %u.\n",
+			input_property);
+		return ffa_error(FFA_INVALID_PARAMETERS);
+	}
+
 	switch (feature_function_id) {
 	/* Check support of the given Function ID. */
 	case FFA_ERROR_32:
@@ -2413,7 +2422,6 @@
 	case FFA_MEM_DONATE_32:
 	case FFA_MEM_LEND_32:
 	case FFA_MEM_SHARE_32:
-	case FFA_MEM_RETRIEVE_REQ_32:
 	case FFA_MEM_RETRIEVE_RESP_32:
 	case FFA_MEM_RELINQUISH_32:
 	case FFA_MEM_RECLAIM_32:
@@ -2441,6 +2449,29 @@
 	case FFA_PARTITION_INFO_GET_REGS_64:
 #endif
 		return (struct ffa_value){.func = FFA_SUCCESS_32};
+	case FFA_MEM_RETRIEVE_REQ_32:
+		if ((input_property & FFA_FEATURES_MEM_RETRIEVE_REQ_MBZ_MASK) !=
+		    0U) {
+			dlog_verbose(
+				"Bits[31:2] and Bit[0] of input_property must "
+				"be zero.\ninput_property = %u.\n",
+				input_property);
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+
+		if (ffa_version >= MAKE_FFA_VERSION(1, 1)) {
+			if ((input_property &
+			     FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT) == 0U) {
+				dlog_verbose("NS bit support must be 1.\n");
+				return ffa_error(FFA_INVALID_PARAMETERS);
+			}
+		}
+
+		return api_ffa_feature_success(
+			FFA_FEATURES_MEM_RETRIEVE_REQ_BUFFER_SUPPORT |
+			(input_property &
+			 FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT) |
+			FFA_FEATURES_MEM_RETRIEVE_REQ_HYPERVISOR_SUPPORT);
 
 #if (MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED)
 	/* Check support of a feature provided respective feature ID. */
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index ad062dc..0146ad5 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -557,7 +557,8 @@
 		*args = api_ffa_spm_id_get();
 		return true;
 	case FFA_FEATURES_32:
-		*args = api_ffa_features(args->arg1);
+		*args = api_ffa_features(args->arg1, args->arg2,
+					 current->vm->ffa_version);
 		return true;
 	case FFA_RX_RELEASE_32:
 		*args = api_ffa_rx_release(ffa_receiver(*args), current, next);
diff --git a/test/vmapi/ffa_secure_partition_only/secure_partition.c b/test/vmapi/ffa_secure_partition_only/secure_partition.c
index fdeeaf2..453f68c 100644
--- a/test/vmapi/ffa_secure_partition_only/secure_partition.c
+++ b/test/vmapi/ffa_secure_partition_only/secure_partition.c
@@ -36,6 +36,7 @@
 }
 
 /** Ensures that FFA_FEATURES is reporting the expected interfaces. */
+// NOLINTNEXTLINE(readability-function-size)
 TEST(ffa_features, succeeds_ffa_call_ids)
 {
 	struct ffa_value ret;
@@ -85,8 +86,15 @@
 	ret = ffa_features(FFA_MEM_SHARE_32);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
 
-	ret = ffa_features(FFA_MEM_RETRIEVE_REQ_32);
+	ret = ffa_features_with_input_property(
+		FFA_MEM_RETRIEVE_REQ_32,
+		FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	EXPECT_EQ(ret.arg1, 0);
+	EXPECT_EQ(ret.arg2,
+		  FFA_FEATURES_MEM_RETRIEVE_REQ_BUFFER_SUPPORT |
+			  FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT |
+			  FFA_FEATURES_MEM_RETRIEVE_REQ_HYPERVISOR_SUPPORT);
 
 	ret = ffa_features(FFA_MEM_RETRIEVE_RESP_32);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
@@ -171,6 +179,31 @@
 }
 
 /**
+ * Validates error return for FFA_FEATURES given:
+ *  - Version is v1.1 or greater
+ *  - function_id is FFA_MEM_RETRIEVE_REQ_32
+ *  - parameter does not have bit 1 set
+ */
+TEST(ffa_features, fails_if_parameter_wrong_and_v_1_1)
+{
+	ffa_version(MAKE_FFA_VERSION(1, 1));
+
+	EXPECT_FFA_ERROR(
+		ffa_features_with_input_property(FFA_MEM_RETRIEVE_REQ_32, 0),
+		FFA_INVALID_PARAMETERS);
+}
+
+TEST(ffa_features, does_not_fail_if_parameter_wrong_and_v_1_0)
+{
+	struct ffa_value ret;
+
+	ffa_version(MAKE_FFA_VERSION(1, 0));
+
+	ret = ffa_features_with_input_property(FFA_MEM_RETRIEVE_REQ_32, 0);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+}
+
+/**
  * Ensures that FFA_FEATURES returns not supported for a bogus FID or
  * currently non-implemented interfaces.
  */
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index 8c90c61..2ee64c8 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -10,6 +10,7 @@
 
 #include "hf/arch/vm/power_mgmt.h"
 
+#include "hf/ffa.h"
 #include "hf/spinlock.h"
 
 #include "vmapi/hf/call.h"
@@ -242,7 +243,9 @@
 	ret = ffa_features(FFA_MEM_SHARE_32);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
 
-	ret = ffa_features(FFA_MEM_RETRIEVE_REQ_32);
+	ret = ffa_features_with_input_property(
+		FFA_MEM_RETRIEVE_REQ_32,
+		FFA_FEATURES_MEM_RETRIEVE_REQ_NS_SUPPORT);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
 
 	ret = ffa_features(FFA_MEM_RETRIEVE_RESP_32);