Allow messages to be sent between all VMs.

Any VM can send a message to any other VM. The hypervisor acts as a
router to deliver the message to the destination but does not track
higher level state about the communications.

When receiving a message, the source VM is reported so access controls
can be applies and replies can be sent back.

Bug: 116705004
Change-Id: Ib83988eb8ddee1753dfd67a3baa3fb991ebd4dd7
diff --git a/inc/hf/api.h b/inc/hf/api.h
index a936558..35aba8d 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -8,10 +8,9 @@
 int64_t api_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx, struct vcpu **next);
 int64_t api_vm_configure(ipaddr_t send, ipaddr_t recv);
 
-int64_t api_rpc_request(uint32_t vm_id, size_t size);
-int64_t api_rpc_read_request(bool block, struct vcpu **next);
-int64_t api_rpc_reply(size_t size, bool ack, struct vcpu **next);
-int64_t api_rpc_ack(void);
+int64_t api_mailbox_send(uint32_t vm_id, size_t size, struct vcpu **next);
+int64_t api_mailbox_receive(bool block, struct vcpu **next);
+int64_t api_mailbox_clear(void);
 
 struct vcpu *api_wait_for_interrupt(void);
 struct vcpu *api_yield(void);
diff --git a/inc/hf/cpu.h b/inc/hf/cpu.h
index b263e39..d598296 100644
--- a/inc/hf/cpu.h
+++ b/inc/hf/cpu.h
@@ -10,10 +10,19 @@
 #include "hf/spinlock.h"
 
 enum vcpu_state {
+	/* The vcpu is switched off. */
 	vcpu_state_off,
+
+	/* The vcpu is ready to be run. */
 	vcpu_state_ready,
+
+	/* The vcpu is currently running. */
 	vcpu_state_running,
-	vcpu_state_blocked_rpc,
+
+	/* The vcpu is waiting for a message. */
+	vcpu_state_blocked_mailbox,
+
+	/* The vcpu is waiting for an interrupt. */
 	vcpu_state_blocked_interrupt,
 };
 
@@ -21,7 +30,7 @@
 	struct spinlock lock;
 	enum vcpu_state state;
 	struct vm *vm;
-	struct vcpu *rpc_next;
+	struct vcpu *mailbox_next;
 	struct arch_regs regs;
 };
 
diff --git a/inc/hf/vm.h b/inc/hf/vm.h
index 3d7bf2e..64e4ee0 100644
--- a/inc/hf/vm.h
+++ b/inc/hf/vm.h
@@ -3,14 +3,20 @@
 #include "hf/cpu.h"
 #include "hf/mm.h"
 
