Export policy for selecting message recipient.

Previously this was a first come first served policy but the scheduler
may be in the position to make a better choice. This also conforms to
our intent of exporting policy outside of the hypervisor.

Change-Id: I8cee6ce9b976e5ed990616c896cd53ecd0f083c8
diff --git a/test/hftest/inc/hftest_impl.h b/test/hftest/inc/hftest_impl.h
index 093ecf0..5778b20 100644
--- a/test/hftest/inc/hftest_impl.h
+++ b/test/hftest/inc/hftest_impl.h
@@ -277,7 +277,8 @@
 		 * message.                                                   \
 		 */                                                           \
 		run_res = hf_vcpu_run(vm_id, 0);                              \
-		ASSERT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);      \
+		ASSERT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);        \
+		ASSERT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);             \
                                                                               \
 		/* Send the selected service to run and let it be handled. */ \
 		memcpy(send_buffer, service, strlen(service));                \
diff --git a/test/vmapi/gicv3/busy_secondary.c b/test/vmapi/gicv3/busy_secondary.c
index 3d6d176..9ea691f 100644
--- a/test/vmapi/gicv3/busy_secondary.c
+++ b/test/vmapi/gicv3/busy_secondary.c
@@ -36,15 +36,8 @@
 
 SET_UP(busy_secondary)
 {
-	struct hf_vcpu_run_return run_res;
-
 	system_setup();
-
-	/* Configure mailbox pages. */
 	EXPECT_EQ(hf_vm_configure(send_page_addr, recv_page_addr), 0);
-	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
-
 	SERVICE_SELECT(SERVICE_VM0, "busy", send_page);
 }
 
@@ -69,7 +62,8 @@
 
 	/* Let the secondary get started and wait for our message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Check that no interrupts are active or pending to start with. */
 	EXPECT_EQ(GICD_ISPENDR(0), 0);
@@ -123,7 +117,8 @@
 
 	/* Let the secondary get started and wait for our message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Check that no interrupts are active or pending to start with. */
 	EXPECT_EQ(GICD_ISPENDR(0), 0);
