SPCI: SPCI_FEATURES implementation.

Change-Id: I1bb3e32808d503ee75303a5e70b41645b2d7e86b
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 406b620..024973e 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -66,3 +66,4 @@
 	struct vm_locked to_locked, struct vm_locked from_locked,
 	struct spci_memory_region *memory_region, uint32_t memory_to_attributes,
 	enum spci_memory_share share);
+struct spci_value api_spci_features(uint32_t function_id);
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 9091875..52396aa 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -297,3 +297,19 @@
 {
 	return spci_call((struct spci_value){.func = SPCI_VERSION_32});
 }
+
+/**
+ * Discovery function returning information about the implementation of optional
+ * SPCI interfaces.
+ *
+ * Returns:
+ *  - SPCI_SUCCESS in .func if the the optional interface with function_id is
+ * implemented.
+ *  - SPCI_ERROR in .func if the optional interface with function_id is not
+ * implemented.
+ */
+static inline struct spci_value spci_features(uint32_t function_id)
+{
+	return spci_call((struct spci_value){.func = SPCI_FEATURES_32,
+					     .arg1 = function_id});
+}
diff --git a/src/api.c b/src/api.c
index 92328c8..7c1f12a 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1705,3 +1705,25 @@
 
 	return 0;
 }
+
+/**
+ * Discovery function returning information about the implementation of optional
+ * SPCI interfaces.
+ */
+struct spci_value api_spci_features(uint32_t function_id)
+{
+	switch (function_id) {
+	case SPCI_ERROR_32:
+	case SPCI_SUCCESS_32:
+	case SPCI_ID_GET_32:
+	case SPCI_YIELD_32:
+	case SPCI_VERSION_32:
+	case SPCI_FEATURES_32:
+	case SPCI_MSG_SEND_32:
+	case SPCI_MSG_POLL_32:
+	case SPCI_MSG_WAIT_32:
+		return (struct spci_value){.func = SPCI_SUCCESS_32};
+	default:
+		return spci_error(SPCI_NOT_SUPPORTED);
+	}
+}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index bbb115f..7928f54 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -312,6 +312,10 @@
 
 static bool spci_handler(struct spci_value *args, struct vcpu **next)
 {
+	/*
+	 * NOTE: When adding new methods to this handler update
+	 * api_spci_features accordingly.
+	 */
 	switch (args->func & ~SMCCC_CONVENTION_MASK) {
 	case SPCI_VERSION_32:
 		*args = api_spci_version();
@@ -319,6 +323,9 @@
 	case SPCI_ID_GET_32:
 		*args = api_spci_id_get(current());
 		return true;
+	case SPCI_FEATURES_32:
+		*args = api_spci_features(args->arg1);
+		return true;
 	case SPCI_YIELD_32:
 		api_yield(current(), next);
 
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index 96b1309..74302c6 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -158,6 +158,99 @@
 	EXPECT_EQ(ret.arg2, current_version);
 }
 
+/** Ensures that SPCI_FEATURES is reporting the expected interfaces. */
+TEST(spci, spci_features)
+{
+	struct spci_value ret;
+
+	ret = spci_features(SPCI_ERROR_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_SUCCESS_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_VERSION_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_FEATURES_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_ID_GET_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_YIELD_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_MSG_SEND_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_MSG_POLL_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_MSG_WAIT_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+
+	ret = spci_features(SPCI_YIELD_32);
+	EXPECT_EQ(ret.func, SPCI_SUCCESS_32);
+}
+
+/**
+ * Ensures that SPCI_FEATURES returns not supported for a bogus FID or
+ * currently non-implemented interfaces.
+ */
+TEST(spci, spci_features_not_supported)
+{
+	struct spci_value ret;
+
+	ret = spci_features(0);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(0x84000000);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_INTERRUPT_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_RX_RELEASE_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_RXTX_MAP_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_RXTX_UNMAP_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_PARTITION_INFO_GET_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_RUN_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_MSG_SEND_DIRECT_REQ_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_MSG_SEND_DIRECT_REQ_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+
+	ret = spci_features(SPCI_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(ret.func, SPCI_ERROR_32);
+	EXPECT_EQ(ret.arg2, SPCI_NOT_SUPPORTED);
+}
+
 /**
  * Test that floating-point operations work in the primary VM.
  */