Msg tests and fixes to for running without hftest as a driver

- Fixed (hacked) some assumptions on memory size that interfered
  with running as a primary VM when there are secondaries
- Added some basic indirect message tests, all pass
diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index e176974..18a8f30 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -20,8 +20,10 @@
 cflatobjs += lib/arm64/processor.o
 cflatobjs += lib/arm64/spinlock.o
 cflatobjs += lib/arm64/gic-v3-its.o lib/arm64/gic-v3-its-cmd.o
+cflatobjs += lib/arm64/ffa_utils.o
 cflatobjs += lib/arm64/ffa.o
 cflatobjs += lib/arm64/call.o
+cflatobjs += lib/arm64/ffa_mailbox.o
 
 
 OBJDIRS += lib/arm64
@@ -31,6 +33,7 @@
 tests += $(TEST_DIR)/micro-bench.flat
 tests += $(TEST_DIR)/cache.flat
 tests += $(TEST_DIR)/ffa.flat
+tests += $(TEST_DIR)/ffa_mailbox.flat
 tests += $(TEST_DIR)/hello.flat
 tests += $(TEST_DIR)/null.flat
 
diff --git a/arm/ffa.c b/arm/ffa.c
index c03768b..30c92e6 100644
--- a/arm/ffa.c
+++ b/arm/ffa.c
@@ -3,16 +3,7 @@
  * 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;
