Support SPCI memory sharing clear flag.

Bug: 132420445
Change-Id: I2705cc8114dcf3ad500e4060be54527cebc3d5a2
diff --git a/inc/vmapi/hf/spci.h b/inc/vmapi/hf/spci.h
index 30feb0e..812865b 100644
--- a/inc/vmapi/hf/spci.h
+++ b/inc/vmapi/hf/spci.h
@@ -264,6 +264,15 @@
 	uint16_t memory_attributes;
 };
 
+/** Flags to control the behaviour of a memory sharing transaction. */
+typedef uint32_t spci_memory_region_flags_t;
+
+/**
+ * Clear memory region contents after unmapping it from the sender and before
+ * mapping it for any receiver.
+ */
+#define SPCI_MEMORY_REGION_FLAG_CLEAR 0x1
+
 struct spci_memory_region {
 	/**
 	 * An implementation defined value associated with the receiver and the
@@ -271,7 +280,7 @@
 	 */
 	uint32_t tag;
 	/** Flags to control behaviour of the transaction. */
-	uint32_t flags;
+	spci_memory_region_flags_t flags;
 	/**
 	 * The total number of 4 kiB pages included in this memory region. This
 	 * must be equal to the sum of page counts specified in each
@@ -327,16 +336,16 @@
 	struct spci_memory_region *memory_region, spci_vm_id_t receiver,
 	const struct spci_memory_region_constituent constituents[],
 	uint32_t constituent_count, uint32_t tag,
-	enum spci_memory_access access, enum spci_memory_type type,
-	enum spci_memory_cacheability cacheability,
+	spci_memory_region_flags_t flags, enum spci_memory_access access,
+	enum spci_memory_type type, enum spci_memory_cacheability cacheability,
 	enum spci_memory_shareability shareability);
 
 uint32_t spci_memory_init(
 	void *message, enum spci_memory_share share_type, spci_vm_id_t receiver,
 	struct spci_memory_region_constituent *region_constituents,
 	uint32_t constituent_count, uint32_t tag,
-	enum spci_memory_access access, enum spci_memory_type type,
-	enum spci_memory_cacheability cacheability,
+	spci_memory_region_flags_t flags, enum spci_memory_access access,
+	enum spci_memory_type type, enum spci_memory_cacheability cacheability,
 	enum spci_memory_shareability shareability);
 
 /** Constructs an SPCI donate memory region message. */
@@ -349,7 +358,7 @@
 	enum spci_memory_shareability shareability)
 {
 	return spci_memory_init(message, SPCI_MEMORY_DONATE, receiver,
-				region_constituents, constituent_count, tag,
+				region_constituents, constituent_count, tag, 0,
 				access, type, cacheability, shareability);
 }
 
@@ -365,7 +374,7 @@
 	enum spci_memory_shareability shareability)
 {
 	return spci_memory_init(message, SPCI_MEMORY_LEND, receiver,
-				region_constituents, constituent_count, tag,
+				region_constituents, constituent_count, tag, 0,
 				access, type, cacheability, shareability);
 }
 
@@ -381,7 +390,7 @@
 	enum spci_memory_shareability shareability)
 {
 	return spci_memory_init(message, SPCI_MEMORY_SHARE, receiver,
-				region_constituents, constituent_count, tag,
+				region_constituents, constituent_count, tag, 0,
 				access, type, cacheability, shareability);
 }
 
@@ -395,7 +404,7 @@
 	uint32_t constituent_count, uint32_t tag)
 {
 	return spci_memory_init(message, SPCI_MEMORY_RELINQUISH, receiver,
-				region_constituents, constituent_count, tag,
+				region_constituents, constituent_count, tag, 0,
 				SPCI_MEMORY_RW_X, SPCI_MEMORY_DEVICE_MEM,
 				SPCI_MEMORY_DEV_NGNRNE,
 				SPCI_MEMORY_SHARE_NON_SHAREABLE);
diff --git a/src/api.c b/src/api.c
index caf024c..c748fb5 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1544,6 +1544,19 @@
 		goto out;
 	}
 
