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/src/BUILD.gn b/src/BUILD.gn
index 89c476c..d0cbc90 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -108,6 +108,7 @@
     "fdt_test.cc",
     "mm_test.cc",
     "mpool_test.cc",
+    "spci_test.cc",
   ]
   sources += [ "layout_fake.c" ]
   cflags_cc = [
diff --git a/src/api.c b/src/api.c
index 938d978..1fdb54a 100644
--- a/src/api.c
+++ b/src/api.c
@@ -28,6 +28,7 @@
 #include "hf/vm.h"
 
 #include "vmapi/hf/call.h"
+#include "vmapi/hf/spci.h"
 
 /*
  * To eliminate the risk of deadlocks, we define a partial order for the
@@ -729,43 +730,63 @@
  * If the recipient's receive buffer is busy, it can optionally register the
  * caller to be notified when the recipient's receive buffer becomes available.
  */
-int64_t api_mailbox_send(uint32_t vm_id, size_t size, bool notify,
-			 struct vcpu *current, struct vcpu **next)
+int32_t api_spci_msg_send(uint32_t attributes, struct vcpu *current,
+			  struct vcpu **next)
 {
 	struct vm *from = current->vm;
 	struct vm *to;
-	const void *from_buf;
-	int64_t ret;
 	struct hf_vcpu_run_return primary_ret = {
 		.code = HF_VCPU_RUN_MESSAGE,
 	};
+	struct spci_message from_msg_replica;
+	struct spci_message *to_msg;
+	const struct spci_message *from_msg;
 
-	/* Limit the size of transfer. */
-	if (size > HF_MAILBOX_SIZE) {
-		return -1;
-	}
+	uint32_t size;
 
-	/* Disallow reflexive requests as this suggests an error in the VM. */
-	if (vm_id == from->id) {
-		return -1;
-	}
+	int64_t ret;
+	bool notify = (attributes & SPCI_MSG_SEND_NOTIFY_MASK) ==
+		      SPCI_MSG_SEND_NOTIFY;
 
-	/* Ensure the target VM exists. */
-	to = vm_get(vm_id);
-	if (to == NULL) {
-		return -1;
+	/*
+	 * Check that the sender has configured its send buffer. Copy the
+	 * message header. If the tx mailbox at from_msg is configured (i.e.
+	 * from_msg != NULL) then it can be safely accessed after releasing the
+	 * lock since the tx mailbox address can only be configured once.
+	 */
+	sl_lock(&from->lock);
+	from_msg = from->mailbox.send;
+	sl_unlock(&from->lock);
+
+	if (from_msg == NULL) {
+		return SPCI_INVALID_PARAMETERS;
 	}
 
 	/*
-	 * Check that the sender has configured its send buffer. It is safe to
-	 * use from_buf after releasing the lock because the buffer cannot be
-	 * modified once it's configured.
+	 * Note that the payload is not copied when the message header is.
 	 */
-	sl_lock(&from->lock);
-	from_buf = from->mailbox.send;
-	sl_unlock(&from->lock);
-	if (from_buf == NULL) {
-		return -1;
+	from_msg_replica = *from_msg;
+
+	/* Ensure source VM id corresponds to the current VM. */
+	if (from_msg_replica.source_vm_id != from->id) {
+		return SPCI_INVALID_PARAMETERS;
+	}
+
+	size = from_msg_replica.length;
+	/* Limit the size of transfer. */
+	if (size > HF_MAILBOX_SIZE - sizeof(struct spci_message)) {
+		return SPCI_INVALID_PARAMETERS;
+	}
+
+	/* Disallow reflexive requests as this suggests an error in the VM. */
+	if (from_msg_replica.target_vm_id == from->id) {
+		return SPCI_INVALID_PARAMETERS;
+	}
+
+	/* Ensure the target VM exists. */
+	to = vm_get(from_msg_replica.target_vm_id);
+	if (to == NULL) {
+		return SPCI_INVALID_PARAMETERS;
 	}
 
 	sl_lock(&to->lock);
@@ -778,7 +799,8 @@
 		 */
 		if (notify) {
 			struct wait_entry *entry =
-				&current->vm->wait_entries[vm_id];
+				&current->vm->wait_entries
+					 [from_msg_replica.target_vm_id];
 
 			/* Append waiter only if it's not there yet. */
 			if (list_empty(&entry->wait_links)) {
@@ -787,16 +809,18 @@
 			}
 		}
 
-		ret = -1;
+		ret = SPCI_BUSY;
 		goto out;
 	}
 
 	/* Copy data. */
-	memcpy(to->mailbox.recv, from_buf, size);
+	to_msg = to->mailbox.recv;
+	*to_msg = from_msg_replica;
+	memcpy(to_msg->payload, from->mailbox.send->payload, size);
 	to->mailbox.recv_bytes = size;
 	to->mailbox.recv_from_id = from->id;
 	primary_ret.message.vm_id = to->id;
-	ret = 0;
+	ret = SPCI_SUCCESS;
 
 	/* Messages for the primary VM are delivered directly. */
 	if (to->id == HF_PRIMARY_VM_ID) {
diff --git a/src/arch/aarch64/handler.c b/src/arch/aarch64/handler.c
index eb8f8dc..e29ddf0 100644
--- a/src/arch/aarch64/handler.c
+++ b/src/arch/aarch64/handler.c
@@ -21,6 +21,7 @@
 #include "hf/api.h"
 #include "hf/cpu.h"
 #include "hf/dlog.h"
+#include "hf/spci.h"
 #include "hf/vm.h"
 
 #include "vmapi/hf/call.h"
@@ -404,7 +405,7 @@
 		}
 	}
 
-	switch ((uint32_t)arg0 & ~SMCCC_CONVENTION_MASK) {
+	switch ((uint32_t)arg0) {
 	case HF_VM_GET_ID:
 		ret.user_ret = api_vm_get_id(current());
 		break;
@@ -432,9 +433,8 @@
 						current(), &ret.new);
 		break;
 
-	case HF_MAILBOX_SEND:
-		ret.user_ret =
-			api_mailbox_send(arg1, arg2, arg3, current(), &ret.new);
+	case SPCI_MSG_SEND_32:
+		ret.user_ret = api_spci_msg_send(arg1, current(), &ret.new);
 		break;
 
 	case HF_MAILBOX_RECEIVE:
diff --git a/src/spci_test.cc b/src/spci_test.cc
new file mode 100644
index 0000000..a3191cc
--- /dev/null
+++ b/src/spci_test.cc
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+extern "C" {
+#include "vmapi/hf/spci.h"
+}
+
+#include <gmock/gmock.h>
+
+namespace
+{
+using ::testing::Eq;
+
+/**
+ * Ensure that spci_message_init is correctly setting the expected fields in the
+ * SPCI common message header.
+ */
+TEST(spci, spci_message_init)
+{
+	spci_message header;
+	spci_message compare_header = {
+		.flags = SPCI_MESSAGE_IMPDEF_MASK,
+		.length = 1,
+		.target_vm_id = 2,
+		.source_vm_id = 3,
+	};
+
+	memset(&header, 0xff, sizeof(header));
+	spci_message_init(&header, 1, 2, 3);
+
+	EXPECT_THAT(memcmp(&header, &compare_header, sizeof(header)), 0);
+}
+} /* namespace */