diff --git a/test/vmapi/gicv3/services/timer.c b/test/vmapi/gicv3/services/timer.c
index 16ccccb..029c729 100644
--- a/test/vmapi/gicv3/services/timer.c
+++ b/test/vmapi/gicv3/services/timer.c
@@ -59,72 +59,74 @@
 	arch_irq_enable();
 
 	for (;;) {
-		const char timer_wfi_message[] = "WFI  xxxxxxx";
-		const char timer_wfe_message[] = "WFE  xxxxxxx";
-		const char timer_receive_message[] = "RECV xxxxxxx";
+		const char timer_wfi_message[] = "**** xxxxxxx";
+		char *message = SERVICE_RECV_BUFFER();
+		bool wfi, wfe, receive;
+		bool disable_interrupts;
+		uint32_t ticks;
 		struct hf_mailbox_receive_return received_message =
 			mailbox_receive_retry();
-		if (received_message.vm_id == HF_PRIMARY_VM_ID &&
-		    received_message.size == sizeof(timer_wfi_message)) {
-			/*
-			 * Start a timer to send the message back: enable it and
-			 * set it for the requested number of ticks.
-			 */
-			char *message = SERVICE_RECV_BUFFER();
-			bool wfi = memcmp(message, timer_wfi_message, 5) == 0;
-			bool wfe = memcmp(message, timer_wfe_message, 5) == 0;
-			bool receive =
-				memcmp(message, timer_receive_message, 5) == 0;
-			int32_t ticks = (message[5] - '0') * 1000000 +
-					(message[6] - '0') * 100000 +
-					(message[7] - '0') * 10000 +
-					(message[8] - '0') * 1000 +
-					(message[9] - '0') * 100 +
-					(message[10] - '0') * 10 +
-					(message[11] - '0');
-			dlog("Starting timer for %d ticks.\n", ticks);
-			if (wfi || receive) {
-				arch_irq_disable();
-			}
-			timer_set(ticks);
-			timer_start();
-			dlog("Waiting for timer...\n");
-			if (wfi) {
-				/* WFI until the timer fires. */
-				interrupt_wait();
-				arch_irq_enable();
-			} else if (wfe) {
-				/* WFE until the timer fires. */
-				while (!timer_fired) {
-					event_wait();
-				}
-			} else if (receive) {
-				/*
-				 * Block on hf_mailbox_receive until timer
-				 * fires.
-				 */
-				struct hf_mailbox_receive_return received =
-					hf_mailbox_receive(true);
-				/*
-				 * Expect to be interrupted, not to actually
-				 * receive a message.
-				 */
-				EXPECT_EQ(received.vm_id, HF_INVALID_VM_ID);
-				EXPECT_EQ(received.size, 0);
-				arch_irq_enable();
-			} else {
-				/* Busy wait until the timer fires. */
-				while (!timer_fired) {
-				}
-			}
-			EXPECT_TRUE(timer_fired);
-			timer_fired = false;
-			dlog("Done waiting.\n");
-		} else {
-			dlog("Got unexpected message from VM %d, size %d.\n",
+
+		if (received_message.vm_id != HF_PRIMARY_VM_ID ||
+		    received_message.size != sizeof(timer_wfi_message)) {
+			FAIL("Got unexpected message from VM %d, size %d.\n",
 			     received_message.vm_id, received_message.size);
-			FAIL("Unexpected message");
 		}
+
+		/*
+		 * Start a timer to send the message back: enable it and
+		 * set it for the requested number of ticks.
+		 */
+		wfi = memcmp(message, "WFI ", 4) == 0;
+		wfe = memcmp(message, "WFE ", 4) == 0;
+		receive = memcmp(message, "RECV", 4) == 0;
+		disable_interrupts = wfi || receive;
+		ticks = (message[5] - '0') * 1000000 +
+			(message[6] - '0') * 100000 +
+			(message[7] - '0') * 10000 + (message[8] - '0') * 1000 +
+			(message[9] - '0') * 100 + (message[10] - '0') * 10 +
+			(message[11] - '0');
+
 		hf_mailbox_clear();
+
+		dlog("Starting timer for %d ticks.\n", ticks);
+
+		if (disable_interrupts) {
+			arch_irq_disable();
+		}
+
+		timer_set(ticks);
+		timer_start();
+		dlog("Waiting for timer...\n");
+
+		/* Wait for the timer interrupt. */
+		if (wfi) {
+			interrupt_wait();
+		} else if (wfe) {
+			while (!timer_fired) {
+				event_wait();
+			}
+		} else if (receive) {
+			struct hf_mailbox_receive_return received =
+				hf_mailbox_receive(true);
+			/*
+			 * Expect to be interrupted, not to actually
+			 * receive a message.
+			 */
+			EXPECT_EQ(received.vm_id, HF_INVALID_VM_ID);
+			EXPECT_EQ(received.size, 0);
+		} else {
+			/* Busy wait until the timer fires. */
+			while (!timer_fired) {
+			}
+		}
+
+		if (disable_interrupts) {
+			arch_irq_enable();
+		}
+
+		EXPECT_TRUE(timer_fired);
+		timer_fired = false;
+		dlog("Done waiting.\n");
 	}
 }
diff --git a/test/vmapi/gicv3/timer_secondary.c b/test/vmapi/gicv3/timer_secondary.c
index 9baa24e..dcb2e87 100644
--- a/test/vmapi/gicv3/timer_secondary.c
+++ b/test/vmapi/gicv3/timer_secondary.c
@@ -27,13 +27,7 @@
 {
 	system_setup();
 
-	struct hf_vcpu_run_return run_res;
-
-	/* Configure mailbox pages. */
 	EXPECT_EQ(hf_vm_configure(send_page_addr, recv_page_addr), 0);
-	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
-
 	SERVICE_SELECT(SERVICE_VM0, "timer", send_page);
 
 	interrupt_enable(VIRTUAL_TIMER_IRQ, true);
@@ -42,7 +36,7 @@
 	arch_irq_enable();
 }
 
-void timer_busywait_secondary()
+static void timer_busywait_secondary()
 {
 	const char message[] = "loop 0099999";
 	const char expected_response[] = "Got IRQ 03.";
@@ -50,7 +44,8 @@
 
 	/* Let the secondary get started and wait for our message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send the message for the secondary to set a timer. */
 	memcpy(send_page, message, sizeof(message));
@@ -96,7 +91,8 @@
 	timer_busywait_secondary();
 }
 
-void timer_wfi_secondary(const char message[], bool wfe)
+static void timer_secondary(const char message[],
+			    enum hf_vcpu_run_code expected_code)
 {
 	const char expected_response[] = "Got IRQ 03.";
 	size_t message_length = strlen(message) + 1;
@@ -104,7 +100,8 @@
 
 	/* Let the secondary get started and wait for our message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send the message for the secondary to set a timer. */
 	memcpy(send_page, message, message_length);
@@ -112,53 +109,44 @@
 
 	/*
 	 * Let the secondary handle the message and set the timer. Then there's
-	 * a race for whether it manages to WFI before the hardware timer fires,
-	 * so we need to handle both cases.
+	 * a race for whether it manages to block and switch to the primary
+	 * before the hardware timer fires, so we need to handle both cases.
 	 */
 	last_interrupt_id = 0;
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	if (run_res.code == HF_VCPU_RUN_SLEEP && !wfe) {
+	if (run_res.code == expected_code) {
 		/*
-		 * This case happens if the secondary manages to call WFI before
-		 * the timer fires. This is likely when the timer is set for a
-		 * long time.
+		 * This case happens if the secondary manages to block and
+		 * switch to the primary before the timer fires.
 		 */
 		dlog("secondary sleeping after receiving timer message\n");
 		/* Loop until the timer fires. */
-		while (run_res.code == HF_VCPU_RUN_SLEEP) {
-			dlog("Primary looping until timer fires; %d ns "
-			     "remaining\n",
-			     run_res.sleep.ns);
-			run_res = hf_vcpu_run(SERVICE_VM0, 0);
-		}
-		dlog("Primary done looping\n");
-	} else if (run_res.code == HF_VCPU_RUN_YIELD && wfe) {
-		/*
-		 * This case happens if the secondary manages to call WFE before
-		 * the timer fires. This is likely when the timer is set for a
-		 * long time.
-		 */
-		dlog("secondary yielding after receiving timer message\n");
-		/* Loop until the timer fires. */
-		while (run_res.code == HF_VCPU_RUN_YIELD) {
+		while (run_res.code == expected_code) {
 			dlog("Primary looping until timer fires\n");
+			if (expected_code == HF_VCPU_RUN_WAIT_FOR_INTERRUPT ||
+			    expected_code == HF_VCPU_RUN_WAIT_FOR_MESSAGE) {
+				EXPECT_NE(run_res.sleep.ns,
+					  HF_SLEEP_INDEFINITE);
+				dlog("%d ns remaining\n", run_res.sleep.ns);
+			}
 			run_res = hf_vcpu_run(SERVICE_VM0, 0);
 		}
 		dlog("Primary done looping\n");
 	} else if (run_res.code == HF_VCPU_RUN_PREEMPTED) {
 		/*
 		 * This case happens if the (hardware) timer fires before the
-		 * secondary calls WFI. Then we get the interrupt to the
-		 * primary, ignore it, and see a HF_VCPU_RUN_PREEMPTED code from
-		 * the hf_vcpu_run call, so we should call it again for the
-		 * timer interrupt to be injected automatically by Hafnium.
+		 * secondary blocks and switches to the primary. Then we get the
+		 * interrupt to the primary, ignore it, and see a
+		 * HF_VCPU_RUN_PREEMPTED code from the hf_vcpu_run call, so we
+		 * should call it again for the timer interrupt to be injected
+		 * automatically by Hafnium.
 		 */
 		EXPECT_EQ(last_interrupt_id, VIRTUAL_TIMER_IRQ);
-		dlog("Primary yielded, running again\n");
+		dlog("Preempted by timer interrupt, running again\n");
 		run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	} else {
 		/* No other return codes should occur here, so fail. */
-		FAIL("Unexpected run result code.");
+		FAIL("Unexpected run result code (%d).", run_res.code);
 	}
 
 	/* Once we wake it up it should get the timer interrupt and respond. */
@@ -174,7 +162,8 @@
  * Send a message to the interruptible VM, which will start a timer to interrupt
  * itself to send a response back. This test is run with both long and short
  * timer lengths, to try to cover both cases of the race for whether the timer
- * fires before or after the WFI in the secondary VM.
+ * fires before or after the secondary VM blocks and switches back to the
+ * primary.
  */
 TEST(timer_secondary, wfi_short)
 {
@@ -182,8 +171,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("WFI  0000001", false);
-	timer_wfi_secondary("WFI  0000001", false);
+	timer_secondary("WFI  0000001", HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	timer_secondary("WFI  0000001", HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
 }
 
 TEST(timer_secondary, wfi_long)
@@ -192,8 +181,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("WFI  0099999", false);
-	timer_wfi_secondary("WFI  0099999", false);
+	timer_secondary("WFI  0099999", HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	timer_secondary("WFI  0099999", HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
 }
 
 TEST(timer_secondary, wfe_short)
@@ -202,8 +191,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("WFE  0000001", true);
-	timer_wfi_secondary("WFE  0000001", true);
+	timer_secondary("WFE  0000001", HF_VCPU_RUN_YIELD);
+	timer_secondary("WFE  0000001", HF_VCPU_RUN_YIELD);
 }
 
 TEST(timer_secondary, wfe_long)
@@ -212,8 +201,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("WFE  0099999", true);
-	timer_wfi_secondary("WFE  0099999", true);
+	timer_secondary("WFE  0099999", HF_VCPU_RUN_YIELD);
+	timer_secondary("WFE  0099999", HF_VCPU_RUN_YIELD);
 }
 
 TEST(timer_secondary, receive_short)
@@ -222,8 +211,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("RECV 0000001", false);
-	timer_wfi_secondary("RECV 0000001", false);
+	timer_secondary("RECV 0000001", HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	timer_secondary("RECV 0000001", HF_VCPU_RUN_WAIT_FOR_MESSAGE);
 }
 
 TEST(timer_secondary, receive_long)
@@ -232,8 +221,8 @@
 	 * Run the test twice in a row, to check that the state doesn't get
 	 * messed up.
 	 */
-	timer_wfi_secondary("RECV 0099999", false);
-	timer_wfi_secondary("RECV 0099999", false);
+	timer_secondary("RECV 0099999", HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	timer_secondary("RECV 0099999", HF_VCPU_RUN_WAIT_FOR_MESSAGE);
 }
 
 /**
@@ -247,7 +236,8 @@
 
 	/* Let the secondary get started and wait for our message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send the message for the secondary to set a timer. */
 	memcpy(send_page, message, message_length);
@@ -259,7 +249,7 @@
 	last_interrupt_id = 0;
 	for (int i = 0; i < 20; ++i) {
 		run_res = hf_vcpu_run(SERVICE_VM0, 0);
-		EXPECT_EQ(run_res.code, HF_VCPU_RUN_SLEEP);
+		EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
 		dlog("Primary looping until timer fires; %d ns "
 		     "remaining\n",
 		     run_res.sleep.ns);
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index b5e45a4..df19d32 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -74,6 +74,7 @@
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(HF_PRIMARY_VM_ID, 0);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
@@ -84,6 +85,7 @@
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(1, 0);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
diff --git a/test/vmapi/primary_with_secondaries/interrupts.c b/test/vmapi/primary_with_secondaries/interrupts.c
index 09f8719..7791c64 100644
--- a/test/vmapi/primary_with_secondaries/interrupts.c
+++ b/test/vmapi/primary_with_secondaries/interrupts.c
@@ -37,7 +37,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Set the message, echo it and wait for a response. */
 	memcpy(mb.send, message, sizeof(message));
@@ -64,7 +65,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Inject the interrupt and wait for a message. */
 	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
@@ -99,7 +101,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Inject the interrupt and wait for a message. */
 	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
@@ -137,7 +140,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "interruptible", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Inject the interrupt and wait for a message. */
 	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
@@ -149,7 +153,8 @@
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Now send a message to the secondary. */
 	memcpy(mb.send, message, sizeof(message));
@@ -180,7 +185,8 @@
 	/* Inject the interrupt and expect not to get a message. */
 	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_C);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/*
 	 * Now send a message to the secondary to enable the interrupt ID, and
@@ -249,3 +255,29 @@
 		  0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
+
+/*
+ * Deliver an interrupt and a message to the same vCPU and check that both are
+ * delivered the next time the vCPU is run.
+ */
+TEST(interrupts, deliver_interrupt_and_message)
+{
+	const char message[] = "I\'ll see you again.";
+	struct hf_vcpu_run_return run_res;
+	struct mailbox_buffers mb = set_up_mailbox();
+
+	SERVICE_SELECT(SERVICE_VM0, "interruptible_echo", mb.send);
+
+	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
+
+	memcpy(mb.send, message, sizeof(message));
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
+	hf_interrupt_inject(SERVICE_VM0, 0, EXTERNAL_INTERRUPT_ID_A);
+	run_res = hf_vcpu_run(SERVICE_VM0, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(message));
+	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+}
diff --git a/test/vmapi/primary_with_secondaries/mailbox.c b/test/vmapi/primary_with_secondaries/mailbox.c
index afb09eb..c3ee826 100644
--- a/test/vmapi/primary_with_secondaries/mailbox.c
+++ b/test/vmapi/primary_with_secondaries/mailbox.c
@@ -82,7 +82,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "echo", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Set the message, echo it and check it didn't change. */
 	memcpy(mb.send, message, sizeof(message));
@@ -109,7 +110,8 @@
 	for (i = 0; i < 100; i++) {
 		/* Run secondary until it reaches the wait for messages. */
 		run_res = hf_vcpu_run(SERVICE_VM0, 0);
-		EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+		EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+		EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 		/* Set the message, echo it and check it didn't change. */
 		next_permutation(message, sizeof(message) - 1);
@@ -138,9 +140,11 @@
 	SERVICE_SELECT(SERVICE_VM1, "relay", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 	run_res = hf_vcpu_run(SERVICE_VM1, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/*
 	 * Build the message chain so the message is sent from here to
@@ -160,15 +164,16 @@
 
 	/* Let SERVICE_VM0 forward the message. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAKE_UP);
-	EXPECT_EQ(run_res.wake_up.vm_id, SERVICE_VM1);
-	EXPECT_EQ(run_res.wake_up.vcpu, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.vm_id, SERVICE_VM1);
+	EXPECT_EQ(run_res.message.size, 0);
 
 	/* Let SERVICE_VM1 forward the message. */
 	run_res = hf_vcpu_run(SERVICE_VM1, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
 
 	/* Ensure the message is in tact. */
+	EXPECT_EQ(run_res.message.vm_id, HF_PRIMARY_VM_ID);
 	EXPECT_EQ(run_res.message.size, sizeof(message));
 	EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
@@ -187,7 +192,8 @@
 	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), -1);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
 }
@@ -215,8 +221,8 @@
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
 
-	/* Send should succeed now, though no vCPU is blocked waiting for it. */
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), HF_INVALID_VCPU);
+	/* Send should now succeed. */
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
 }
 
 /**
@@ -233,7 +239,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send a message to echo service, and get response back. */
 	memcpy(mb.send, message, sizeof(message));
@@ -245,7 +252,8 @@
 
 	/* Let secondary VM continue running so that it will wait again. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Without clearing our mailbox, send message again. */
 	reverse(message, strlen(message));
@@ -253,6 +261,7 @@
 	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(message), false), 0);
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Clear the mailbox. We expect to be told there are pending waiters. */
 	EXPECT_EQ(hf_mailbox_clear(), 1);
@@ -288,7 +297,8 @@
 	SERVICE_SELECT(SERVICE_VM0, "echo_with_notification", mb.send);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 
 	/* Send a message to echo service twice. The second should fail. */
 	memcpy(mb.send, message, sizeof(message));
@@ -309,6 +319,6 @@
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), HF_PRIMARY_VM_ID);
 	EXPECT_EQ(hf_mailbox_waiter_get(SERVICE_VM0), -1);
 