+#include "arm64/ffa_utils.h"
 
 static void test_ffa_primary_basics(void)
 {
diff --git a/arm/ffa_mailbox.c b/arm/ffa_mailbox.c
new file mode 100644
index 0000000..82158f0
--- /dev/null
+++ b/arm/ffa_mailbox.c
@@ -0,0 +1,346 @@
+/*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license header.
+ */
+
+#include "arm64/ffa_utils.h"
+
+
+static struct mailbox_buffers mb;
+
+#ifndef be16toh
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#define be16toh(v) __builtin_bswap16(v)
+#define be32toh(v) __builtin_bswap32(v)
+#define be64toh(v) __builtin_bswap64(v)
+
+#define htobe16(v) __builtin_bswap16(v)
+#define htobe32(v) __builtin_bswap32(v)
+#define htobe64(v) __builtin_bswap64(v)
+
+#define le16toh(v) (v)
+#define le32toh(v) (v)
+#define le64toh(v) (v)
+
+#define htole16(v) (v)
+#define htole32(v) (v)
+#define htole64(v) (v)
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#define be16toh(v) (v)
+#define be32toh(v) (v)
+#define be64toh(v) (v)
+
+#define htobe16(v) (v)
+#define htobe32(v) (v)
+#define htobe64(v) (v)
+
+#define le16toh(v) __builtin_bswap16(v)
+#define le32toh(v) __builtin_bswap32(v)
+#define le64toh(v) __builtin_bswap64(v)
+
+#define htole16(v) __builtin_bswap16(v)
+#define htole32(v) __builtin_bswap32(v)
+#define htole64(v) __builtin_bswap64(v)
+
+#else
+
+/*
+ * __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ &&
+ * __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
+ */
+
+#error "Unsupported byte order"
+
+#endif
+#endif
+
+
+/**
+ * Reverses the order of the elements in the given array.
+ */
+static void reverse(char *s, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len / 2; i++) {
+		char t = s[i];
+		s[i] = s[len - 1 - i];
+		s[len - 1 - i] = t;
+	}
+}
+
+/**
+ * Finds the next lexicographic permutation of the given array, if there is one.
+ */
+static void next_permutation(char *s, size_t len)
+{
+	size_t i;
+	size_t j;
+
+	for (i = len - 2; i < len; i--) {
+		const char t = s[i];
+		if (t >= s[i + 1]) {
+			continue;
+		}
+
+		for (j = len - 1; t >= s[j]; j--) {
+		}
+
+		s[i] = s[j];
+		s[j] = t;
+		reverse(s + i + 1, len - i - 1);
+		return;
+	}
+}
+
+static void service_echo(int count)
+{
+        report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id());
+
+	/* Loop, echo messages back to the sender. */
+	while (count--) {
+		struct ffa_value ret = ffa_msg_wait();
+		ffa_vm_id_t target_vm_id = ffa_msg_send_receiver(ret);
+		ffa_vm_id_t source_vm_id = ffa_msg_send_sender(ret);
+		void *send_buf = mb.send;
+		void *recv_buf = mb.recv;
+
+		ASSERT_EQ(ret.func, FFA_MSG_SEND_32);
+		memcpy_s(send_buf, FFA_MSG_PAYLOAD_MAX, recv_buf,
+			 ffa_msg_send_size(ret));
+
+		EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+		ffa_msg_send(target_vm_id, source_vm_id, ffa_msg_send_size(ret),
+			     0);
+	}
+
+        report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id());
+}
+
+static void service_relay(int count)
+{
+        report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id());
+
+	/*
+	 * Loop, forward messages to the next VM.
+	 *
+	 * The first 32-bits of the message are the little-endian 32-bit ID of
+	 * the VM to forward the message to. This ID will be dropped from the
+	 * message so multiple IDs can be places at the start of the message.
+	 */
+	while (count--) {
+		ffa_vm_id_t *chain;
+		ffa_vm_id_t next_vm_id;
+		void *next_message;
+		uint32_t next_message_size;
+
+		/* Receive the message to relay. */
+		struct ffa_value ret = ffa_msg_wait();
+		ASSERT_EQ(ret.func, FFA_MSG_SEND_32);
+
+		/* Prepare to relay the message. */
+		void *send_buf = mb.send;
+		void *recv_buf = mb.recv;
+		ASSERT_GE(ffa_msg_send_size(ret), sizeof(ffa_vm_id_t));
+
+		chain = (ffa_vm_id_t *)recv_buf;
+		next_vm_id = le16toh(*chain);
+		next_message = chain + 1;
+		next_message_size =
+			ffa_msg_send_size(ret) - sizeof(ffa_vm_id_t);
+
+		/* Send the message to the next stage. */
+		memcpy_s(send_buf, FFA_MSG_PAYLOAD_MAX, next_message,
+			 next_message_size);
+
+		EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+		ffa_msg_send(get_vm_id(), next_vm_id, next_message_size, 0);
+	}
+
+	report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id());
+}
+
+static void service_clear_empty(void)
+{
+        report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id());
+        report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id());
+}
+
+static void echo(void)
+{
+	const char message[] = "Echo this back to me!";
+	struct ffa_value run_res;
+
+	EXPECT(is_primary_vm());
+
+	run_res = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32);
+	EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE);
+
+	/* Set the message, echo it and check it didn't change. */
+	memcpy_s(mb.send, FFA_MSG_PAYLOAD_MAX, message, sizeof(message));
+	EXPECT_EQ(
+		ffa_msg_send(get_vm_id(), SERVICE_VM1, sizeof(message), 0)
+			.func,
+		FFA_SUCCESS_32);
+	run_res = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_SEND_32);
+	EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message));
+	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	report(true, "%s", __func__);
+}
+
+static void repeated_echo(int count)
+{
+	char message[] = "Echo this back to me!";
+	struct ffa_value run_res;
+
+	while (count--) {
+		/* Run secondary until it reaches the wait for messages. */
+		run_res = ffa_run(SERVICE_VM1, 0);
+		EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32);
+		EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE);
+
+		/* Set the message, echo it and check it didn't change. */
+		next_permutation(message, sizeof(message) - 1);
+		memcpy_s(mb.send, FFA_MSG_PAYLOAD_MAX, message,
+			 sizeof(message));
+		EXPECT_EQ(ffa_msg_send(get_vm_id(), SERVICE_VM1,
+				       sizeof(message), 0)
+				  .func,
+			  FFA_SUCCESS_32);
+		run_res = ffa_run(SERVICE_VM1, 0);
+		EXPECT_EQ(run_res.func, FFA_MSG_SEND_32);
+		EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message));
+		EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+		EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+	}
+
+	report(true, "%s", __func__);
+}
+
+/**
+ * Send a message to relay_a which will forward it to relay_b where it will be
+ * sent back here.
+ */
+static void relay(void)
+{
+	const char message[] = "Send this round the relay!";
+	struct ffa_value run_res;
+
+	run_res = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32);
+	EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE);
+	run_res = ffa_run(SERVICE_VM2, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32);
+	EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE);
+
+	/*
+	 * Build the message chain so the message is sent from here to
+	 * SERVICE_VM1, then to SERVICE_VM2 and finally back to here.
+	 */
+	{
+		ffa_vm_id_t *chain = (ffa_vm_id_t *)mb.send;
+		*chain++ = htole32(SERVICE_VM2);
+		*chain++ = htole32(get_vm_id());
+		memcpy_s(chain, FFA_MSG_PAYLOAD_MAX - (2 * sizeof(ffa_vm_id_t)),
+			 message, sizeof(message));
+
+		EXPECT_EQ(
+			ffa_msg_send(
+				get_vm_id(), SERVICE_VM1,
+				sizeof(message) + (2 * sizeof(ffa_vm_id_t)), 0)
+				.func,
+			FFA_SUCCESS_32);
+	}
+
+	/* Let SERVICE_VM1 forward the message. */
+	run_res = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_SEND_32);
+	EXPECT_EQ(ffa_msg_send_receiver(run_res), SERVICE_VM2);
+	EXPECT_EQ(ffa_msg_send_size(run_res), 0);
+
+	/* Let SERVICE_VM2 forward the message. */
+	run_res = ffa_run(SERVICE_VM2, 0);
+	EXPECT_EQ(run_res.func, FFA_MSG_SEND_32);
+
+	/* Ensure the message is intact. */
+	EXPECT_EQ(ffa_msg_send_receiver(run_res), get_vm_id());
+	EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message));
+	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	report(true, "%s", __func__);
+}
+
+
+/**
+ * Clearing an empty mailbox is an error.
+ */
+static void clear_empty(void)
+{
+	EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED);
+	EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED);
+	EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED);
+	report(true, "%s", __func__);
+}
+
+static void test_echo(void)
+{
+	if (is_primary_vm()) {
+		echo();
+	} else {
+		service_echo(1);
+	}
+}
+
+static void test_repeated_echo(void)
+{
+	int count = 100;
+
+	if (is_primary_vm()) {
+		repeated_echo(count);
+	} else {
+		service_echo(count);
+	}
+}
+
+static void test_relay(void)
+{
+	if (is_primary_vm()) {
+		relay();
+	} else {
+		service_relay(1);
+	}
+}
+
+static void test_clear_empty(void)
+{
+	if (is_primary_vm()) {
+		clear_empty();
+	} else {
+		service_clear_empty();
+	}
+}
+
+int main(void)
+{
+	mb = set_up_mailbox();
+
+	if (0) {
+	test_echo();
+	test_repeated_echo();
+	test_clear_empty();}
+	test_relay();
+
+	if (is_primary_vm()) {
+		return report_summary();
+	} else {
+		return 0;
+	}
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index 5919cfc..61ed63b 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -248,6 +248,10 @@
 arch = arm64
 groups = nodefault,ffa
 
+[ffa_mailbox]
+file = ffa_mailbox.flat
+arch = arm64
+groups = nodefault,ffa
 
 # Hello world
 [hello]
diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
index edf4e86..c8afb24 100644
--- a/lib/arm/asm/setup.h
+++ b/lib/arm/asm/setup.h
@@ -12,7 +12,6 @@
 #define NR_CPUS			511
 extern u64 cpus[NR_CPUS];	/* per-cpu IDs (MPIDRs) */
 extern int nr_cpus;
-extern bool is_secondary_vm;	/* True if running as an FFA secondary VM */
 
 #define MR_F_PRIMARY		(1U << 0)
 #define MR_F_IO			(1U << 1)
diff --git a/lib/arm/io.c b/lib/arm/io.c
index fac3f74..04acb43 100644
--- a/lib/arm/io.c
+++ b/lib/arm/io.c
@@ -18,7 +18,7 @@
 #include <asm/io.h>
 
 #include "io.h"
-#include "arm64/call.h"
+#include "arm64/ffa_utils.h"
 
 /**
  * Sends a character to the debug log for the VM.
@@ -102,7 +102,7 @@
 	spin_lock(&uart_lock);
 	while (*s)
 	{
-		if (!is_secondary_vm)
+		if (is_primary_vm())
 			writeb(*s++, uart0_base);
 		else
 			hf_debug_log(*s++);
@@ -150,7 +150,7 @@
 {
 	chr_testdev_exit(code);
 
-	if (!is_secondary_vm)
+	if (is_primary_vm())
 		psci_system_off();
 	else
 		ffa_yield();
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
index ee9f8d0..6adcae8 100644
--- a/lib/arm/setup.c
+++ b/lib/arm/setup.c
@@ -23,6 +23,7 @@
 #include <asm/processor.h>
 #include <asm/smp.h>
 
+#include "arm64/ffa_utils.h"
 #include "io.h"
 
 #define NR_INITIAL_MEM_REGIONS 16
@@ -34,7 +35,6 @@
 
 u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 };
 int nr_cpus;
-bool is_secondary_vm;
 
 static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1];
 struct mem_region *mem_regions = __initial_mem_regions;
@@ -89,13 +89,31 @@
 	return MR_F_UNKNOWN;
 }
 
+static phys_addr_t get_highest_reserved_addr(const void *fdt)
+{
+        int n;
+        u64 base, size;
+	phys_addr_t highest_reserved = 0;
+
+        /* Process header /memreserve/ fields */
+        for (n = 0; ; n++) {
+                fdt_get_mem_rsv(fdt, n, &base, &size);
+		if (!size)
+			break;
+		if (base > highest_reserved)
+			highest_reserved = base;
+        }
+
+	return highest_reserved;
+}
+
 static void mem_init(phys_addr_t freemem_start)
 {
 	struct dt_pbus_reg regs[NR_INITIAL_MEM_REGIONS];
 	struct mem_region primary, mem = {
 		.start = (phys_addr_t)-1,
 	};
-	phys_addr_t base, top;
+	phys_addr_t base, top, highest_reserved;
 	int nr_regs, nr_io = 0, i;
 
 	/*
@@ -141,6 +159,16 @@
 	assert(primary.end != 0);
 	assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK));
 
+	if (is_primary_vm()) {
+		highest_reserved = get_highest_reserved_addr(dt_fdt());
+
+		if (highest_reserved < mem.end)
+			mem.end = highest_reserved;
+
+		if (highest_reserved < primary.end)
+			primary.end = highest_reserved;
+	}
+
 	__phys_offset = primary.start;	/* PHYS_OFFSET */
 	__phys_end = primary.end;	/* PHYS_END */
 
@@ -205,16 +233,13 @@
 	mem_init(PAGE_ALIGN((unsigned long)freemem));
 	cpu_init();
 
-	/* Check whether this is running as a secondary VM. */
-	is_secondary_vm = (fdt_node_offset_by_compatible(fdt, -1,
-		"linux,dummy-virt") >= 0);
 
 
 	/* cpu_init must be called before thread_info_init */
 	thread_info_init(current_thread_info(), 0);
 
 	/* mem_init must be called before io_init */
-	if (!is_secondary_vm)
+	if (is_primary_vm())
 		io_init();
 
 	/* finish setup */
diff --git a/lib/arm64/call.c b/lib/arm64/call.c
index e6a5cf3..2274d00 100644
--- a/lib/arm64/call.c
+++ b/lib/arm64/call.c
@@ -64,4 +64,5 @@
 		"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 e1a67f4..d5cbd80 100644
--- a/lib/arm64/call.h
+++ b/lib/arm64/call.h
@@ -257,3 +257,4 @@
 	return ffa_call((struct ffa_value){.func = FFA_FEATURES_32,
 					   .arg1 = function_id});
 }
