Add test that WFI is a no-op when there is a pending interrupt.
Change-Id: I764a2ce7c45834e81257f418b7c5442fd05571ff
diff --git a/test/vmapi/primary_with_secondaries/interrupts.c b/test/vmapi/primary_with_secondaries/interrupts.c
index 5f12565..09f8719 100644
--- a/test/vmapi/primary_with_secondaries/interrupts.c
+++ b/test/vmapi/primary_with_secondaries/interrupts.c
@@ -222,3 +222,30 @@
0);
EXPECT_EQ(hf_mailbox_clear(), 0);
}
+
+/**
+ * If a secondary VM has an enabled and pending interrupt, even if interrupts
+ * are disabled globally via PSTATE, then WFI should be treated as a no-op and
+ * not return to the primary.
+ */
+TEST(interrupts, pending_interrupt_wfi_not_trapped)
+{
+ const char expected_response[] = "Done waiting";
+ struct hf_vcpu_run_return run_res;
+ struct mailbox_buffers mb = set_up_mailbox();
+
+ SERVICE_SELECT(SERVICE_VM0, "wfi", mb.send);
+
+ /*
+ * Inject the interrupt and run the VM. It should disable interrupts
+ * globally, enable the specific interrupt, and then send us a message
+ * back after running WFI a few times.
+ */
+ 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(expected_response));
+ EXPECT_EQ(memcmp(mb.recv, expected_response, sizeof(expected_response)),
+ 0);
+ EXPECT_EQ(hf_mailbox_clear(), 0);
+}
diff --git a/test/vmapi/primary_with_secondaries/services/BUILD.gn b/test/vmapi/primary_with_secondaries/services/BUILD.gn
index 923ad75..75a797e 100644
--- a/test/vmapi/primary_with_secondaries/services/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/services/BUILD.gn
@@ -112,6 +112,18 @@
]
}
+# Service to check that WFI is a no-op when there are pending interrupts.
+source_set("wfi") {
+ testonly = true
+ public_configs = [
+ "..:config",
+ "//test/hftest:hftest_config",
+ ]
+ sources = [
+ "wfi.c",
+ ]
+}
+
# Group services together into VMs.
vm_kernel("service_vm0") {
@@ -126,6 +138,7 @@
":memory",
":receive_block",
":relay",
+ ":wfi",
"//test/hftest:hftest_secondary_vm",
]
}
diff --git a/test/vmapi/primary_with_secondaries/services/wfi.c b/test/vmapi/primary_with_secondaries/services/wfi.c
new file mode 100644
index 0000000..1733aea
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/services/wfi.c
@@ -0,0 +1,54 @@
+/*
+ * 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/vm/interrupts_gicv3.h"
+
+#include "hf/dlog.h"
+
+#include "vmapi/hf/call.h"
+
+#include "hftest.h"
+#include "primary_with_secondary.h"
+
+/*
+ * Secondary VM that enables an interrupt, disables interrupts globally, and
+ * calls WFI.
+ */
+
+static void irq(void)
+{
+ uint32_t interrupt_id = hf_interrupt_get();
+ dlog("Unexpected secondary IRQ %d from current\n", interrupt_id);
+ FAIL("Unexpected secondary IRQ");
+}
+
+TEST_SERVICE(wfi)
+{
+ int32_t i;
+ const char message[] = "Done waiting";
+
+ exception_setup(irq);
+ arch_irq_disable();
+ hf_interrupt_enable(EXTERNAL_INTERRUPT_ID_A, true);
+
+ for (i = 0; i < 10; ++i) {
+ interrupt_wait();
+ }
+
+ memcpy(SERVICE_SEND_BUFFER(), message, sizeof(message));
+ hf_mailbox_send(HF_PRIMARY_VM_ID, sizeof(message), false);
+}