SPCI: Donate memory architected message.

The donate mechnism accepts a single memory region as per the spec but
restrics the number of constituents of this regions to 1.
Multiple constituents will be introduced in a later commit.

Change-Id: I7af9b80068060aedb953d3e204fa3e03c9ccc438
diff --git a/test/hftest/inc/hftest_impl.h b/test/hftest/inc/hftest_impl.h
index 64eeac4..f3eb407 100644
--- a/test/hftest/inc/hftest_impl.h
+++ b/test/hftest/inc/hftest_impl.h
@@ -22,6 +22,8 @@
 #include "hf/spci.h"
 #include "hf/std.h"
 
+#include "vmapi/hf/spci.h"
+
 #define HFTEST_MAX_TESTS 50
 
 /*
diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c
index 70dbc08..ed402b8 100644
--- a/test/vmapi/primary_with_secondaries/memory_sharing.c
+++ b/test/vmapi/primary_with_secondaries/memory_sharing.c
@@ -163,6 +163,45 @@
 }
 
 /**
+ * SPCI Memory given away can be given back.
+ * Employing SPCI donate architected messages.
+ */
+TEST(memory_sharing, spci_give_and_get_back)
+{
+	struct hf_vcpu_run_return run_res;
+	struct mailbox_buffers mb = set_up_mailbox();
+	uint8_t *ptr = page;
+
+	SERVICE_SELECT(SERVICE_VM0, "memory_return_spci", mb.send);
+
+	/* Initialise the memory before giving it. */
+	memset_s(ptr, sizeof(page), 'b', PAGE_SIZE);
+
+	/* Can only donate single constituent memory region. */
+	struct spci_memory_region_constituent constituents[] = {
+		{.address = (uint64_t)page, .page_count = 1},
+	};
+
+	spci_memory_donate(mb.send, SERVICE_VM0, HF_PRIMARY_VM_ID, constituents,
+			   1, 0);
+
+	EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
+	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+
+	/* Let the memory be returned. */
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+
+	/* Ensure that the secondary VM accessed the region. */
+	for (int i = 0; i < PAGE_SIZE; ++i) {
+		ASSERT_EQ(ptr[i], 'c');
+	}
+
+	/* Observe the service faulting when accessing the memory. */
+	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_ABORTED);
+}
+
+/**
  * Memory given away can be given back.
  */
 TEST(memory_sharing, give_and_get_back)
diff --git a/test/vmapi/primary_with_secondaries/services/memory.c b/test/vmapi/primary_with_secondaries/services/memory.c
index f17932c..8b65600 100644
--- a/test/vmapi/primary_with_secondaries/services/memory.c
+++ b/test/vmapi/primary_with_secondaries/services/memory.c
@@ -55,6 +55,41 @@
 	}
 }
 
+TEST_SERVICE(memory_return_spci)
+{
+	/* Loop, giving memory back to the sender. */
+	for (;;) {
+		spci_msg_recv(SPCI_MSG_RECV_BLOCK);
+		uint8_t *ptr;
+
+		struct spci_message *recv_buf = SERVICE_RECV_BUFFER();
+		struct spci_message *send_buf = SERVICE_SEND_BUFFER();
+		struct spci_memory_region *memory_region =
+			spci_get_donated_memory_region(recv_buf);
+
+		ptr = (uint8_t *)memory_region->constituents[0].address;
+		/* Relevant information read, mailbox can be cleared. */
+		hf_mailbox_clear();
+
+		/* Check that one has access to the shared region. */
+		for (int i = 0; i < PAGE_SIZE; ++i) {
+			ptr[i]++;
+		}
+
+		/* Give the memory back and notify the sender. */
+		spci_memory_donate(
+			send_buf, HF_PRIMARY_VM_ID, recv_buf->target_vm_id,
+			memory_region->constituents, memory_region->count, 0);
+		spci_msg_send(0);
+
+		/*
+		 * Try and access the memory which will cause a fault unless the
+		 * memory has been shared back again.
+		 */
+		ptr[0] = 123;
+	}
+}
+
 TEST_SERVICE(memory_return)
 {
 	/* Loop, giving memory back to the sender. */