+
diff --git a/lib/arm64/ffa_mailbox.c b/lib/arm64/ffa_mailbox.c
new file mode 100644
index 0000000..d5322a3
--- /dev/null
+++ b/lib/arm64/ffa_mailbox.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2018 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "ffa_mailbox.h"
+
+#include "call.h"
+
+#include "ffa_utils.h"
+
+static uint8_t send_page[FFA_PAGE_SIZE] __attribute__((aligned(FFA_PAGE_SIZE)));
+static uint8_t recv_page[FFA_PAGE_SIZE] __attribute__((aligned(FFA_PAGE_SIZE)));
+// static_assert(sizeof(send_page) == FFA_PAGE_SIZE, "Send page is not a page.");
+// static_assert(sizeof(recv_page) == FFA_PAGE_SIZE, "Recv page is not a page.");
+
+static uint64_t send_page_addr = (uint64_t)send_page;
+static uint64_t recv_page_addr = (uint64_t)recv_page;
+
+struct mailbox_buffers set_up_mailbox(void)
+{
+	ASSERT_EQ(ffa_rxtx_map(send_page_addr, recv_page_addr).func,
+		  FFA_SUCCESS_32);
+	return (struct mailbox_buffers){
+		.send = send_page,
+		.recv = recv_page,
+	};
+}
+
+/*
+ * Helper function to send memory to a VM then send a message with the retrieve
+ * request it needs to retrieve it.
+ */
+ffa_memory_handle_t send_memory_and_retrieve_request(
+	uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
+	ffa_vm_id_t recipient,
+	struct ffa_memory_region_constituent constituents[],
+	uint32_t constituent_count, ffa_memory_region_flags_t flags,
+	enum ffa_data_access send_data_access,
+	enum ffa_data_access retrieve_data_access,
+	enum ffa_instruction_access send_instruction_access,
+	enum ffa_instruction_access retrieve_instruction_access)
+{
+	uint32_t total_length;
+	uint32_t fragment_length;
+	uint32_t msg_size;
+	struct ffa_value ret;
+	const ffa_memory_handle_t INVALID_FRAGMENT_HANDLE = 0xffffffffffffffff;
+	ffa_memory_handle_t fragment_handle = INVALID_FRAGMENT_HANDLE;
+	ffa_memory_handle_t handle;
+	uint32_t remaining_constituent_count;
+	uint32_t sent_length;
+
+	/* Send the first fragment of the memory. */
+	remaining_constituent_count = ffa_memory_region_init(
+		tx_buffer, FFA_MAILBOX_SIZE, sender, recipient, constituents,
+		constituent_count, 0, flags, send_data_access,
+		send_instruction_access, FFA_MEMORY_NORMAL_MEM,
+		FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE,
+		&total_length, &fragment_length);
+	if (remaining_constituent_count == 0) {
+		EXPECT_EQ(total_length, fragment_length);
+	}
+	switch (share_func) {
+	case FFA_MEM_DONATE_32:
+		ret = ffa_mem_donate(total_length, fragment_length);
+		break;
+	case FFA_MEM_LEND_32:
+		ret = ffa_mem_lend(total_length, fragment_length);
+		break;
+	case FFA_MEM_SHARE_32:
+		ret = ffa_mem_share(total_length, fragment_length);
+		break;
+	default:
+		//FAIL("Invalid share_func %#x.\n", share_func);
+		/* Never reached, but needed to keep clang-analyser happy. */
+		return 0;
+	}
+	sent_length = fragment_length;
+
+	/* Send the remaining fragments. */
+	while (remaining_constituent_count != 0) {
+		//dlog_verbose("%d constituents left to send.\n",
+		//	     remaining_constituent_count);
+		EXPECT_EQ(ret.func, FFA_MEM_FRAG_RX_32);
+		if (fragment_handle == INVALID_FRAGMENT_HANDLE) {
+			fragment_handle = ffa_frag_handle(ret);
+		} else {
+			EXPECT_EQ(ffa_frag_handle(ret), fragment_handle);
+		}
+		EXPECT_EQ(ret.arg3, sent_length);
+		/* Sender MBZ at virtual instance. */
+		EXPECT_EQ(ffa_frag_sender(ret), 0);
+
+		remaining_constituent_count = ffa_memory_fragment_init(
+			tx_buffer, FFA_MAILBOX_SIZE,
+			constituents + constituent_count -
+				remaining_constituent_count,
+			remaining_constituent_count, &fragment_length);
+
+		ret = ffa_mem_frag_tx(fragment_handle, fragment_length);
+		sent_length += fragment_length;
+	}
+
+	EXPECT_EQ(sent_length, total_length);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	handle = ffa_mem_success_handle(ret);
+	EXPECT_EQ(handle & FFA_MEMORY_HANDLE_ALLOCATOR_MASK,
+		  FFA_MEMORY_HANDLE_ALLOCATOR_HYPERVISOR);
+	if (fragment_handle != INVALID_FRAGMENT_HANDLE) {
+		EXPECT_EQ(handle, fragment_handle);
+	}
+
+	/*
+	 * Send the appropriate retrieve request to the VM so that it can use it
+	 * to retrieve the memory.
+	 */
+	msg_size = ffa_memory_retrieve_request_init(
+		tx_buffer, handle, sender, recipient, 0, 0,
+		retrieve_data_access, retrieve_instruction_access,
+		FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
+		FFA_MEMORY_OUTER_SHAREABLE);
+	EXPECT_LE(msg_size, FFA_MAILBOX_SIZE);
+	EXPECT_EQ(ffa_msg_send(sender, recipient, msg_size, 0).func,
+		  FFA_SUCCESS_32);
+
+	return handle;
+}
+
+/*
+ * Helper function to send memory to a VM then send a message with the retrieve
+ * request it needs to retrieve it, forcing the request to be made in at least
+ * two fragments even if it could fit in one.
+ */
+ffa_memory_handle_t send_memory_and_retrieve_request_force_fragmented(
+	uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
+	ffa_vm_id_t recipient,
+	struct ffa_memory_region_constituent constituents[],
+	uint32_t constituent_count, ffa_memory_region_flags_t flags,
+	enum ffa_data_access send_data_access,
+	enum ffa_data_access retrieve_data_access,
+	enum ffa_instruction_access send_instruction_access,
+	enum ffa_instruction_access retrieve_instruction_access)
+{
+	uint32_t total_length;
+	uint32_t fragment_length;
+	uint32_t msg_size;
+	uint32_t remaining_constituent_count;
+	struct ffa_value ret;
+	ffa_memory_handle_t handle;
+
+	/* Send everything except the last constituent in the first fragment. */
+	remaining_constituent_count = ffa_memory_region_init(
+		tx_buffer, FFA_MAILBOX_SIZE, sender, recipient, constituents,
+		constituent_count, 0, flags, send_data_access,
+		send_instruction_access, FFA_MEMORY_NORMAL_MEM,
+		FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE,
+		&total_length, &fragment_length);
+	EXPECT_EQ(remaining_constituent_count, 0);
+	EXPECT_EQ(total_length, fragment_length);
+	/* Don't include the last constituent in the first fragment. */
+	fragment_length -= sizeof(struct ffa_memory_region_constituent);
+	switch (share_func) {
+	case FFA_MEM_DONATE_32:
+		ret = ffa_mem_donate(total_length, fragment_length);
+		break;
+	case FFA_MEM_LEND_32:
+		ret = ffa_mem_lend(total_length, fragment_length);
+		break;
+	case FFA_MEM_SHARE_32:
+		ret = ffa_mem_share(total_length, fragment_length);
+		break;
+	default:
+		//FAIL("Invalid share_func %#x.\n", share_func);
+		/* Never reached, but needed to keep clang-analyser happy. */
+		return 0;
+	}
+	EXPECT_EQ(ret.func, FFA_MEM_FRAG_RX_32);
+	EXPECT_EQ(ret.arg3, fragment_length);
+	/* Sender MBZ at virtual instance. */
+	EXPECT_EQ(ffa_frag_sender(ret), 0);
+
+	handle = ffa_frag_handle(ret);
+
+	/* Send the last constituent in a separate fragment. */
+	remaining_constituent_count = ffa_memory_fragment_init(
+		tx_buffer, FFA_MAILBOX_SIZE,
+		&constituents[constituent_count - 1], 1, &fragment_length);
+	EXPECT_EQ(remaining_constituent_count, 0);
+	ret = ffa_mem_frag_tx(handle, fragment_length);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+	EXPECT_EQ(ffa_mem_success_handle(ret), handle);
+
+	/*
+	 * Send the appropriate retrieve request to the VM so that it can use it
+	 * to retrieve the memory.
+	 */
+	msg_size = ffa_memory_retrieve_request_init(
+		tx_buffer, handle, sender, recipient, 0, 0,
+		retrieve_data_access, retrieve_instruction_access,
+		FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
+		FFA_MEMORY_OUTER_SHAREABLE);
+	EXPECT_LE(msg_size, FFA_MAILBOX_SIZE);
+	EXPECT_EQ(ffa_msg_send(sender, recipient, msg_size, 0).func,
+		  FFA_SUCCESS_32);
+
+	return handle;
+}
+
+/*
+ * Use the retrieve request from the receive buffer to retrieve a memory region
+ * which has been sent to us. Copies all the fragments into the provided buffer
+ * if any, and checks that the total length of all fragments is no more than
+ * `memory_region_max_size`. Returns the sender, and the handle via a return
+ * parameter.
+ */
+ffa_vm_id_t retrieve_memory_from_message(
+	void *recv_buf, void *send_buf, struct ffa_value msg_ret,
+	ffa_memory_handle_t *handle,
+	struct ffa_memory_region *memory_region_ret,
+	size_t memory_region_max_size)
+{
+	uint32_t msg_size;
+	struct ffa_value ret;
+	struct ffa_memory_region *memory_region;
+	ffa_vm_id_t sender;
+	struct ffa_memory_region *retrieve_request;
+	ffa_memory_handle_t handle_;
+	uint32_t fragment_length;
+	uint32_t total_length;
+	uint32_t fragment_offset;
+
+	EXPECT_EQ(msg_ret.func, FFA_MSG_SEND_32);
+	msg_size = ffa_msg_send_size(msg_ret);
+	sender = ffa_msg_send_sender(msg_ret);
+
+	retrieve_request = (struct ffa_memory_region *)recv_buf;
+	handle_ = retrieve_request->handle;
+	if (handle != NULL) {
+		*handle = handle_;
+	}
+	memcpy_s(send_buf, FFA_MAILBOX_SIZE, recv_buf, msg_size);
+	ffa_rx_release();
+	ret = ffa_mem_retrieve_req(msg_size, msg_size);
+	EXPECT_EQ(ret.func, FFA_MEM_RETRIEVE_RESP_32);
+	total_length = ret.arg1;
+	fragment_length = ret.arg2;
+	EXPECT_GE(fragment_length,
+		  sizeof(struct ffa_memory_region) +
+			  sizeof(struct ffa_memory_access) +
+			  sizeof(struct ffa_composite_memory_region));
+	EXPECT_LE(fragment_length, FFA_MAILBOX_SIZE);
+	EXPECT_LE(fragment_length, total_length);
+	memory_region = (struct ffa_memory_region *)recv_buf;
+	EXPECT_EQ(memory_region->receiver_count, 1);
+	EXPECT_EQ(memory_region->receivers[0].receiver_permissions.receiver,
+		  ffa_id_get().arg2);
+
+	/* Copy into the return buffer. */
+	if (memory_region_ret != NULL) {
+		memcpy_s(memory_region_ret, memory_region_max_size,
+			 memory_region, fragment_length);
+	}
+
+	/*
+	 * Release the RX buffer now that we have read everything we need from
+	 * it.
+	 */
+	memory_region = NULL;
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	/* Retrieve the remaining fragments. */
+	fragment_offset = fragment_length;
+	while (fragment_offset < total_length) {
+		ret = ffa_mem_frag_rx(handle_, fragment_offset);
+		EXPECT_EQ(ret.func, FFA_MEM_FRAG_TX_32);
+		EXPECT_EQ(ffa_frag_handle(ret), handle_);
+		/* Sender MBZ at virtual instance. */
+		EXPECT_EQ(ffa_frag_sender(ret), 0);
+		fragment_length = ret.arg3;
+		EXPECT_GT(fragment_length, 0);
+		ASSERT_LE(fragment_offset + fragment_length,
+			  memory_region_max_size);
+		if (memory_region_ret != NULL) {
+			memcpy_s((uint8_t *)memory_region_ret + fragment_offset,
+				 memory_region_max_size - fragment_offset,
+				 recv_buf, fragment_length);
+		}
+		fragment_offset += fragment_length;
+		EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+	}
+	EXPECT_EQ(fragment_offset, total_length);
+
+	return sender;
+}
+
+/*
+ * Use the retrieve request from the receive buffer to retrieve a memory region
+ * which has been sent to us, expecting it to fail with the given error code.
+ * Returns the sender.
+ */
+ffa_vm_id_t retrieve_memory_from_message_expect_fail(void *recv_buf,
+						     void *send_buf,
+						     struct ffa_value msg_ret,
+						     int32_t expected_error)
+{
+	uint32_t msg_size;
+	struct ffa_value ret;
+	ffa_vm_id_t sender;
+
+	EXPECT_EQ(msg_ret.func, FFA_MSG_SEND_32);
+	msg_size = ffa_msg_send_size(msg_ret);
+	sender = ffa_msg_send_sender(msg_ret);
+
+	memcpy_s(send_buf, FFA_MAILBOX_SIZE, recv_buf, msg_size);
+	ffa_rx_release();
+	ret = ffa_mem_retrieve_req(msg_size, msg_size);
+	EXPECT_FFA_ERROR(ret, expected_error);
+
+	return sender;
+}
diff --git a/lib/arm64/ffa_mailbox.h b/lib/arm64/ffa_mailbox.h
new file mode 100644
index 0000000..8261aed
--- /dev/null
+++ b/lib/arm64/ffa_mailbox.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#pragma once
+
+#include "ffa.h"
+#include "call.h"
+
+
+struct mailbox_buffers {
+	void *send;
+	void *recv;
+};
+
+struct mailbox_buffers set_up_mailbox(void);
+ffa_memory_handle_t send_memory_and_retrieve_request(
+	uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
+	ffa_vm_id_t recipient,
+	struct ffa_memory_region_constituent constituents[],
+	uint32_t constituent_count, ffa_memory_region_flags_t flags,
+	enum ffa_data_access send_data_access,
+	enum ffa_data_access retrieve_data_access,
+	enum ffa_instruction_access send_instruction_access,
+	enum ffa_instruction_access retrieve_instruction_access);
+ffa_memory_handle_t send_memory_and_retrieve_request_force_fragmented(
+	uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
+	ffa_vm_id_t recipient,
+	struct ffa_memory_region_constituent constituents[],
+	uint32_t constituent_count, ffa_memory_region_flags_t flags,
+	enum ffa_data_access send_data_access,
+	enum ffa_data_access retrieve_data_access,
+	enum ffa_instruction_access send_instruction_access,
+	enum ffa_instruction_access retrieve_instruction_access);
+ffa_vm_id_t retrieve_memory_from_message(
+	void *recv_buf, void *send_buf, struct ffa_value msg_ret,
+	ffa_memory_handle_t *handle,
+	struct ffa_memory_region *memory_region_ret,
+	size_t memory_region_max_size);
+ffa_vm_id_t retrieve_memory_from_message_expect_fail(void *recv_buf,
+						     void *send_buf,
+						     struct ffa_value msg_ret,
+						     int32_t expected_error);
diff --git a/lib/arm64/ffa_utils.c b/lib/arm64/ffa_utils.c
new file mode 100644
index 0000000..5ad75d5
--- /dev/null
+++ b/lib/arm64/ffa_utils.c
@@ -0,0 +1,16 @@
+#include "ffa_utils.h"
+
+ffa_vm_id_t get_vm_id(void)
+{
+        struct ffa_value ret;
+
+        ret = ffa_id_get();
+        ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+
+        return ret.arg2;
+}
+
+bool is_primary_vm(void)
+{
+	return get_vm_id() == PRIMARY_VM_ID;
+}
\ No newline at end of file
diff --git a/lib/arm64/ffa_utils.h b/lib/arm64/ffa_utils.h
new file mode 100644
index 0000000..0339a41
--- /dev/null
+++ b/lib/arm64/ffa_utils.h
@@ -0,0 +1,57 @@
+/*
+ * Adapted from Hafnium.
+ * TODO: Figure out the right way of specifying the license header.
+ */
+
+#pragma once
+
+#include <libcflat.h>
+
+#include "call.h"
+#include "ffa.h"
+#include "ffa_mailbox.h"
+
+#define memcpy_s(dest, destmax, src, count) memcpy(dest, src, count)
+
+#define TEST_ASSERT(a, b, op) \
+do { \
+	if (!((a) op (b))) { \
+		report_info("%s:%d: Assertion failed: %s %s %s", \
+		       __FILE__, __LINE__, #a, #op, #b); \
+		dump_stack(); \
+                assert(0); \
+	} \
+} while (0)
+
+#define EXPECT(a) TEST_ASSERT(a, true, ==)
+#define EXPECT_EQ(a, b) TEST_ASSERT(a, b, ==)
+#define EXPECT_NE(a, b) TEST_ASSERT(a, b, !=)
+#define EXPECT_GT(a, b) TEST_ASSERT(a, b, >)
+#define EXPECT_GE(a, b) TEST_ASSERT(a, b, >=)
+#define EXPECT_LE(a, b) TEST_ASSERT(a, b, <=)
+#define ASSERT_LE(a, b) TEST_ASSERT(a, b, <=)
+#define ASSERT_EQ(a, b) TEST_ASSERT(a, b, ==)
+#define ASSERT_NE(a, b) TEST_ASSERT(a, b, !=)
+#define ASSERT_GE(a, b) TEST_ASSERT(a, b, >=)
+
+#define EXPECT_FFA_ERROR(value, ffa_error)       \
+	do {                                     \
+		struct ffa_value v = (value);    \
+		EXPECT_EQ(v.func, FFA_ERROR_32); \
+		EXPECT_EQ(v.arg2, (ffa_error));  \
+	} while (0)
+
+
+/* SMCCC reserves ID 0 for the hypervisor itself. */
+#define HYPERVISOR_ID 0
+
+/* For now, assume the primary VM has an ID of 1, same as hafnium */
+#define PRIMARY_VM_ID 1
+
+#define SERVICE_VM1 (PRIMARY_VM_ID + 1)
+
+#define SERVICE_VM2 (PRIMARY_VM_ID + 2)
+
+ffa_vm_id_t get_vm_id(void);
+
+bool is_primary_vm(void);
\ No newline at end of file