Add tests for injecting interrupts from the primary to a secondary.

Bug: 117270899
Change-Id: I5948ddb35ba3d8a7b4ebf0d58c95eb32fa0c40a3
diff --git a/build/image/image.gni b/build/image/image.gni
index 2f9da5b..02489f0 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -33,6 +33,8 @@
                              "cflags_c",
                              "defines",
                              "deps",
+                             "include_dirs",
+                             "public_configs",
                              "public_deps",
                              "sources",
                              "testonly",
@@ -99,6 +101,8 @@
                              "cflags_c",
                              "defines",
                              "deps",
+                             "include_dirs",
+                             "public_configs",
                              "public_deps",
                              "sources",
                              "testonly",
diff --git a/test/vmapi/BUILD.gn b/test/vmapi/BUILD.gn
index 7ad3834..da28503 100644
--- a/test/vmapi/BUILD.gn
+++ b/test/vmapi/BUILD.gn
@@ -15,6 +15,10 @@
 import("//build/image/image.gni")
 import("//build/toolchain/platform.gni")
 
+config("config") {
+  include_dirs = [ "inc" ]
+}
+
 group("vmapi") {
   testonly = true
 
@@ -27,6 +31,7 @@
 # Tests with no secondary VMs.
 vm_kernel("primary_only_test_vm") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "primary_only.c",
@@ -46,6 +51,7 @@
 # Tests specific to GICv3.
 vm_kernel("gicv3_test_vm") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "gicv3.c",
@@ -66,6 +72,7 @@
 # Tests with secondary VMs.
 vm_kernel("primary_with_secondaries_test_vm") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "primary_with_secondaries.c",
diff --git a/test/vmapi/inc/constants.h b/test/vmapi/inc/constants.h
new file mode 100644
index 0000000..1be98cd
--- /dev/null
+++ b/test/vmapi/inc/constants.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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.
+ */
+
+#define SELF_INTERRUPT_ID 5
+#define EXTERNAL_INTERRUPT_ID 7
+#define EXTERNAL_INTERRUPT_ID_B 8
diff --git a/test/vmapi/primary_with_secondaries.c b/test/vmapi/primary_with_secondaries.c
index c70f38e..b13ac6e 100644
--- a/test/vmapi/primary_with_secondaries.c
+++ b/test/vmapi/primary_with_secondaries.c
@@ -23,6 +23,7 @@
 
 #include "vmapi/hf/call.h"
 
+#include "constants.h"
 #include "hftest.h"
 
 static alignas(PAGE_SIZE) uint8_t send_page[PAGE_SIZE];
@@ -307,3 +308,115 @@
 		0);
 	EXPECT_EQ(hf_mailbox_clear(), 0);
 }
