SPCI: Introduce SPCI_MSG_SEND.

Morph the vmapi service HF_MAILBOX_SEND onto SPCI_MSG_SEND.
The current SPCI_MSG_SEND only allows for implementation defined
messages to be exchanged between VMs.

The new SPCI service returns SPCI_SUCCESS if message is delivered or one
of the following error codes:
        SPCI_INVALID_PARAMETER: one of the parameters in the header does
        not conform;
        SPCI_BUSY: the mailbox was full or the target VM does not exist.

Adapted the tests to this new service.
Added tests specific to spci_message_init and spci_msg_send.

Change-Id: I55adfe68502ddc7bf864432f3e567b6cfe785f92
diff --git a/test/vmapi/primary_with_secondaries/BUILD.gn b/test/vmapi/primary_with_secondaries/BUILD.gn
index b82fa30..34f8857 100644
--- a/test/vmapi/primary_with_secondaries/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/BUILD.gn
@@ -30,6 +30,7 @@
     "memory_sharing.c",
     "no_services.c",
     "run_race.c",
+    "spci.c",
   ]
 
   sources += [ "util.c" ]
diff --git a/test/vmapi/primary_with_secondaries/inc/util.h b/test/vmapi/primary_with_secondaries/inc/util.h
index b127fae..eca641f 100644
--- a/test/vmapi/primary_with_secondaries/inc/util.h
+++ b/test/vmapi/primary_with_secondaries/inc/util.h
@@ -16,9 +16,11 @@
 
 #pragma once
 
+#include "vmapi/hf/spci.h"
+
 struct mailbox_buffers {
-	void *send;
-	void *recv;
+	struct spci_message *send;
+	struct spci_message *recv;
 };
 
 struct mailbox_buffers set_up_mailbox(void);
diff --git a/test/vmapi/primary_with_secondaries/interrupts.c b/test/vmapi/primary_with_secondaries/interrupts.c
index 655ca54..9429acc 100644
--- a/test/vmapi/primary_with_secondaries/interrupts.c
+++ b/test/vmapi/primary_with_secondaries/interrupts.c
@@ -42,12 +42,15 @@
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Set the message, echo it and wait for a response. */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
@@ -74,7 +77,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 
@@ -83,7 +87,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
@@ -110,7 +115,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 
@@ -119,7 +125,7 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
-	EXPECT_EQ(memcmp(mb.recv, expected_response_2,
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
 			 sizeof(expected_response_2)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
@@ -149,7 +155,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 
@@ -158,12 +165,14 @@
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Now send a message to the secondary. */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
-	EXPECT_EQ(memcmp(mb.recv, expected_response_2,
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response_2,
 			 sizeof(expected_response_2)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
@@ -193,12 +202,15 @@
 	 * Now send a message to the secondary to enable the interrupt ID, and
 	 * expect the response from the interrupt we sent before.
 	 */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
@@ -225,7 +237,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
@@ -252,7 +265,8 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
-	EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+	EXPECT_EQ(memcmp(mb.recv->payload, expected_response,
+			 sizeof(expected_response)),
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
@@ -273,12 +287,14 @@
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
diff --git a/test/vmapi/primary_with_secondaries/mailbox.c b/test/vmapi/primary_with_secondaries/mailbox.c
index 1e1a4d3..8615957 100644
--- a/test/vmapi/primary_with_secondaries/mailbox.c
+++ b/test/vmapi/primary_with_secondaries/mailbox.c
@@ -18,6 +18,8 @@
 
 #include "hf/arch/std.h"
 
+#include "hf/spci.h"
+
 #include "vmapi/hf/call.h"
 
 #include "hftest.h"
@@ -87,12 +89,14 @@
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Set the message, echo it and check it didn't change. */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.send->payload, message, sizeof(message)), 0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
 
@@ -116,13 +120,15 @@
 
 		/* Set the message, echo it and check it didn't change. */
 		next_permutation(message, sizeof(message) - 1);
-		memcpy(mb.send, message, sizeof(message));
-		EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false),
-			  0);
+		memcpy(mb.send->payload, message, sizeof(message));
+		spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+				  HF_PRIMARY_VM_ID);
+		EXPECT_EQ(spci_msg_send(0), 0);
 		run_res = hf_vcpu_run(SERVICE_VM0, 0);
 		EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 		EXPECT_EQ(run_res.message.size, sizeof(message));
-		EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+		EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)),
+			  0);
 		EXPECT_EQ(hf_mailbox_clear(), 0);
 	}
 }