-	/* Send should succeed now, though no vCPU is blocked waiting for it. */
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), HF_INVALID_VCPU);
+	/* Send should now succeed. */
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, 0, false), 0);
 }
diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c
index 39106ee..d1adea5 100644
--- a/test/vmapi/primary_with_secondaries/memory_sharing.c
+++ b/test/vmapi/primary_with_secondaries/memory_sharing.c
@@ -104,8 +104,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
 	EXPECT_EQ(run_res.code, HF_VCPU_RUN_YIELD);
@@ -147,8 +146,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -185,8 +183,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -223,8 +220,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -260,8 +256,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
@@ -274,7 +269,8 @@
 
 	/* Observe the service doesn't fault when accessing the memory. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
-	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_MESSAGE);
+	EXPECT_EQ(run_res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
@@ -299,8 +295,7 @@
 	 *       explicitly to test the mechanism.
 	 */
 	memcpy(mb.send, &ptr, sizeof(ptr));
-	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false),
-		  HF_INVALID_VCPU);
+	EXPECT_EQ(hf_mailbox_send(SERVICE_VM0, sizeof(ptr), false), 0);
 
 	/* Let the memory be returned. */
 	run_res = hf_vcpu_run(SERVICE_VM0, 0);
diff --git a/test/vmapi/primary_with_secondaries/no_services.c b/test/vmapi/primary_with_secondaries/no_services.c
index a90da65..9d7f29b 100644
--- a/test/vmapi/primary_with_secondaries/no_services.c
+++ b/test/vmapi/primary_with_secondaries/no_services.c
@@ -75,6 +75,7 @@
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(HF_PRIMARY_VM_ID, 0);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
@@ -84,6 +85,7 @@
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(1234, 0);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
@@ -93,6 +95,7 @@
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(SERVICE_VM0, 1234);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+	EXPECT_EQ(res.sleep.ns, HF_SLEEP_INDEFINITE);
 }
 
 /**
diff --git a/test/vmapi/primary_with_secondaries/run_race.c b/test/vmapi/primary_with_secondaries/run_race.c
index 4230ba5..51c25a3 100644
--- a/test/vmapi/primary_with_secondaries/run_race.c
+++ b/test/vmapi/primary_with_secondaries/run_race.c
@@ -41,7 +41,8 @@
 		/* Run until it manages to schedule vCPU on this CPU. */
 		do {
 			run_res = hf_vcpu_run(SERVICE_VM0, 0);
-		} while (run_res.code == HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+		} while (run_res.code == HF_VCPU_RUN_WAIT_FOR_INTERRUPT &&
+			 run_res.sleep.ns == HF_SLEEP_INDEFINITE);
 
 		/* Break out if we received a message with non-zero length. */
 		if (run_res.code == HF_VCPU_RUN_MESSAGE &&
diff --git a/test/vmapi/primary_with_secondaries/services/BUILD.gn b/test/vmapi/primary_with_secondaries/services/BUILD.gn
index 75a797e..b0ef794 100644
--- a/test/vmapi/primary_with_secondaries/services/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/services/BUILD.gn
@@ -82,6 +82,7 @@
 
   sources = [
     "interruptible.c",
+    "interruptible_echo.c",
   ]
 
   deps = [
diff --git a/test/vmapi/primary_with_secondaries/services/interruptible_echo.c b/test/vmapi/primary_with_secondaries/services/interruptible_echo.c
new file mode 100644
index 0000000..e1eb643
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/services/interruptible_echo.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hf/arch/cpu.h"
+#include "hf/arch/std.h"
+#include "hf/arch/vm/interrupts_gicv3.h"
+
+#include "hf/dlog.h"
+
+#include "vmapi/hf/call.h"
+
+#include "hftest.h"
+#include "primary_with_secondary.h"
+
+static void irq(void)
+{
+	/* Clear the interrupt. */
+	hf_interrupt_get();
+}
+
+TEST_SERVICE(interruptible_echo)
+{
+	exception_setup(irq);
+	hf_interrupt_enable(EXTERNAL_INTERRUPT_ID_A, true);
+	arch_irq_enable();
+
+	for (;;) {
+		struct hf_mailbox_receive_return res = hf_mailbox_receive(true);
+
+		/* Retry if interrupted but made visible with the yield. */
+		while (res.vm_id == HF_INVALID_VM_ID && res.size == 0) {
+			hf_vcpu_yield();
+			res = hf_mailbox_receive(true);
+		}
+
+		memcpy(SERVICE_SEND_BUFFER(), SERVICE_RECV_BUFFER(), res.size);
+		hf_mailbox_clear();
+		hf_mailbox_send(res.vm_id, res.size, false);
+	}
+}