SPCI: memory relinquish.

The relinquish mechnism implementation accepts a single memory region
with a single constituent.
Multiple memory region and constituent support will be introduced in
a later commit.

Change-Id: I9588ae597fd56336da135522b74e4a42e4514042
diff --git a/inc/vmapi/hf/spci.h b/inc/vmapi/hf/spci.h
index 99664ed..a6d9c68 100644
--- a/inc/vmapi/hf/spci.h
+++ b/inc/vmapi/hf/spci.h
@@ -48,6 +48,7 @@
 
 /* Architected memory sharing message IDs. */
 enum spci_memory_share {
+	SPCI_MEMORY_RELINQUISH = 0x1,
 	SPCI_MEMORY_DONATE = 0x2,
 };
 
@@ -288,3 +289,26 @@
 	spci_memory_region_add(message, handle, region_constituents,
 			       num_elements);
 }
+
+/**
+ * Construct the SPCI memory region relinquish message.
+ * A set of memory regions can be given back to the owner.
+ */
+static inline void spci_memory_relinquish(
+	struct spci_message *message, spci_vm_id_t target_vm_id,
+	spci_vm_id_t source_vm_id,
+	struct spci_memory_region_constituent *region_constituents,
+	uint64_t num_elements, uint32_t handle)
+{
+	int32_t message_length;
+
+	message_length = sizeof(struct spci_architected_message_header);
+
+	/* Fill in the details on the common message header. */
+	spci_architected_message_init(message, message_length, target_vm_id,
+				      source_vm_id, SPCI_MEMORY_RELINQUISH);
+
+	/* Create single memory region. */
+	spci_memory_region_add(message, handle, region_constituents,
+			       num_elements);
+}
diff --git a/src/spci_architected_message.c b/src/spci_architected_message.c
index 0905531..0b47fb3 100644
--- a/src/spci_architected_message.c
+++ b/src/spci_architected_message.c
@@ -81,6 +81,23 @@
 			memory_share_size, to_mode, message_type);
 		break;
 
+	case SPCI_MEMORY_RELINQUISH:
+
+		memory_region = (struct spci_memory_region *)
+					architected_message_replica->payload;
+
+		memory_share_size =
+			from_msg_replica->length -
+			sizeof(struct spci_architected_message_header);
+
+		to_mode = MM_MODE_R | MM_MODE_W | MM_MODE_X;
+
+		ret = spci_validate_call_share_memory(
+			to_locked, from_locked, memory_region,
+			memory_share_size, to_mode, message_type);
+
+		break;
+
 	default:
 		dlog("Invalid memory sharing message.\n");
 		return SPCI_INVALID_PARAMETERS;
@@ -208,6 +225,30 @@
 			.to_mode = 0,
 		},
 	};
+
+	static const struct spci_mem_transitions relinquish_transitions[] = {
+		{
+			/* 1) {!O-EA, O-NA} -> {!O-NA, O-EA} */
+			.orig_from_mode = MM_MODE_UNOWNED,
+			.orig_to_mode = MM_MODE_INVALID,
+			.from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
+				     MM_MODE_SHARED,
+			.to_mode = 0,
+		},
+		{
+			/* 2) {!O-SA, O-SA} -> {!O-NA, O-EA} */
+			.orig_from_mode = MM_MODE_UNOWNED | MM_MODE_SHARED,
+			.orig_to_mode = MM_MODE_SHARED,
+			.from_mode = MM_MODE_INVALID | MM_MODE_UNOWNED |
+				     MM_MODE_SHARED,
+			.to_mode = 0,
+		},
+	};
+
+	static const uint32_t size_relinquish_transitions =
+		sizeof(relinquish_transitions) /
+		sizeof(struct spci_mem_transitions);
+
 	static const uint32_t size_donate_transitions =
 		ARRAY_SIZE(donate_transitions);
 
@@ -232,6 +273,11 @@
 		transition_table_size = size_donate_transitions;
 		break;
 
+	case SPCI_MEMORY_RELINQUISH:
+		mem_transition_table = relinquish_transitions;
+		transition_table_size = size_relinquish_transitions;
+		break;
+
 	default:
 		return false;
 	}