@@ -152,15 +158,15 @@
 	 * SERVICE_VM0, then to SERVICE_VM1 and finally back to here.
 	 */
 	{
-		uint32_t *chain = mb.send;
+		uint32_t *chain = (uint32_t *)mb.send->payload;
 		*chain++ = htole32(SERVICE_VM1);
 		*chain++ = htole32(HF_PRIMARY_VM_ID);
 		memcpy(chain, message, sizeof(message));
-		EXPECT_EQ(hf_mailbox_send(
-				  SERVICE_VM0,
+
+		spci_message_init(mb.send,
 				  sizeof(message) + (2 * sizeof(uint32_t)),
-				  false),
-			  0);
+				  SERVICE_VM0, HF_PRIMARY_VM_ID);
+		EXPECT_EQ(spci_msg_send(0), 0);
 	}
 
 	/* Let SERVICE_VM0 forward the message. */
@@ -176,7 +182,7 @@
 	/* Ensure the message is in tact. */
 	EXPECT_EQ(run_res.message.vm_id, HF_PRIMARY_VM_ID);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
 
@@ -188,15 +194,16 @@
 {
 	struct hf_vcpu_run_return run_res;
 
-	set_up_mailbox();
-
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), -1);
+	struct mailbox_buffers mb = set_up_mailbox();
+	spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_BUSY);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+	spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 }
 
 /**
@@ -207,9 +214,10 @@
 {
 	struct hf_vcpu_run_return run_res;
 
-	set_up_mailbox();
+	struct mailbox_buffers mb = set_up_mailbox();
 
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, true), -1);
+	spci_message_init(mb.send, 0, SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(SPCI_MSG_SEND_NOTIFY), SPCI_BUSY);
 
 	/*
 	 * Run first VM for it to configure itself. It should result in
@@ -223,7 +231,7 @@
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
 
 	/* Send should now succeed. */
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 }
 
 /**
@@ -244,12 +252,14 @@
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send a message to echo service, and get response back. */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
 
 	/* Let secondary VM continue running so that it will wait again. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -258,8 +268,12 @@
 
 	/* Without clearing our mailbox, send message again. */
 	reverse(message, strlen(message));
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+
+	/* Message should be dropped since the mailbox was not cleared. */
+	EXPECT_EQ(spci_msg_send(0), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
@@ -281,7 +295,7 @@
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
 }
 
 /**
@@ -302,15 +316,17 @@
 	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send a message to echo service twice. The second should fail. */