-enum rpc_state {
-	rpc_state_idle,
-	rpc_state_pending,
-	rpc_state_inflight,
+enum mailbox_state {
+	/* There is no message in the mailbox. */
+	mailbox_state_empty,
+
+	/* There is a message in the mailbox that is waiting for a reader. */
+	mailbox_state_received,
+
+	/* There is a message in the mailbox that has been read. */
+	mailbox_state_read,
 };
 
-struct rpc {
-	enum rpc_state state;
+struct mailbox {
+	enum mailbox_state state;
+	uint32_t recv_from_id;
 	int16_t recv_bytes;
 	void *recv;
 	const void *send;
@@ -23,7 +29,7 @@
 	uint32_t vcpu_count;
 	struct vcpu vcpus[MAX_CPUS];
 	struct mm_ptable ptable;
-	struct rpc rpc;
+	struct mailbox mailbox;
 };
 
 bool vm_init(uint32_t vcpu_count, struct vm **new_vm);
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 0650868..4f6e05b 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -24,10 +24,9 @@
 #define HF_VM_GET_COUNT     0xff01
 #define HF_VCPU_GET_COUNT   0xff02
 #define HF_VM_CONFIGURE     0xff03
-#define HF_RPC_REQUEST      0xff04
-#define HF_RPC_READ_REQUEST 0xff05
-#define HF_RPC_ACK          0xff06
-#define HF_RPC_REPLY        0xff07
+#define HF_MAILBOX_SEND     0xff04
+#define HF_MAILBOX_RECEIVE  0xff05
+#define HF_MAILBOX_CLEAR    0xff06
 
 /* The ID of the primary VM which is responsile for scheduling. */
 #define HF_PRIMARY_VM_ID 0
@@ -36,14 +35,26 @@
 #define HF_VCPU_RUN_YIELD              0x00
 #define HF_VCPU_RUN_WAIT_FOR_INTERRUPT 0x01
 #define HF_VCPU_RUN_WAKE_UP            0x02
-#define HF_VCPU_RUN_RESPONSE_READY     0x03
+#define HF_VCPU_RUN_MESSAGE            0x03
 
 /* Construct and destruct the hf_vcpu_run() response. */
-#define HF_VCPU_RUN_RESPONSE(code, data) ((code & 0xff) | (data << 8))
+#define HF_VCPU_RUN_RESPONSE(code, vm_id, data)               \
+	((code & 0xff) | ((uint64_t)(vm_id & 0xffff) << 16) | \
+	 ((uint64_t)data << 32))
 #define HF_VCPU_RUN_CODE(ret) (ret & 0xff)
-#define HF_VCPU_RUN_DATA(ret) (ret >> 8)
+#define HF_VCPU_RUN_VM_ID(ret) ((ret >> 16) & 0xffff)
+#define HF_VCPU_RUN_DATA(ret) (ret >> 32)
 
-#define HF_RPC_REQUEST_MAX_SIZE 4096
+/* Construct and destruct the hf_mailbox_receive() response. */
+#define HF_MAILBOX_RECEIVE_RESPONSE(vm_id, size) \
+	((vm_id & 0xffff) | ((uint64_t)size << 32))
+#define HF_MAILBOX_RECEIVE_VM_ID(ret) (ret & 0xffff)
+#define HF_MAILBOX_RECEIVE_SIZE(ret) (ret >> 32)
+
+#define HF_MAILBOX_SIZE 4096
+
+#define HF_INVALID_VM_ID 0xffff
+#define HF_INVALID_VCPU  0xffff
 
 /* clang-format on */
 
@@ -87,45 +98,28 @@
 }
 
 /**
- * Called by the primary VM to send an RPC request to a secondary VM. Data is
- * copied from the caller's send buffer to the destination's receive buffer.
+ * Copies data from the sender's send buffer to the recipient's receive buffer.
  */
-static inline int64_t hf_rpc_request(uint32_t vm_id, size_t size)
+static inline int64_t hf_mailbox_send(uint32_t vm_id, size_t size)
 {
-	return hf_call(HF_RPC_REQUEST, vm_id, size, 0);
+	return hf_call(HF_MAILBOX_SEND, vm_id, size, 0);
 }
 
 /**
- * Called by the primary VM to read a request sent from a previous call to
- * api_rpc_request. If one isn't available, this function can optionally block
- * the caller until one becomes available.
+ * Called by secondary VMs to receive a message. The call can optionally block
+ * until a message is received.
  *
- * Once the caller has completed handling a request, it must indicate it by
- * either calling api_rpc_reply or api_rpc_ack. No new requests can be accepted
- * until the current one is acknowledged.
+ * The mailbox must be cleared before a new message can be received.
  */
-static inline int64_t hf_rpc_read_request(bool block)
+static inline int64_t hf_mailbox_receive(bool block)
 {
-	return hf_call(HF_RPC_READ_REQUEST, block, 0, 0);
+	return hf_call(HF_MAILBOX_RECEIVE, block, 0, 0);
 }
 
 /**
- * Acknowledges that either a request or a reply has been received and handled.
- * After this call completes, the caller will be able to receive additional
- * requests or replies.
+ * Clears the mailbox so a new message can be received.
  */
-static inline int64_t hf_rpc_ack(void)
+static inline int64_t hf_mailbox_clear(void)
 {
-	return hf_call(HF_RPC_ACK, 0, 0, 0);
-}
-
-/**
- * Called by a secondary VM to send a reply to the primary VM. Data is copied
- * from the caller's send buffer to the destination's receive buffer.
- *
- * It can optionally acknowledge the pending request.
- */
-static inline int64_t hf_rpc_reply(size_t size, bool ack)
-{
-	return hf_call(HF_RPC_REPLY, size, ack, 0);
+	return hf_call(HF_MAILBOX_CLEAR, 0, 0, 0);
 }