Initial commit of ported FF-A code and some tests

- Ported FF-A interface code from Hafnium
- Added some tests and adapted some others

All the new tests pass as images running in Hafnium (both primary and secondary).

Signed-off-by: Fuad Tabba <tabba@google.com>
diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index 27a9bf0..e176974 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -30,6 +30,9 @@
 tests = $(TEST_DIR)/timer.flat
 tests += $(TEST_DIR)/micro-bench.flat
 tests += $(TEST_DIR)/cache.flat
+tests += $(TEST_DIR)/ffa.flat
+tests += $(TEST_DIR)/hello.flat
+tests += $(TEST_DIR)/null.flat
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/Makefile.common b/arm/Makefile.common
index 2e37484..1012519 100644
--- a/arm/Makefile.common
+++ b/arm/Makefile.common
@@ -12,8 +12,7 @@
 tests-common += $(TEST_DIR)/psci.flat
 tests-common += $(TEST_DIR)/sieve.flat
 tests-common += $(TEST_DIR)/pl031.flat
-tests-common += $(TEST_DIR)/hello.flat
-tests-common += $(TEST_DIR)/null.flat
+
 
 
 tests-all = $(tests-common) $(tests)
diff --git a/arm/ffa.c b/arm/ffa.c
new file mode 100644
index 0000000..c03768b
--- /dev/null
+++ b/arm/ffa.c
@@ -0,0 +1,172 @@
+/*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license header.
+ */
+
+#include <libcflat.h>
+
+#include "arm64/call.h"
+#include "arm64/ffa.h"
+
+/* SMCCC reserves ID 0 for the hypervisor itself. */
+const int HYPERVISOR_ID = 0;
+
+/* For now, assume the primary VM has an ID of 1, same as hafnium */
+const ffa_vm_id_t PRIMARY_VM_ID = 1;
+
+static void test_ffa_primary_basics(void)
+{
+        bool pass = true;
+        struct ffa_value ret;
+        ffa_vm_id_t vm_id;
+
+        ret = ffa_id_get();
+        pass &= (ret.func == FFA_SUCCESS_32 && ret.arg2 != HYPERVISOR_ID);
+        vm_id = ret.arg2;
+
+        if (vm_id != PRIMARY_VM_ID) {
+                report_info("VM id %d not a primary VM.", vm_id);
+                goto end;
+        }
+
+        /* Yielding from the primary is a noop. */
+        ret = ffa_yield();
+        pass &= (ret.func == FFA_SUCCESS_32);
+
+end:
+        report(pass, "ffa_primary_basics");
+}
+
+static void test_ffa_basics(void)
+{
+        bool pass = true;
+        struct ffa_value ret;
+
+        ret = ffa_id_get();
+        pass &= (ret.func == FFA_SUCCESS_32 && ret.arg2 != HYPERVISOR_ID);
+
+        /* Primary VM cannot be run by the hypervisor. */
+        ret = ffa_run(PRIMARY_VM_ID, 0);
+        pass &= (ret.func == FFA_ERROR_32);
+
+        /* Cannot run a non-existent VM. */
+        ret = ffa_run(PRIMARY_VM_ID + 1234, 0);
+        pass &= (ret.func == FFA_ERROR_32);
+
+        report(pass, "ffa_basics");
+}
+
+/** Ensures that the FF-A version is reported as expected. */
+static void test_ffa_version(void)
+{
+	const int32_t major_revision = 1;
+	const int32_t major_revision_offset = 16;
+	const int32_t minor_revision = 0;
+	const int32_t current_version =
+		(major_revision << major_revision_offset) | minor_revision;
+
+        bool pass = true;
+        int32_t ret;
+
+	pass &= (ffa_version(current_version) == current_version);
+	pass &= (ffa_version(0x0) == current_version);
+	pass &= (ffa_version(0x1)== current_version);
+	pass &= (ffa_version(0x10003) == current_version);
+	pass &= (ffa_version(0xffff) == current_version);
+	pass &= (ffa_version(0xfffffff) == current_version);
+
+        ret = ffa_version(0x80000000);
+        pass &= (ret == FFA_NOT_SUPPORTED);
+
+        report(pass, "ffa_version");
+}
+
+static void test_ffa_features(void)
+{
+        bool pass = true;
+	struct ffa_value ret;
+
+        /*
+         * TODO: Some features are mandatory some are optional.
+         * Handle appropriately based on that.
+         */
+
+	ret = ffa_features(FFA_ERROR_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_SUCCESS_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_INTERRUPT_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_VERSION_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_FEATURES_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_RX_RELEASE_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_RXTX_MAP_64);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_ID_GET_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MSG_POLL_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MSG_WAIT_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_YIELD_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_RUN_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MSG_SEND_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_DONATE_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_LEND_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_SHARE_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_RETRIEVE_REQ_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_RETRIEVE_RESP_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_RELINQUISH_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MEM_RECLAIM_32);
+	pass &= (ret.func == FFA_SUCCESS_32);
+
+        ret = ffa_features(0);
+	pass &= (ret.func == FFA_ERROR_32 && ret.arg2 == FFA_NOT_SUPPORTED);
+
+	ret = ffa_features(0x84000000);
+	pass &= (ret.func == FFA_ERROR_32 && ret.arg2 == FFA_NOT_SUPPORTED);
+
+        report(pass, "ffa_features");
+}
+
+int main(void)
+{
+        test_ffa_basics();
+        test_ffa_primary_basics();
+        test_ffa_version();
+        test_ffa_features();
+
+        report_pass();
+        return report_summary();
+}
diff --git a/arm/hello.c b/arm/hello.c
index ee2ca95..166adf1 100644
--- a/arm/hello.c
+++ b/arm/hello.c
@@ -2,6 +2,9 @@
 
 int main(void)
 {
+    report_prefix_push("HELLO-WORLD");
+
     printf("Hello, world!\n");
-    return 0;
+    report_pass();
+    return report_summary();
 }
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index 257d658..5919cfc 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -242,14 +242,21 @@
 arch = arm64
 groups = cache
 