-	memcpy(mb.send, message, sizeof(message));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), true), -1);
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
+	EXPECT_EQ(spci_msg_send(SPCI_MSG_SEND_NOTIFY), SPCI_BUSY);
 
 	/* Receive a reply for the first message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
-	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(memcmp(mb.recv->payload, message, sizeof(message)), 0);
 
 	/* Run VM again so that it clears its mailbox. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -321,5 +337,5 @@
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
 
 	/* Send should now succeed. */
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 }
diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c
index e44dd93..8d8bb47 100644
--- a/test/vmapi/primary_with_secondaries/memory_sharing.c
+++ b/test/vmapi/primary_with_secondaries/memory_sharing.c
@@ -104,8 +104,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_YIELD);
@@ -146,8 +147,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -183,8 +185,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -220,8 +223,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -256,8 +260,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -295,8 +300,9 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
+	memcpy(mb.send->payload, &ptr, sizeof(ptr));
+	spci_message_init(mb.send, sizeof(ptr), SERVICE_VM0, HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -328,7 +334,7 @@
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 
 	/* Check the memory was cleared. */
-	memcpy(&ptr, mb.recv, sizeof(ptr));
+	memcpy(&ptr, mb.recv->payload, sizeof(ptr));
 	for (int i = 0; i < PAGE_SIZE; ++i) {
 		ASSERT_EQ(ptr[i], 0);
 	}
@@ -354,7 +360,7 @@
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 
 	/* Check the memory was cleared. */
-	memcpy(&ptr, mb.recv, sizeof(ptr));
+	memcpy(&ptr, mb.recv->payload, sizeof(ptr));
 	for (int i = 0; i < PAGE_SIZE; ++i) {
 		ASSERT_EQ(ptr[i], 0);
 	}
diff --git a/test/vmapi/primary_with_secondaries/run_race.c b/test/vmapi/primary_with_secondaries/run_race.c
index f77a7b1..2a25c65 100644
--- a/test/vmapi/primary_with_secondaries/run_race.c
+++ b/test/vmapi/primary_with_secondaries/run_race.c
@@ -57,7 +57,7 @@
 
 	/* Copies the contents of the received boolean to the return value. */
 	if (run_res.message.size == sizeof(ok)) {
-		memcpy(&ok, mb->recv, sizeof(ok));
+		memcpy(&ok, mb->recv->payload, sizeof(ok));
 	}
 
 	hf_mailbox_clear();
diff --git a/test/vmapi/primary_with_secondaries/services/BUILD.gn b/test/vmapi/primary_with_secondaries/services/BUILD.gn
index b0ef794..6b15dc7 100644
--- a/test/vmapi/primary_with_secondaries/services/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/services/BUILD.gn
@@ -125,6 +125,19 @@
   ]
 }
 
+# Service to receive messages in a secondary VM and ensure that the header fields are correctly set.
+source_set("spci_check") {
+  testonly = true
+  public_configs = [
+    "..:config",
+    "//test/hftest:hftest_config",
+  ]
+
+  sources = [
+    "spci_check.c",
+  ]
+}
+
 # Group services together into VMs.
 
 vm_kernel("service_vm0") {
@@ -139,6 +152,7 @@
     ":memory",
     ":receive_block",
     ":relay",
+    ":spci_check",
     ":wfi",
     "//test/hftest:hftest_secondary_vm",
   ]
diff --git a/test/vmapi/primary_with_secondaries/services/check_state.c b/test/vmapi/primary_with_secondaries/services/check_state.c
index 735b8a5..e467f52 100644
--- a/test/vmapi/primary_with_secondaries/services/check_state.c
+++ b/test/vmapi/primary_with_secondaries/services/check_state.c
@@ -21,13 +21,13 @@
 
 #include "hftest.h"
 
-void send_with_retry(uint32_t vm_id, size_t size)
+void send_with_retry()
 {
 	int64_t res;
 
 	do {
-		res = hf_mailbox_send(vm_id, size, false);
-	} while (res == -1);
+		res = spci_msg_send(0);
+	} while (res != SPCI_SUCCESS);
 }
 
 /**
@@ -48,6 +48,9 @@
 	static volatile uintptr_t expected;
 	static volatile uintptr_t actual;
 
+	spci_message_init(SERVICE_SEND_BUFFER(), 0, HF_PRIMARY_VM_ID,
+			  hf_vm_get_id());
+
 	for (i = 0; i < 100000; i++) {
 		/*
 		 * We store the expected/actual values in volatile static
@@ -56,13 +59,15 @@
 		 */
 		expected = i;
 		per_cpu_ptr_set(expected);
-		send_with_retry(HF_PRIMARY_VM_ID, 0);
+		send_with_retry();
 		actual = per_cpu_ptr_get();
 		ok &= expected == actual;
 	}
 
 	/* Send two replies, one for each physical CPU. */