+	/* Clear the memory so no VM or device can see the previous contents. */
+	if ((memory_region->flags & SPCI_MEMORY_REGION_FLAG_CLEAR) &&
+	    !api_clear_memory(pa_begin, pa_end, &local_page_pool)) {
+		ret = spci_error(SPCI_NO_MEMORY);
+
+		/* Return memory to the sender. */
+		CHECK(mm_vm_identity_map(&from->ptable, pa_begin, pa_end,
+					 orig_from_mode, NULL,
+					 &local_page_pool));
+
+		goto out;
+	}
+
 	/* Complete the transfer by mapping the memory into the recipient. */
 	if (!mm_vm_identity_map(&to->ptable, pa_begin, pa_end, to_mode, NULL,
 				&local_page_pool)) {
diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c
index e8dd6c5..1606c6d 100644
--- a/test/vmapi/primary_with_secondaries/memory_sharing.c
+++ b/test/vmapi/primary_with_secondaries/memory_sharing.c
@@ -1655,3 +1655,38 @@
 			SPCI_INVALID_PARAMETERS);
 	}
 }
+
+/**
+ * SPCI: Memory can be cleared while being shared.
+ */
+TEST(memory_sharing, spci_share_clear)
+{
+	struct mailbox_buffers mb = set_up_mailbox();
+	uint8_t *ptr = page;
+	uint32_t msg_size;
+	size_t i;
+
+	SERVICE_SELECT(SERVICE_VM1, "spci_memory_return", mb.send);
+
+	/* Initialise the memory before giving it. */
+	memset_s(ptr, sizeof(page) * 2, 'b', PAGE_SIZE * 2);
+
+	struct spci_memory_region_constituent constituents[] = {
+		{.address = (uint64_t)page, .page_count = 2},
+	};
+
+	msg_size = spci_memory_init(
+		mb.send, SPCI_MEMORY_SHARE, SERVICE_VM1, constituents,
+		ARRAY_SIZE(constituents), 0, SPCI_MEMORY_REGION_FLAG_CLEAR,
+		SPCI_MEMORY_RO_X, SPCI_MEMORY_NORMAL_MEM,
+		SPCI_MEMORY_CACHE_WRITE_BACK, SPCI_MEMORY_OUTER_SHAREABLE);
+	EXPECT_EQ(spci_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM1, msg_size,
+				SPCI_MSG_SEND_LEGACY_MEMORY)
+			  .func,
+		  SPCI_SUCCESS_32);
+
+	/* Check that it has been cleared. */
+	for (i = 0; i < PAGE_SIZE * 2; ++i) {
+		ASSERT_EQ(ptr[i], 0);
+	};
+}
diff --git a/vmlib/spci.c b/vmlib/spci.c
index 93fe88d..19da8d7 100644
--- a/vmlib/spci.c
+++ b/vmlib/spci.c
@@ -48,7 +48,7 @@
 uint32_t spci_memory_region_init(
 	struct spci_memory_region *memory_region, spci_vm_id_t receiver,
 	const struct spci_memory_region_constituent constituents[],
-	uint32_t constituent_count, uint32_t tag,
+	uint32_t constituent_count, uint32_t tag, spci_memory_region_flags_t flags,
 	enum spci_memory_access access, enum spci_memory_type type,
 	enum spci_memory_cacheability cacheability,
 	enum spci_memory_shareability shareability)
@@ -67,7 +67,7 @@
 	spci_set_memory_shareability_attr(&attributes, shareability);
 
 	memory_region->tag = tag;
-	memory_region->flags = 0;
+	memory_region->flags = flags;
 	memory_region->page_count = 0;
 	memory_region->constituent_count = constituent_count;
 	memory_region->attribute_count = 1;
@@ -107,7 +107,7 @@
 uint32_t spci_memory_init(
 	void *message, enum spci_memory_share share_type, spci_vm_id_t receiver,
 	struct spci_memory_region_constituent *region_constituents,
-	uint32_t constituent_count, uint32_t tag,
+	uint32_t constituent_count, uint32_t tag, spci_memory_region_flags_t flags,
 	enum spci_memory_access access, enum spci_memory_type type,
 	enum spci_memory_cacheability cacheability,
 	enum spci_memory_shareability shareability)
@@ -123,6 +123,6 @@
 	/* Fill in memory region. */
 	message_length += spci_memory_region_init(
 		memory_region, receiver, region_constituents, constituent_count,
-		tag, access, type, cacheability, shareability);
+		tag, flags, access, type, cacheability, shareability);
 	return message_length;
 }