+# PSA FF-A Tests
+[ffa]
+file = ffa.flat
+arch = arm64
+groups = nodefault,ffa
+
+
 # Hello world
 [hello]
 file = hello.flat
 arch = arm64
-groups = hello
+groups = nodefault,hello
 
 # null (empty test)
 [null]
 file = null.flat
 arch = arm64
-groups = null
+groups = nodefault,null
diff --git a/lib/arm/asm/psci.h b/lib/arm/asm/psci.h
index 7d1e29a..310a5ec 100644
--- a/lib/arm/asm/psci.h
+++ b/lib/arm/asm/psci.h
@@ -14,6 +14,5 @@
 extern int cpu_psci_cpu_boot(unsigned int cpu);
 extern void cpu_psci_cpu_die(void);
 extern void psci_system_off(void);
-extern void smc_yield(void);
 
 #endif /* _ASMARM_PSCI_H_ */
diff --git a/lib/arm/io.c b/lib/arm/io.c
index fcfbcfa..fac3f74 100644
--- a/lib/arm/io.c
+++ b/lib/arm/io.c
@@ -18,27 +18,7 @@
 #include <asm/io.h>
 
 #include "io.h"
-
-#define HF_DEBUG_LOG            0xbd000000
-
-
-static int hf_call(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3)
-{
-	register uint64_t r0 __asm__("x0") = arg0;
-	register uint64_t r1 __asm__("x1") = arg1;
-	register uint64_t r2 __asm__("x2") = arg2;
-	register uint64_t r3 __asm__("x3") = arg3;
-
-	__asm__ volatile(
-		"hvc #0"
-		: /* Output registers, also used as inputs ('+' constraint). */
-		"+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3)
-		:
-		: /* Clobber registers. */
-		"x4", "x5", "x6", "x7");
-
-	return r0;
-}
+#include "arm64/call.h"
 
 /**
  * Sends a character to the debug log for the VM.
@@ -50,7 +30,6 @@
 	return hf_call(HF_DEBUG_LOG, c, 0, 0);
 }
 
-
 static struct spinlock uart_lock;
 /*
  * Use this guess for the uart base in order to make an attempt at
@@ -59,7 +38,7 @@
  * the address we expect the virtual machine manager to put in
  * its generated device tree.
  */
-#define UART_EARLY_BASE (u8 *)(unsigned long)CONFIG_UART_EARLY_BASE
+#define UART_EARLY_BASE (u8 *)(unsigned long) CONFIG_UART_EARLY_BASE
 static volatile u8 *uart0_base = UART_EARLY_BASE;
 
 static void uart0_init(void)
@@ -76,9 +55,11 @@
 	ret = dt_get_default_console_node();
 	assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
 
-	if (ret == -FDT_ERR_NOTFOUND) {
+	if (ret == -FDT_ERR_NOTFOUND)
+	{
 
-		for (i = 0; i < ARRAY_SIZE(compatible); i++) {
+		for (i = 0; i < ARRAY_SIZE(compatible); i++)
+		{
 			ret = dt_pbus_get_base_compatible(compatible[i], &base);
 			assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
 
@@ -86,23 +67,27 @@
 				break;
 		}
 
-		if (ret) {
+		if (ret)
+		{
 			printf("%s: Compatible uart not found in the device tree, "
-				"aborting...\n", __func__);
+			       "aborting...\n",
+			       __func__);
 			abort();
 		}
-
-	} else {
+	}
+	else
+	{
 		ret = dt_pbus_translate_node(ret, 0, &base);
 		assert(ret == 0);
 	}
 
 	uart0_base = ioremap(base.addr, base.size);
 
-	if (uart0_base != UART_EARLY_BASE) {
+	if (uart0_base != UART_EARLY_BASE)
+	{
 		printf("WARNING: early print support may not work. "
 		       "Found uart at %p, but early base is %p.\n",
-			uart0_base, UART_EARLY_BASE);
+		       uart0_base, UART_EARLY_BASE);
 	}
 }
 
@@ -115,12 +100,12 @@
 void puts(const char *s)
 {
 	spin_lock(&uart_lock);
-	while (*s) {
+	while (*s)
+	{
 		if (!is_secondary_vm)
 			writeb(*s++, uart0_base);
 		else
 			hf_debug_log(*s++);
-
 	}
 	spin_unlock(&uart_lock);
 }
@@ -168,7 +153,8 @@
 	if (!is_secondary_vm)
 		psci_system_off();
 	else
-		smc_yield();
+		ffa_yield();
+
 	halt(code);
 	__builtin_unreachable();
 }
diff --git a/lib/arm/psci.c b/lib/arm/psci.c
index 9107b3f..2156781 100644
--- a/lib/arm/psci.c
+++ b/lib/arm/psci.c
@@ -69,8 +69,3 @@
 	int err = psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
 	printf("CPU%d unable to do system off (error = %d)\n", smp_processor_id(), err);
 }