-	memcpy(SERVICE_SEND_BUFFER(), &ok, sizeof(ok));
-	send_with_retry(HF_PRIMARY_VM_ID, sizeof(ok));
-	send_with_retry(HF_PRIMARY_VM_ID, sizeof(ok));
+	memcpy(SERVICE_SEND_BUFFER()->payload, &ok, sizeof(ok));
+	spci_message_init(SERVICE_SEND_BUFFER(), sizeof(ok), HF_PRIMARY_VM_ID,
+			  hf_vm_get_id());
+	send_with_retry();
+	send_with_retry();
 }
diff --git a/test/vmapi/primary_with_secondaries/services/echo.c b/test/vmapi/primary_with_secondaries/services/echo.c
index ffb89df..c58ccfb 100644
--- a/test/vmapi/primary_with_secondaries/services/echo.c
+++ b/test/vmapi/primary_with_secondaries/services/echo.c
@@ -16,6 +16,8 @@
 
 #include "hf/arch/std.h"
 
+#include "hf/spci.h"
+
 #include "vmapi/hf/call.h"
 
 #include "hftest.h"
@@ -24,9 +26,16 @@
 {
 	/* Loop, echo messages back to the sender. */
 	for (;;) {
-		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
-		memcpy(SERVICE_SEND_BUFFER(), SERVICE_RECV_BUFFER(), res.size);
+		hf_mailbox_receive(true);
+		struct spci_message *send_buf = SERVICE_SEND_BUFFER();
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+
+		memcpy(send_buf->payload, recv_buf->payload, recv_buf->length);
+		spci_message_init(SERVICE_SEND_BUFFER(), recv_buf->length,
+				  recv_buf->source_vm_id,
+				  recv_buf->target_vm_id);
+
 		hf_mailbox_clear();
-		hf_mailbox_send(res.vm_id, res.size, false);
+		spci_msg_send(0);
 	}
 }
diff --git a/test/vmapi/primary_with_secondaries/services/echo_with_notification.c b/test/vmapi/primary_with_secondaries/services/echo_with_notification.c
index 2e733ae..e6c66c0 100644
--- a/test/vmapi/primary_with_secondaries/services/echo_with_notification.c
+++ b/test/vmapi/primary_with_secondaries/services/echo_with_notification.c
@@ -18,6 +18,8 @@
 #include "hf/arch/std.h"
 #include "hf/arch/vm/interrupts_gicv3.h"
 
+#include "hf/spci.h"
+
 #include "vmapi/hf/call.h"
 
 #include "../msr.h"
