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/inc/hf/api.h b/inc/hf/api.h
index 43497bf..af21d00 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -52,9 +52,14 @@
 			     spci_vcpu_index_t target_vcpu_idx, uint32_t intid,
 			     struct vcpu *current, struct vcpu **next);
 
-int32_t api_spci_msg_send(uint32_t attributes, struct vcpu *current,
-			  struct vcpu **next);
+spci_return_t api_spci_msg_send(uint32_t attributes, struct vcpu *current,
+				struct vcpu **next);
 int32_t api_spci_msg_recv(uint32_t attributes, struct vcpu *current,
 			  struct vcpu **next);
 int32_t api_spci_yield(struct vcpu *current, struct vcpu **next);
 int32_t api_spci_version(void);
+spci_return_t api_spci_share_memory(struct vm_locked to_locked,
+				    struct vm_locked from_locked,
+				    struct spci_memory_region *memory_region,
+				    uint32_t memory_to_attributes,
+				    enum spci_memory_share share);
diff --git a/inc/hf/spci_internal.h b/inc/hf/spci_internal.h
index 441b424..78477a3 100644
--- a/inc/hf/spci_internal.h
+++ b/inc/hf/spci_internal.h
@@ -16,7 +16,31 @@
 
 #pragma once
 
+#include "hf/addr.h"
+#include "hf/vm.h"
+
+#include "vmapi/hf/spci.h"
+
 #define SPCI_VERSION_MAJOR 0x0
 #define SPCI_VERSION_MINOR 0x9
 
 #define SPCI_VERSION_MAJOR_OFFSET 16
+
+struct spci_mem_transitions {
+	int orig_from_mode;
+	int orig_to_mode;
+	int from_mode;
+	int to_mode;
+};
+
+spci_return_t spci_msg_handle_architected_message(
+	struct vm_locked to_locked, struct vm_locked from_locked,
+	const struct spci_architected_message_header
+		*architected_message_replica,
+	struct spci_message *from_msg_replica, struct spci_message *to_msg);
+
+bool spci_msg_check_transition(struct vm *to, struct vm *from,
+			       enum spci_memory_share share,
+			       int *orig_from_mode, ipaddr_t begin,
+			       ipaddr_t end, uint32_t memory_to_attributes,
+			       int *from_mode, int *to_mode);
diff --git a/inc/hf/vm.h b/inc/hf/vm.h
index c86da21..8930c11 100644
--- a/inc/hf/vm.h
+++ b/inc/hf/vm.h
@@ -24,7 +24,8 @@
 #include "hf/list.h"
 #include "hf/mm.h"
 #include "hf/mpool.h"
-#include "hf/spci.h"
+
+#include "vmapi/hf/spci.h"
 
 enum mailbox_state {
 	/** There is no message in the mailbox. */
@@ -97,10 +98,17 @@
 	struct vm *vm;
 };
 
+/** Container for two vm_locked structures */
+struct two_vm_locked {
+	struct vm_locked vm1;
+	struct vm_locked vm2;
+};
+
 bool vm_init(spci_vcpu_count_t vcpu_count, struct mpool *ppool,
 	     struct vm **new_vm);
 spci_vm_count_t vm_get_count(void);
 struct vm *vm_find(spci_vm_id_t id);
 struct vm_locked vm_lock(struct vm *vm);
+struct two_vm_locked vm_lock_both(struct vm *vm1, struct vm *vm2);
 void vm_unlock(struct vm_locked *locked);
 struct vcpu *vm_get_vcpu(struct vm *vm, spci_vcpu_index_t vcpu_index);
diff --git a/inc/vmapi/hf/spci.h b/inc/vmapi/hf/spci.h
index e55ea97..cad5e35 100644
--- a/inc/vmapi/hf/spci.h
+++ b/inc/vmapi/hf/spci.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "hf/types.h"
-
 #pragma once
 
+#include "hf/types.h"
+
 /* clang-format off */
 
 #define SPCI_LOW_32_ID  0x84000060
@@ -46,10 +46,17 @@
 /* TODO: return code currently undefined in SPCI alpha2. */
 #define SPCI_RETRY              INT32_C(-7)
 
+/* Architected memory sharing message IDs. */
+enum spci_memory_share {
+	SPCI_MEMORY_DONATE = 0x2,
+};
+
 /* SPCI function specific constants. */
 #define SPCI_MSG_RECV_BLOCK_MASK  0x1
 #define SPCI_MSG_SEND_NOTIFY_MASK 0x1
 
+#define SPCI_MESSAGE_ARCHITECTED 0x0
+#define SPCI_MESSAGE_IMPDEF      0x1
 #define SPCI_MESSAGE_IMPDEF_MASK 0x1
 
 #define SPCI_MSG_SEND_NOTIFY 0x1
@@ -62,6 +69,7 @@
 
 /** The ID of a VM. These are assigned sequentially. */
 typedef uint16_t spci_vm_id_t;
+typedef uint32_t spci_memory_handle_t;
 
 /**
  * A count of VMs. This has the same range as the VM IDs but we give it a
@@ -78,6 +86,10 @@
  */
 typedef spci_vcpu_index_t spci_vcpu_count_t;
 