-
-void smc_yield(void)
-{
-	smc_invoke(FFA_YIELD_32, 0, 0, 0);
-}
diff --git a/lib/arm64/call.c b/lib/arm64/call.c
index 7a5ae25..e6a5cf3 100644
--- a/lib/arm64/call.c
+++ b/lib/arm64/call.c
@@ -1,4 +1,9 @@
 /*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license header.
+ */
+
+/*
  * Copyright 2019 The Hafnium Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,24 +21,6 @@
 
 #include "call.h"
 
-int64_t hf_call(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3)
-{
-	register uint64_t r0 __asm__("x0") = arg0;
-	register uint64_t r1 __asm__("x1") = arg1;
-	register uint64_t r2 __asm__("x2") = arg2;
-	register uint64_t r3 __asm__("x3") = arg3;
-
-	__asm__ volatile(
-		"hvc #0"
-		: /* Output registers, also used as inputs ('+' constraint). */
-		"+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3)
-		:
-		: /* Clobber registers. */
-		"x4", "x5", "x6", "x7");
-
-	return r0;
-}
-
 struct ffa_value ffa_call(struct ffa_value args)
 {
 	register uint64_t r0 __asm__("x0") = args.func;
@@ -60,3 +47,21 @@
 				  .arg6 = r6,
 				  .arg7 = r7};
 }
+
+int64_t hf_call(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+	register uint64_t r0 __asm__("x0") = arg0;
+	register uint64_t r1 __asm__("x1") = arg1;
+	register uint64_t r2 __asm__("x2") = arg2;
+	register uint64_t r3 __asm__("x3") = arg3;
+
+	__asm__ volatile(
+		"hvc #0"
+		: /* Output registers, also used as inputs ('+' constraint). */
+		"+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3)
+		:
+		: /* Clobber registers. */
+		"x4", "x5", "x6", "x7");
+
+	return r0;
+}
\ No newline at end of file
diff --git a/lib/arm64/call.h b/lib/arm64/call.h
index e4c9950..e1a67f4 100644
--- a/lib/arm64/call.h
+++ b/lib/arm64/call.h
@@ -1,4 +1,9 @@
 /*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license.
+ */
+
+/*
  * Copyright 2018 The Hafnium Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,10 +27,14 @@
  * This function must be implemented to trigger the architecture-specific
  * mechanism to call to the hypervisor.
  */
-int64_t hf_call(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3);
 struct ffa_value ffa_call(struct ffa_value args);
 
 /**
+ * Hafnium-specific hypervisor calling function.
+ */
+int64_t hf_call(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3);
+
+/**
  * Returns the VM's own ID.
  */
 static inline struct ffa_value ffa_id_get(void)
diff --git a/lib/arm64/ffa.c b/lib/arm64/ffa.c
index 3746ca0..3ca17b2 100644
--- a/lib/arm64/ffa.c
+++ b/lib/arm64/ffa.c
@@ -1,4 +1,9 @@
 /*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license header.
+ */
+
+/*
  * Copyright 2019 The Hafnium Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/lib/arm64/ffa.h b/lib/arm64/ffa.h
index ec17356..42f53be 100644
--- a/lib/arm64/ffa.h
+++ b/lib/arm64/ffa.h
@@ -69,6 +69,9 @@
 #define FFA_RETRY              INT32_C(-7)
 #define FFA_ABORTED            INT32_C(-8)
 
+/* Hafnium-specific function identifiers. */
+#define HF_DEBUG_LOG 0xbd000000
+
 /* clang-format on */
 
 /* FF-A function specific constants. */
@@ -84,12 +87,12 @@
 
 /**
  * For use where the FF-A specification refers explicitly to '4K pages'. Not to
- * be confused with PAGE_SIZE, which is the translation granule Hafnium is
- * configured to use.
+ * be confused with PAGE_SIZE, which is the translation granule the hypervisor
+ * is configured to use.
  */
 #define FFA_PAGE_SIZE 4096
 
-/* Size of an FFA mailbox. */
+/* Size of an FF-A mailbox. */
 #define FFA_MAILBOX_SIZE FFA_PAGE_SIZE
 
 /* The maximum length possible for a single message. */