SPCI: Implement SPCI_VERSION.

Change-Id: I57b8c6f1fd12cbc37bd7f56b96d9fa0c241d3ba9
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 1de07ec..259d59e 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -53,3 +53,4 @@
 int32_t api_spci_msg_recv(uint32_t attributes, struct vcpu *current,
 			  struct vcpu **next);
 int32_t api_spci_yield(struct vcpu *current, struct vcpu **next);
+int32_t api_spci_version(void);
diff --git a/inc/hf/spci.h b/inc/hf/spci.h
new file mode 100644
index 0000000..30dfa27
--- /dev/null
+++ b/inc/hf/spci.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "vmapi/hf/spci.h"
+
+#define SPCI_VERSION_MAJOR 0x0
+#define SPCI_VERSION_MINOR 0x9
+
+#define SPCI_VERSION_MAJOR_OFFSET 16
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index aaa2fa7..69296d6 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -239,3 +239,9 @@
 	return hf_call(HF_SHARE_MEMORY, (((uint64_t)vm_id) << 32) | share, addr,
 		       size);
 }
+
+/** Obtains the Hafnium's version of the implemented SPCI specification. */
+static inline int64_t spci_version(void)
+{
+	return hf_call(SPCI_VERSION_32, 0, 0, 0);
+}
diff --git a/src/api.c b/src/api.c
index 178c7f6..c1f337c 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1350,3 +1350,19 @@
 
 	return ret;
 }
+
+/** Returns the version of the implemented SPCI specification. */
+int32_t api_spci_version(void)
+{
+	/*
+	 * Ensure that both major and minor revision representation occupies at
+	 * most 15 bits.
+	 */
+	static_assert(0x8000 > SPCI_VERSION_MAJOR,
+		      "Major revision representation take more than 15 bits.");
+	static_assert(0x10000 > SPCI_VERSION_MINOR,
+		      "Minor revision representation take more than 16 bits.");
+
+	return (SPCI_VERSION_MAJOR << SPCI_VERSION_MAJOR_OFFSET) |
+	       SPCI_VERSION_MINOR;
+}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 9957a28..c580c1a 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -393,6 +393,10 @@
 	}
 
 	switch ((uint32_t)arg0) {
+	case SPCI_VERSION_32:
+		ret.user_ret = api_spci_version();
+		break;
+
 	case HF_VM_GET_ID:
 		ret.user_ret = api_vm_get_id(current());
 		break;
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index d715d19..984a846 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -125,3 +125,15 @@
 	/* Wait for CPU to release the lock. */
 	sl_lock(&lock);
 }
+
+/** Ensures that the Hafnium SPCI version is reported as expected. */
+TEST(spci, spci_version)
+{
+	const int32_t major_revision = 0;
+	const int32_t major_revision_offset = 16;
+	const int32_t minor_revision = 9;
+	const int32_t current_version =
+		(major_revision << major_revision_offset) | minor_revision;
+
+	EXPECT_EQ(spci_version(), current_version);
+}