@@ -51,11 +53,18 @@
 
 	/* Loop, echo messages back to the sender. */
 	for (;;) {
-		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
+		hf_mailbox_receive(true);
 
-		memcpy(SERVICE_SEND_BUFFER(), SERVICE_RECV_BUFFER(), res.size);
-		while (hf_mailbox_send(res.vm_id, res.size, true) < 0) {
-			wait_for_vm(res.vm_id);
+		struct spci_message *send_buf = SERVICE_SEND_BUFFER();
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+
+		memcpy(send_buf->payload, recv_buf->payload, recv_buf->length);
+		spci_message_init(send_buf, recv_buf->length,
+				  recv_buf->source_vm_id,
+				  recv_buf->target_vm_id);
+
+		while (spci_msg_send(SPCI_MSG_SEND_NOTIFY) != SPCI_SUCCESS) {
+			wait_for_vm(recv_buf->source_vm_id);
 		}
 
 		hf_mailbox_clear();
diff --git a/test/vmapi/primary_with_secondaries/services/interruptible.c b/test/vmapi/primary_with_secondaries/services/interruptible.c
index 9b36e8c..c5c67a5 100644
--- a/test/vmapi/primary_with_secondaries/services/interruptible.c
+++ b/test/vmapi/primary_with_secondaries/services/interruptible.c
@@ -38,8 +38,10 @@
 	dlog("secondary IRQ %d from current\n", interrupt_id);
 	buffer[8] = '0' + interrupt_id / 10;
 	buffer[9] = '0' + interrupt_id % 10;
-	memcpy(SERVICE_SEND_BUFFER(), buffer, size);
-	hf_mailbox_send(HF_PRIMARY_VM_ID, size, false);
+	memcpy(SERVICE_SEND_BUFFER()->payload, buffer, size);
+	spci_message_init(SERVICE_SEND_BUFFER(), size, HF_PRIMARY_VM_ID,
+			  hf_vm_get_id());
+	spci_msg_send(0);
 	dlog("secondary IRQ %d ended\n", interrupt_id);
 }
 
@@ -62,6 +64,7 @@
 TEST_SERVICE(interruptible)
 {
 	uint32_t this_vm_id = hf_vm_get_id();
+	struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
 
 	exception_setup(irq);
 	hf_interrupt_enable(SELF_INTERRUPT_ID, true);
@@ -72,23 +75,23 @@
 	for (;;) {
 		const char ping_message[] = "Ping";
 		const char enable_message[] = "Enable interrupt C";
-		struct hf_mailbox_receive_return received_message =
-			mailbox_receive_retry();
-		if (received_message.vm_id == HF_PRIMARY_VM_ID &&
-		    received_message.size == sizeof(ping_message) &&
-		    memcmp(SERVICE_RECV_BUFFER(), ping_message,
+
+		mailbox_receive_retry();
+		if (recv_buf->source_vm_id == HF_PRIMARY_VM_ID &&
+		    recv_buf->length == sizeof(ping_message) &&
+		    memcmp(recv_buf->payload, ping_message,
 			   sizeof(ping_message)) == 0) {
 			/* Interrupt ourselves */
 			hf_interrupt_inject(this_vm_id, 0, SELF_INTERRUPT_ID);
-		} else if (received_message.vm_id == HF_PRIMARY_VM_ID &&
-			   received_message.size == sizeof(enable_message) &&
-			   memcmp(SERVICE_RECV_BUFFER(), enable_message,
+		} else if (recv_buf->source_vm_id == HF_PRIMARY_VM_ID &&
+			   recv_buf->length == sizeof(enable_message) &&
+			   memcmp(recv_buf->payload, enable_message,
 				  sizeof(enable_message)) == 0) {
 			/* Enable interrupt ID C. */
 			hf_interrupt_enable(EXTERNAL_INTERRUPT_ID_C, true);
 		} else {
 			dlog("Got unexpected message from VM %d, size %d.\n",
-			     received_message.vm_id, received_message.size);
+			     recv_buf->source_vm_id, recv_buf->length);
 			FAIL("Unexpected message");
 		}
 		hf_mailbox_clear();
diff --git a/test/vmapi/primary_with_secondaries/services/interruptible_echo.c b/test/vmapi/primary_with_secondaries/services/interruptible_echo.c
index e1eb643..dff3b6d 100644
--- a/test/vmapi/primary_with_secondaries/services/interruptible_echo.c
+++ b/test/vmapi/primary_with_secondaries/services/interruptible_echo.c
@@ -39,6 +39,7 @@
 
 	for (;;) {
 		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
+		struct spci_message *message = SERVICE_SEND_BUFFER();
 
 		/* Retry if interrupted but made visible with the yield. */
 		while (res.vm_id == HF_INVALID_VM_ID && res.size == 0) {
@@ -46,8 +47,12 @@
 			res = hf_mailbox_receive(true);
 		}
 
-		memcpy(SERVICE_SEND_BUFFER(), SERVICE_RECV_BUFFER(), res.size);
+		memcpy(message->payload, SERVICE_RECV_BUFFER()->payload,
+		       res.size);
+		spci_message_init(message, res.size, HF_PRIMARY_VM_ID,
+				  SERVICE_VM0);
+
 		hf_mailbox_clear();
-		hf_mailbox_send(res.vm_id, res.size, false);
+		spci_msg_send(0);
 	}
 }
diff --git a/test/vmapi/primary_with_secondaries/services/memory.c b/test/vmapi/primary_with_secondaries/services/memory.c
index b85b003..a826184 100644
--- a/test/vmapi/primary_with_secondaries/services/memory.c
+++ b/test/vmapi/primary_with_secondaries/services/memory.c
@@ -28,12 +28,16 @@
 {
 	/* Loop, writing message to the shared memory. */
 	for (;;) {
-		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
+		hf_mailbox_receive(true);
 		uint8_t *ptr;
 		size_t i;
 
 		/* Check the memory was cleared. */
-		memcpy(&ptr, SERVICE_RECV_BUFFER(), sizeof(ptr));
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+		memcpy(&ptr, recv_buf->payload, sizeof(ptr));
+		spci_message_init(SERVICE_SEND_BUFFER(), sizeof(ptr),
+				  recv_buf->source_vm_id, hf_vm_get_id());
+
 		for (int i = 0; i < PAGE_SIZE; ++i) {
 			ASSERT_EQ(ptr[i], 0);
 		}
@@ -48,7 +52,7 @@
 
 		/* Signal completion and reset. */
 		hf_mailbox_clear();
-		hf_mailbox_send(res.vm_id, 0, false);
+		spci_msg_send(0);
 	}
 }
 
@@ -56,21 +60,26 @@
 {
 	/* Loop, giving memory back to the sender. */
 	for (;;) {
-		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
+		hf_mailbox_receive(true);
 		uint8_t *ptr;
 
 		/* Check the memory was cleared. */
-		memcpy(&ptr, SERVICE_RECV_BUFFER(), sizeof(ptr));
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+		memcpy(&ptr, recv_buf->payload, sizeof(ptr));
+		spci_message_init(SERVICE_SEND_BUFFER(), sizeof(ptr),
+				  recv_buf->source_vm_id, hf_vm_get_id());
+
 		for (int i = 0; i < PAGE_SIZE; ++i) {
 			ASSERT_EQ(ptr[i], 0);
 		}
 
 		/* Give the memory back and notify the sender. */
-		ASSERT_EQ(hf_share_memory(res.vm_id, (hf_ipaddr_t)ptr,
-					  PAGE_SIZE, HF_MEMORY_GIVE),
+		ASSERT_EQ(hf_share_memory(recv_buf->source_vm_id,
+					  (hf_ipaddr_t)ptr, PAGE_SIZE,
+					  HF_MEMORY_GIVE),
 			  0);
 		hf_mailbox_clear();
-		hf_mailbox_send(res.vm_id, 0, false);
+		spci_msg_send(0);
 
 		/*
 		 * Try and access the memory which will cause a fault unless the
@@ -94,8 +103,10 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(SERVICE_SEND_BUFFER(), &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(HF_PRIMARY_VM_ID, sizeof(ptr), false), 0);
+	memcpy(SERVICE_SEND_BUFFER()->payload, &ptr, sizeof(ptr));
+	spci_message_init(SERVICE_SEND_BUFFER(), sizeof(ptr), HF_PRIMARY_VM_ID,
+			  hf_vm_get_id());
+	EXPECT_EQ(spci_msg_send(0), 0);
 
 	/* Try using the memory that isn't valid unless it's been returned.  */
 	page[16] = 123;
@@ -115,8 +126,10 @@
 	 *       API is still to be agreed on so the address is passed
 	 *       explicitly to test the mechanism.
 	 */
-	memcpy(SERVICE_SEND_BUFFER(), &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(HF_PRIMARY_VM_ID, sizeof(ptr), false), 0);
+	memcpy(SERVICE_SEND_BUFFER()->payload, &ptr, sizeof(ptr));
+	spci_message_init(SERVICE_SEND_BUFFER(), sizeof(ptr), HF_PRIMARY_VM_ID,
+			  hf_vm_get_id());
+	EXPECT_EQ(spci_msg_send(0), 0);
 
 	/* Try using the memory that isn't valid unless it's been returned.  */
 	page[633] = 180;
diff --git a/test/vmapi/primary_with_secondaries/services/receive_block.c b/test/vmapi/primary_with_secondaries/services/receive_block.c
index bdfbed6..59a10a5 100644
--- a/test/vmapi/primary_with_secondaries/services/receive_block.c
+++ b/test/vmapi/primary_with_secondaries/services/receive_block.c
@@ -18,6 +18,7 @@
 #include "hf/arch/vm/interrupts_gicv3.h"
 
 #include "hf/dlog.h"
+#include "hf/spci.h"
 
 #include "vmapi/hf/call.h"
 
@@ -50,6 +51,9 @@
 		EXPECT_EQ(res.size, 0);
 	}
 
-	memcpy(SERVICE_SEND_BUFFER(), message, sizeof(message));
-	hf_mailbox_send(HF_PRIMARY_VM_ID, sizeof(message), false);
+	memcpy(SERVICE_SEND_BUFFER()->payload, message, sizeof(message));
+	spci_message_init(SERVICE_SEND_BUFFER(), sizeof(message),
+			  HF_PRIMARY_VM_ID, hf_vm_get_id());
+
+	spci_msg_send(0);
 }
diff --git a/test/vmapi/primary_with_secondaries/services/relay.c b/test/vmapi/primary_with_secondaries/services/relay.c
index f509835..627a461 100644
--- a/test/vmapi/primary_with_secondaries/services/relay.c
+++ b/test/vmapi/primary_with_secondaries/services/relay.c
@@ -36,18 +36,24 @@
 		uint32_t next_message_size;
 
 		/* Receive the message to relay. */
-		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
-		ASSERT_GE(res.size, sizeof(uint32_t));
+		hf_mailbox_receive(true);
 
 		/* Prepare to relay the message. */
-		chain = SERVICE_RECV_BUFFER();
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+		struct spci_message *send_buf = SERVICE_SEND_BUFFER();
+		ASSERT_GE(recv_buf->length, sizeof(uint32_t));
+
+		chain = (uint32_t *)recv_buf->payload;
 		next_vm_id = le32toh(*chain);
 		next_message = chain + 1;
-		next_message_size = res.size - sizeof(uint32_t);
+		next_message_size = recv_buf->length - sizeof(uint32_t);
 
 		/* Send the message to the next stage. */
-		memcpy(SERVICE_SEND_BUFFER(), next_message, next_message_size);
+		memcpy(send_buf->payload, next_message, next_message_size);
+		spci_message_init(send_buf, next_message_size, next_vm_id,
+				  hf_vm_get_id());
+
 		hf_mailbox_clear();
-		hf_mailbox_send(next_vm_id, next_message_size, false);
+		spci_msg_send(0);
 	}
 }
diff --git a/test/vmapi/primary_with_secondaries/services/spci_check.c b/test/vmapi/primary_with_secondaries/services/spci_check.c
new file mode 100644
index 0000000..a81fc01
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/services/spci_check.c
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include "hf/arch/std.h"
+
+#include "hf/spci.h"
+
+#include "vmapi/hf/call.h"
+
+#include "hftest.h"
+#include "primary_with_secondary.h"
+
+TEST_SERVICE(spci_check)
+{
+	struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+	const char message[] = "spci_msg_send";
+	struct spci_message expected_message = {
+		.flags = SPCI_MESSAGE_IMPDEF_MASK,
+		.length = sizeof(message),
+		.target_vm_id = SERVICE_VM0,
+		.source_vm_id = HF_PRIMARY_VM_ID,
+
+		/*
+		 * TODO: Padding fields may be set to MBZ in the next SPCI spec
+		 * versions.
+		 */
+		.reserved_1 = 0,
+		.reserved_2 = 0,
+	};
+
+	/* Wait for single message to be sent by the primary VM. */
+	hf_mailbox_receive(true);
+
+	/* Ensure message header has all fields correctly set. */
+	EXPECT_EQ(recv_buf->flags, expected_message.flags);
+	EXPECT_EQ(recv_buf->length, expected_message.length);
+	EXPECT_EQ(recv_buf->target_vm_id, expected_message.target_vm_id);
+	EXPECT_EQ(recv_buf->source_vm_id, expected_message.source_vm_id);
+
+	/* TODO: Padding fields may be set to MBZ in the next SPCI spec
+	 * versions. */
+	EXPECT_EQ(recv_buf->reserved_1, expected_message.reserved_1);
+	EXPECT_EQ(recv_buf->reserved_2, expected_message.reserved_2);
+
+	/* Ensure message header has all fields correctly set. */
+	EXPECT_EQ(memcmp(recv_buf, &expected_message, sizeof(expected_message)),
+		  0);
+
+	/* Ensure that the payload was correctly transmitted. */
+	EXPECT_EQ(memcmp(recv_buf->payload, message, sizeof(message)), 0);
+
+	hf_vcpu_yield();
+}
diff --git a/test/vmapi/primary_with_secondaries/services/wfi.c b/test/vmapi/primary_with_secondaries/services/wfi.c
index 9c4f66e..6d3a05c 100644
--- a/test/vmapi/primary_with_secondaries/services/wfi.c
+++ b/test/vmapi/primary_with_secondaries/services/wfi.c
@@ -48,6 +48,9 @@
 		interrupt_wait();
 	}
 
-	memcpy(SERVICE_SEND_BUFFER(), message, sizeof(message));
-	hf_mailbox_send(HF_PRIMARY_VM_ID, sizeof(message), false);
+	memcpy(SERVICE_SEND_BUFFER()->payload, message, sizeof(message));
+	spci_message_init(SERVICE_SEND_BUFFER(), sizeof(message),
+			  HF_PRIMARY_VM_ID, hf_vm_get_id());
+
+	spci_msg_send(0);
 }
diff --git a/test/vmapi/primary_with_secondaries/spci.c b/test/vmapi/primary_with_secondaries/spci.c
new file mode 100644
index 0000000..e6d1049
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/spci.c
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "hf/spci.h"
+
+#include <stdint.h>
+
+#include "hf/arch/std.h"
+
+#include "vmapi/hf/call.h"
+
+#include "hftest.h"
+#include "primary_with_secondary.h"
+#include "util.h"
+
+/**
+ * Send a message to a secondary VM which checks the validity of the received
+ * header.
+ */
+TEST(spci, msg_send)
+{
+	const char message[] = "spci_msg_send";
+	struct hf_vcpu_run_return run_res;
+	struct mailbox_buffers mb = set_up_mailbox();
+
+	SERVICE_SELECT(SERVICE_VM0, "spci_check", mb.send);
+
+	/* Set the payload, init the message header and send the message. */
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0,
+			  HF_PRIMARY_VM_ID);
+	EXPECT_EQ(spci_msg_send(0), 0);
+
+	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_YIELD);
+}
+
+/**
+ * Send a message to a secondary VM spoofing the source vm id.
+ */
+TEST(spci, msg_send_spoof)
+{
+	const char message[] = "spci_msg_send";
+	struct mailbox_buffers mb = set_up_mailbox();
+
+	SERVICE_SELECT(SERVICE_VM0, "spci_check", mb.send);
+
+	/* Set the payload, init the message header and send the message. */
+	memcpy(mb.send->payload, message, sizeof(message));
+	spci_message_init(mb.send, sizeof(message), SERVICE_VM0, SERVICE_VM1);
+	EXPECT_EQ(spci_msg_send(0), SPCI_INVALID_PARAMETERS);
+}
diff --git a/test/vmapi/primary_with_secondaries/util.c b/test/vmapi/primary_with_secondaries/util.c
index 86830c4..24b2b9e 100644
--- a/test/vmapi/primary_with_secondaries/util.c
+++ b/test/vmapi/primary_with_secondaries/util.c
@@ -17,6 +17,7 @@
 #include "util.h"
 
 #include "hf/mm.h"
+#include "hf/spci.h"
 
 #include "vmapi/hf/call.h"
 
@@ -34,7 +35,7 @@
 {
 	ASSERT_EQ(hf_vm_configure(send_page_addr, recv_page_addr), 0);
 	return (struct mailbox_buffers){
-		.send = send_page,
-		.recv = recv_page,
+		.send = ((struct spci_message *)send_page),
+		.recv = ((struct spci_message *)recv_page),
 	};
 }