+/** Return type of SPCI functions. */
+/* TODO: Reuse spci_return_t type on all SPCI functions declarations. */
+typedef int32_t spci_return_t;
+
 /** SPCI common message header. */
 struct spci_message {
 	/*
@@ -115,15 +127,41 @@
 	uint8_t payload[];
 };
 
+struct spci_architected_message_header {
+	uint16_t type;
+
+	/*
+	 * TODO: Padding is present to ensure that the field
+	 * payload is aligned on a 64B boundary. SPCI
+	 * spec must be updated to reflect this.
+	 */
+	uint16_t reserved[3];
+	uint8_t payload[];
+};
+
+struct spci_memory_region_constituent {
+	uint64_t address;
+	uint32_t page_count;
+
+	uint32_t reserved;
+};
+
+struct spci_memory_region {
+	spci_memory_handle_t handle;
+	uint32_t count;
+
+	struct spci_memory_region_constituent constituents[];
+};
+
+/* TODO: Move all the functions below this line to a support library. */
 /**
- * Set the SPCI common message header fields.
+ * Fill all the fields, except for the flags, in the SPCI message common header.
  */
-static inline void spci_message_init(struct spci_message *message,
-				     uint32_t message_length,
-				     spci_vm_id_t target_vm_id,
-				     spci_vm_id_t source_vm_id)
+static inline void spci_common_header_init(struct spci_message *message,
+					   uint32_t message_length,
+					   spci_vm_id_t target_vm_id,
+					   spci_vm_id_t source_vm_id)
 {
-	message->flags = SPCI_MESSAGE_IMPDEF_MASK;
 	message->length = message_length;
 	message->target_vm_id = target_vm_id;
 	message->source_vm_id = source_vm_id;
@@ -135,3 +173,118 @@
 	message->reserved_1 = 0;
 	message->reserved_2 = 0;
 }
+
+/**
+ * Set the SPCI implementation defined message header fields.
+ */
+static inline void spci_message_init(struct spci_message *message,
+				     uint32_t message_length,
+				     spci_vm_id_t target_vm_id,
+				     spci_vm_id_t source_vm_id)
+{
+	spci_common_header_init(message, message_length, target_vm_id,
+				source_vm_id);
+
+	message->flags = SPCI_MESSAGE_IMPDEF;
+}
+
+/**
+ * Obtain a pointer to the architected header in the spci_message.
+ *
+ * Note: the argument "message" has const qualifier. This qualifier
+ * is meant to forbid changes in information enclosed in the
+ * struct spci_message. The spci_architected_message_header, for which
+ * a pointer is returned in this function, is not part of spci_message.
+ * Its information is meant to be changed and hence the returned pointer
+ * does not have const type qualifier.
+ */
+static inline struct spci_architected_message_header *
+spci_get_architected_message_header(const struct spci_message *message)
+{
+	return (struct spci_architected_message_header *)message->payload;
+}
+
+/**
+ * Helper method to fill in the information about the architected message.
+ */
+static inline void spci_architected_message_init(struct spci_message *message,
+						 uint32_t message_length,
+						 spci_vm_id_t target_vm_id,
+						 spci_vm_id_t source_vm_id,
+						 enum spci_memory_share type)
+{
+	struct spci_architected_message_header *architected_header;
+
+	spci_common_header_init(message, message_length, target_vm_id,
+				source_vm_id);
+	message->flags = SPCI_MESSAGE_ARCHITECTED;
+
+	/* Fill the architected header. */
+	architected_header = spci_get_architected_message_header(message);
+	architected_header->type = type;
+	architected_header->reserved[0] = 0;
+	architected_header->reserved[1] = 0;
+	architected_header->reserved[2] = 0;
+}
+
+/** Obtain a pointer to the start of the memory region in the donate message. */
+static inline struct spci_memory_region *spci_get_donated_memory_region(
+	struct spci_message *message)
+{
+	struct spci_architected_message_header *architected_header =
+		spci_get_architected_message_header(message);
+	return (struct spci_memory_region *)architected_header->payload;
+}
+
+/**
+ * Add a memory region to the current message.
+ * A memory region is composed of one or more constituents.
+ */
+static inline void spci_memory_region_add(
+	struct spci_message *message, spci_memory_handle_t handle,
+	const struct spci_memory_region_constituent constituents[],
+	uint32_t num_constituents)
+{
+	struct spci_memory_region *memory_region =
+		spci_get_donated_memory_region(message);
+
+	uint32_t constituents_length =
+		num_constituents *
+		sizeof(struct spci_memory_region_constituent);
+	uint32_t index;
+
+	memory_region->handle = handle;
+	memory_region->count = num_constituents;
+
+	for (index = 0; index < num_constituents; index++) {
+		memory_region->constituents[index] = constituents[index];
+		memory_region->constituents[index].reserved = 0;
+	}
+
+	/*
+	 * TODO: Add assert ensuring that the specified message
+	 * length is not greater than SPCI_MSG_PAYLOAD_MAX.
+	 */
+	message->length +=
+		sizeof(struct spci_memory_region) + constituents_length;
+}
+
+/** Construct the SPCI donate memory region message. */
+static inline void spci_memory_donate(
+	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,
+	uint32_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_DONATE);
+
+	/* Create single memory region. */
+	spci_memory_region_add(message, handle, region_constituents,
+			       num_elements);
+}