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. */