+
+/**
+ * Inject an interrupt to the interrupt VM, which will send a message back.
+ * Repeat this twice to make sure it doesn't get into a bad state after the
+ * first one.
+ */
+TEST(interrupts, inject_interrupt_twice)
+{
+	const char expected_response[] = "Got IRQ 07.";
+	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(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+
+	/* Inject the interrupt and wait for a message. */
+	hf_inject_interrupt(INTERRUPTIBLE_VM_ID, 0, EXTERNAL_INTERRUPT_ID);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
+	EXPECT_EQ(
+		memcmp(recv_page, expected_response, sizeof(expected_response)),
+		0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+
+	/* Inject the interrupt again, and wait for the same message. */
+	hf_inject_interrupt(INTERRUPTIBLE_VM_ID, 0, EXTERNAL_INTERRUPT_ID);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
+	EXPECT_EQ(
+		memcmp(recv_page, expected_response, sizeof(expected_response)),
+		0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+}
+
+/**
+ * Inject two different interrupts to the interrupt VM, which will send a
+ * message back each time.
+ */
+TEST(interrupts, inject_two_interrupts)
+{
+	const char expected_response[] = "Got IRQ 07.";
+	const char expected_response_2[] = "Got IRQ 08.";
+	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(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+
+	/* Inject the interrupt and wait for a message. */
+	hf_inject_interrupt(INTERRUPTIBLE_VM_ID, 0, EXTERNAL_INTERRUPT_ID);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
+	EXPECT_EQ(
+		memcmp(recv_page, expected_response, sizeof(expected_response)),
+		0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+
+	/* Inject a different interrupt and wait for a different message. */
+	hf_inject_interrupt(INTERRUPTIBLE_VM_ID, 0, EXTERNAL_INTERRUPT_ID_B);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
+	EXPECT_EQ(memcmp(recv_page, expected_response_2,
+			 sizeof(expected_response_2)),
+		  0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+}
+
+/**
+ * Inject an interrupt then send a message to the interrupt VM, which will send
+ * a message back each time. This is to test that interrupt injection doesn't
+ * interfere with message reception.
+ */
+TEST(interrupts, inject_interrupt_message)
+{
+	const char expected_response[] = "Got IRQ 07.";
+	const char message[] = "Ping";
+	const char expected_response_2[] = "Got IRQ 05.";
+	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(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
+
+	/* Inject the interrupt and wait for a message. */
+	hf_inject_interrupt(INTERRUPTIBLE_VM_ID, 0, EXTERNAL_INTERRUPT_ID);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response));
+	EXPECT_EQ(
+		memcmp(recv_page, expected_response, sizeof(expected_response)),
+		0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+
+	/* Now send a message to the secondary. */
+	memcpy(send_page, message, sizeof(message));
+	EXPECT_EQ(hf_mailbox_send(INTERRUPTIBLE_VM_ID, sizeof(message)),
+		  HF_INVALID_VCPU);
+	run_res = hf_vcpu_run(INTERRUPTIBLE_VM_ID, 0);
+	EXPECT_EQ(run_res.code, HF_VCPU_RUN_MESSAGE);
+	EXPECT_EQ(run_res.message.size, sizeof(expected_response_2));
+	EXPECT_EQ(memcmp(recv_page, expected_response_2,
+			 sizeof(expected_response_2)),
+		  0);
+	EXPECT_EQ(hf_mailbox_clear(), 0);
+}
diff --git a/test/vmapi/secondaries/BUILD.gn b/test/vmapi/secondaries/BUILD.gn
index af4bfd6..52dfd57 100644
--- a/test/vmapi/secondaries/BUILD.gn
+++ b/test/vmapi/secondaries/BUILD.gn
@@ -16,6 +16,7 @@
 
 vm_kernel("echo") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "echo.c",
@@ -28,6 +29,7 @@
 
 vm_kernel("relay_a") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "relay_a.c",
@@ -40,6 +42,7 @@
 
 vm_kernel("relay_b") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "relay_b.c",
@@ -52,6 +55,7 @@
 
 vm_kernel("interruptible") {
   testonly = true
+  public_configs = [ "//test/vmapi:config" ]
 
   sources = [
     "interruptible.c",
diff --git a/test/vmapi/secondaries/interruptible.c b/test/vmapi/secondaries/interruptible.c
index 09ebd51..73c6da0 100644
--- a/test/vmapi/secondaries/interruptible.c
+++ b/test/vmapi/secondaries/interruptible.c
@@ -26,15 +26,13 @@
 #include "vmapi/hf/call.h"
 
 #include "../msr.h"
+#include "constants.h"
 
 /*
  * Secondary VM that sends messages in response to interrupts, and interrupts
  * itself when it receives a message.
  */
 
-#define DESTINATION_VM_ID 0
-#define SELF_INTERRUPT_ID 5
-
 alignas(4096) uint8_t kstack[4096];
 
 static alignas(PAGE_SIZE) uint8_t send_page[PAGE_SIZE];
@@ -54,7 +52,23 @@
 	buffer[8] = '0' + interrupt_id / 10;
 	buffer[9] = '0' + interrupt_id % 10;
 	memcpy(send_page, buffer, size);
-	hf_mailbox_send(DESTINATION_VM_ID, size);
+	hf_mailbox_send(HF_PRIMARY_VM_ID, size);
+}
+
+/**
+ * Try to receive a message from the mailbox, blocking if necessary, and
+ * retrying if interrupted.
+ */
+struct hf_mailbox_receive_return mailbox_receive_retry()
+{
+	struct hf_mailbox_receive_return received = {
+		.vm_id = HF_INVALID_VM_ID,
+		.size = 0,
+	};
+	while (received.vm_id == HF_INVALID_VM_ID && received.size == 0) {
+		received = hf_mailbox_receive(true);
+	}
+	return received;
 }
 
 void kmain(void)
@@ -63,13 +77,23 @@
 
 	exception_setup();
 	hf_enable_interrupt(SELF_INTERRUPT_ID, true);
+	hf_enable_interrupt(EXTERNAL_INTERRUPT_ID, true);
+	hf_enable_interrupt(EXTERNAL_INTERRUPT_ID_B, true);
 	arch_irq_enable();
 
 	/* Loop, echo messages back to the sender. */
 	for (;;) {
-		received_message = hf_mailbox_receive(true);
+		const char ping_message[] = "Ping";
+		received_message = mailbox_receive_retry();
+		if (received_message.vm_id == 0 && received_message.size == 5 &&
+		    memcmp(recv_page, ping_message, sizeof(ping_message)) ==
+			    0) {
+			/* Interrupt ourselves */
+			hf_inject_interrupt(4, 0, SELF_INTERRUPT_ID);
+		} else {
+			dlog("Got unexpected message from VM %d, size %d.\n",
+			     received_message.vm_id, received_message.size);
+		}
 		hf_mailbox_clear();
-		/* Interrupt ourselves */
-		hf_inject_interrupt(4, 0, SELF_INTERRUPT_ID);
 	}
 }