[utest][core][interrupt] Convert to C++
Convert interrupt-test from C to C++, so that we can exercise ulib/zx.
In addition, it also simplifies the test and reduces the code.
Test: interrupt-test
Change-Id: Iccc18b0860cd3cb2743381703924e5540366e763
diff --git a/system/utest/core/interrupt/interrupt-test.c b/system/utest/core/interrupt/interrupt-test.c
deleted file mode 100644
index 7802f9f..0000000
--- a/system/utest/core/interrupt/interrupt-test.c
+++ /dev/null
@@ -1,342 +0,0 @@
-// Copyright 2017 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <unittest/unittest.h>
-#include <zircon/syscalls.h>
-#include <zircon/process.h>
-#include <zircon/syscalls/port.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <threads.h>
-
-extern zx_handle_t get_root_resource(void);
-
-static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
- return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
-}
-
-static bool wait_thread(zx_handle_t thread, uint32_t reason) {
- while (true) {
- zx_info_thread_t info;
- ASSERT_TRUE(get_thread_info(thread, &info), "");
- if (info.state == reason)
- break;
- zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
- }
- return true;
-}
-
-static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
- zx_handle_t vinth = *(zx_handle_t*)arg1;
- while(1) {
- zx_interrupt_wait(vinth, NULL);
- }
- zx_thread_exit();
-}
-
-// Tests to bind interrupt to a non-bindable port
-static bool interrupt_port_non_bindable_test(void) {
- BEGIN_TEST;
-
- zx_handle_t port_handle;
- zx_handle_t virt_interrupt_port_handle;
- zx_handle_t rsrc = get_root_resource();
- uint32_t key = 789;
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
- &virt_interrupt_port_handle), ZX_OK, "");
- ASSERT_EQ(zx_port_create(0, &port_handle), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle,
- port_handle, key, 0), ZX_ERR_WRONG_TYPE, "");
-
- ASSERT_EQ(zx_handle_close(port_handle), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
-
- END_TEST;
-}
-
-// Tests Interrupts bound to a port
-static bool interrupt_port_bound_test(void) {
- BEGIN_TEST;
-
- zx_handle_t virt_interrupt_port_handle;
- zx_handle_t port_handle_bind;
- zx_time_t signaled_timestamp_1 = 12345;
- zx_time_t signaled_timestamp_2 = 67890;
- uint32_t key = 789;
- zx_port_packet_t out;
- zx_handle_t rsrc = get_root_resource();
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
- &virt_interrupt_port_handle), ZX_OK, "");
- ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port_handle_bind), ZX_OK, "");
-
- // Test port binding
- ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle, port_handle_bind, key, 0), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
- ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
- ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
-
- // Triggering 2nd time, ACKing it causes port packet to be delivered
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
- ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
- ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
- ASSERT_EQ(out.key, key, "");
- ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT, "");
- ASSERT_EQ(out.status, ZX_OK, "");
- ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
-
- // Triggering it twice
- // the 2nd timestamp is recorded and upon ACK another packet is queued
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_2), ZX_OK, "");
- ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
- ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
- ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
- ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
- ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2, "");
-
- // Try to destroy now, expecting to return error telling packet
- // has been read but the interrupt has not been re-armed
- ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_port_handle), ZX_ERR_NOT_FOUND,"");
- ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_ERR_CANCELED, "");
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0,
- signaled_timestamp_1), ZX_ERR_CANCELED, "");
-
- ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(port_handle_bind), ZX_OK, "");
-
- END_TEST;
-}
-
-// Tests support for virtual interrupts
-static bool interrupt_test(void) {
- BEGIN_TEST;
-
- zx_handle_t virt_interrupt_handle;
- zx_handle_t virt_interrupt_handle_cancelled;
- zx_time_t timestamp;
- zx_time_t signaled_timestamp = 12345;
- zx_handle_t rsrc = get_root_resource();
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
- &virt_interrupt_handle), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
- &virt_interrupt_handle_cancelled), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_SLOT_USER,
- &virt_interrupt_handle), ZX_ERR_INVALID_ARGS, "");
-
-
- ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_handle_cancelled), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle_cancelled,
- 0, signaled_timestamp), ZX_ERR_CANCELED, "");
-
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle_cancelled, ×tamp), ZX_ERR_CANCELED, "");
- ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, ×tamp), ZX_OK, "");
- ASSERT_EQ(timestamp, signaled_timestamp, "");
-
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, NULL), ZX_OK, "");
-
- ASSERT_EQ(zx_handle_close(virt_interrupt_handle), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(virt_interrupt_handle_cancelled), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle,
- 0, signaled_timestamp), ZX_ERR_BAD_HANDLE, "");
-
- END_TEST;
-}
-
-// Tests interrupt thread after suspend/resume
-static bool interrupt_suspend_test(void) {
- BEGIN_TEST;
-
- zx_handle_t thread_h;
- const char* thread_name = "interrupt_test_thread";
- // preallocated stack to satisfy the thread we create
- static uint8_t stack[1024] __ALIGNED(16);
- zx_handle_t rsrc = get_root_resource();
- zx_handle_t vinth;
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL, &vinth), ZX_OK, "");
-
- // Create and start a thread which waits for an IRQ
- ASSERT_EQ(zx_thread_create(zx_process_self(), thread_name, strlen(thread_name),
- 0, &thread_h), ZX_OK, "");
-
- ASSERT_EQ(zx_thread_start(thread_h, (uintptr_t)thread_entry,
- (uintptr_t)stack + sizeof(stack),
- (uintptr_t)&vinth, 0), ZX_OK, "");
-
- // Wait till the thread is in blocked state
- ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
-
- // Suspend the thread, wait till it is suspended
- zx_handle_t suspend_token = ZX_HANDLE_INVALID;
- ASSERT_EQ(zx_task_suspend_token(thread_h, &suspend_token), ZX_OK, "");
- ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_SUSPENDED), "");
-
- // Resume the thread, wait till it is back to being in blocked state
- ASSERT_EQ(zx_handle_close(suspend_token), ZX_OK, "");
- ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
-
- END_TEST;
-}
-
-// Tests binding an interrupt to multiple VCPUs
-static bool interrupt_bind_vcpu_test(void) {
- BEGIN_TEST;
-
- zx_handle_t rsrc = get_root_resource();
- zx_handle_t interrupt;
- zx_handle_t guest;
- zx_handle_t vmar;
- zx_handle_t vcpu1;
- zx_handle_t vcpu2;
-
- zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
- if (status == ZX_ERR_NOT_SUPPORTED) {
- fprintf(stderr, "Guest creation not supported\n");
- return true;
- }
- ASSERT_EQ(status, ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu1), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu2), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu1, 0), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu2, 0), ZX_OK, "");
-
- ASSERT_EQ(zx_handle_close(vcpu1), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vcpu2), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
- END_TEST;
-}
-
-// Tests binding a virtual interrupt to a VCPU
-static bool interrupt_bind_vcpu_not_supported_test(void) {
- BEGIN_TEST;
-
- zx_handle_t rsrc = get_root_resource();
- zx_handle_t interrupt;
- zx_handle_t port;
- zx_handle_t guest;
- zx_handle_t vmar;
- zx_handle_t vcpu;
-
- zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
- if (status == ZX_ERR_NOT_SUPPORTED) {
- fprintf(stderr, "Guest creation not supported\n");
- return true;
- }
- ASSERT_EQ(status, ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK, "");
- ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_bind(interrupt, port, 0, 0), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu, 0), ZX_ERR_NOT_SUPPORTED, "");
-
- ASSERT_EQ(zx_handle_close(vcpu), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(port), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
- END_TEST;
-}
-
-// Tests binding an interrupt to a VCPU, after binding it to a port
-static bool interrupt_bind_vcpu_already_bound_test(void) {
- BEGIN_TEST;
-
- zx_handle_t rsrc = get_root_resource();
- zx_handle_t interrupt;
- zx_handle_t port;
- zx_handle_t guest;
- zx_handle_t vmar;
- zx_handle_t vcpu;
-
- zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
- if (status == ZX_ERR_NOT_SUPPORTED) {
- fprintf(stderr, "Guest creation not supported\n");
- return true;
- }
- ASSERT_EQ(status, ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
- ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_bind(interrupt, port, 0, 0), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu, 0), ZX_ERR_ALREADY_BOUND, "");
-
- ASSERT_EQ(zx_handle_close(vcpu), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(port), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
- END_TEST;
-}
-
-// Tests binding an interrupt to VCPUs from different guests
-static bool interrupt_bind_vcpu_multiple_guests_test(void) {
- BEGIN_TEST;
-
- zx_handle_t rsrc = get_root_resource();
- zx_handle_t interrupt;
- zx_handle_t guest1;
- zx_handle_t guest2;
- zx_handle_t vmar1;
- zx_handle_t vmar2;
- zx_handle_t vcpu1;
- zx_handle_t vcpu2;
-
- zx_status_t status = zx_guest_create(rsrc, 0, &guest1, &vmar1);
- if (status == ZX_ERR_NOT_SUPPORTED) {
- fprintf(stderr, "Guest creation not supported\n");
- return true;
- }
- ASSERT_EQ(status, ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest1, 0, 0, &vcpu1), ZX_OK, "");
- ASSERT_EQ(zx_guest_create(rsrc, 0, &guest2, &vmar2), ZX_OK, "");
- ASSERT_EQ(zx_vcpu_create(guest2, 0, 0, &vcpu2), ZX_OK, "");
-
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu1, 0), ZX_OK, "");
- ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu2, 0), ZX_ERR_INVALID_ARGS, "");
-
- ASSERT_EQ(zx_handle_close(vcpu1), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vcpu2), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vmar1), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(vmar2), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(guest1), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(guest2), ZX_OK, "");
- ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
- END_TEST;
-}
-
-BEGIN_TEST_CASE(interrupt_tests)
-RUN_TEST(interrupt_test)
-RUN_TEST(interrupt_port_bound_test)
-RUN_TEST(interrupt_port_non_bindable_test)
-RUN_TEST(interrupt_suspend_test)
-RUN_TEST(interrupt_bind_vcpu_test)
-RUN_TEST(interrupt_bind_vcpu_not_supported_test)
-RUN_TEST(interrupt_bind_vcpu_already_bound_test)
-RUN_TEST(interrupt_bind_vcpu_multiple_guests_test)
-END_TEST_CASE(interrupt_tests)
diff --git a/system/utest/core/interrupt/interrupt-test.cpp b/system/utest/core/interrupt/interrupt-test.cpp
new file mode 100644
index 0000000..255dccb
--- /dev/null
+++ b/system/utest/core/interrupt/interrupt-test.cpp
@@ -0,0 +1,294 @@
+// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <lib/zx/guest.h>
+#include <lib/zx/interrupt.h>
+#include <lib/zx/port.h>
+#include <lib/zx/process.h>
+#include <lib/zx/thread.h>
+#include <lib/zx/vcpu.h>
+#include <unittest/unittest.h>
+
+extern "C" zx_handle_t get_root_resource(void);
+
+static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
+ return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
+}
+
+static bool wait_thread(const zx::thread& thread, uint32_t reason) {
+ while (true) {
+ zx_info_thread_t info;
+ ASSERT_EQ(thread.get_info(ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr), ZX_OK);
+ if (info.state == reason) {
+ return true;
+ }
+ zx::nanosleep(zx::deadline_after(zx::msec(1)));
+ }
+}
+
+static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
+ zx_handle_t interrupt = static_cast<zx_handle_t>(arg1);
+ while (zx_interrupt_wait(interrupt, nullptr) == ZX_OK) {}
+}
+
+// Tests to bind interrupt to a non-bindable port
+static bool interrupt_port_non_bindable_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::port port;
+ const uint32_t key = 789;
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::port::create(0, &port), ZX_OK);
+
+ ASSERT_EQ(interrupt.bind(port.get(), key, 0), ZX_ERR_WRONG_TYPE);
+
+ END_TEST;
+}
+
+// Tests Interrupts bound to a port
+static bool interrupt_port_bound_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::port port;
+ const zx::time signaled_timestamp_1(12345);
+ const zx::time signaled_timestamp_2(67890);
+ const uint32_t key = 789;
+ zx_port_packet_t out;
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+
+ // Test port binding
+ ASSERT_EQ(interrupt.bind(port.get(), key, 0), ZX_OK);
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+ ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+ ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+
+ // Triggering 2nd time, ACKing it causes port packet to be delivered
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+ ASSERT_EQ(interrupt.ack(), ZX_OK);
+ ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+ ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+ ASSERT_EQ(out.key, key);
+ ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT);
+ ASSERT_EQ(out.status, ZX_OK);
+ ASSERT_EQ(interrupt.ack(), ZX_OK);
+
+ // Triggering it twice
+ // the 2nd timestamp is recorded and upon ACK another packet is queued
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_2), ZX_OK);
+ ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+ ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+ ASSERT_EQ(interrupt.ack(), ZX_OK);
+ ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+ ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2.get());
+
+ // Try to destroy now, expecting to return error telling packet
+ // has been read but the interrupt has not been re-armed
+ ASSERT_EQ(interrupt.destroy(), ZX_ERR_NOT_FOUND,"");
+ ASSERT_EQ(interrupt.ack(), ZX_ERR_CANCELED);
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_ERR_CANCELED);
+
+ END_TEST;
+}
+
+// Tests support for virtual interrupts
+static bool interrupt_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::interrupt interrupt_cancelled;
+ const zx::time signaled_timestamp(12345);
+ zx::time timestamp;
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_SLOT_USER, &interrupt),
+ ZX_ERR_INVALID_ARGS);
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt_cancelled),
+ ZX_OK);
+
+ ASSERT_EQ(interrupt_cancelled.destroy(), ZX_OK);
+ ASSERT_EQ(interrupt_cancelled.trigger(0, signaled_timestamp), ZX_ERR_CANCELED);
+
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
+
+ ASSERT_EQ(interrupt_cancelled.wait(×tamp), ZX_ERR_CANCELED);
+ ASSERT_EQ(interrupt.wait(×tamp), ZX_OK);
+ ASSERT_EQ(timestamp.get(), signaled_timestamp.get());
+
+ ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
+ ASSERT_EQ(interrupt.wait(×tamp), ZX_OK);
+
+ END_TEST;
+}
+
+// Tests interrupt thread after suspend/resume
+static bool interrupt_suspend_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::thread thread;
+ const char name[] = "interrupt_test_thread";
+ // preallocated stack to satisfy the thread we create
+ static uint8_t stack[1024] __ALIGNED(16);
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+
+ // Create and start a thread which waits for an IRQ
+ ASSERT_EQ(zx::thread::create(*zx::process::self(), name, sizeof(name), 0, &thread), ZX_OK);
+
+ ASSERT_EQ(thread.start(reinterpret_cast<uintptr_t>(thread_entry),
+ reinterpret_cast<uintptr_t>(stack) + sizeof(stack),
+ static_cast<uintptr_t>(interrupt.get()), 0),
+ ZX_OK);
+
+ // Wait till the thread is in blocked state
+ ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
+
+ // Suspend the thread, wait till it is suspended
+ zx::suspend_token suspend_token;
+ ASSERT_EQ(thread.suspend(&suspend_token), ZX_OK);
+ ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_SUSPENDED));
+
+ // Resume the thread, wait till it is back to being in blocked state
+ suspend_token.reset();
+ ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
+ thread.kill();
+
+ END_TEST;
+}
+
+// Tests binding an interrupt to multiple VCPUs
+static bool interrupt_bind_vcpu_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::guest guest;
+ zx::vmar vmar;
+ zx::vcpu vcpu1;
+ zx::vcpu vcpu2;
+
+ zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+ if (status == ZX_ERR_NOT_SUPPORTED) {
+ fprintf(stderr, "Guest creation not supported\n");
+ return true;
+ }
+ ASSERT_EQ(status, ZX_OK);
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu1), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu2), ZX_OK);
+
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu1.get(), 0), ZX_OK);
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu2.get(), 0), ZX_OK);
+
+ END_TEST;
+}
+
+// Tests binding a virtual interrupt to a VCPU
+static bool interrupt_bind_vcpu_not_supported_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::port port;
+ zx::guest guest;
+ zx::vmar vmar;
+ zx::vcpu vcpu;
+
+ zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+ if (status == ZX_ERR_NOT_SUPPORTED) {
+ fprintf(stderr, "Guest creation not supported\n");
+ return true;
+ }
+ ASSERT_EQ(status, ZX_OK);
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
+
+ ASSERT_EQ(interrupt.bind(port.get(), 0, 0), ZX_OK);
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu.get(), 0), ZX_ERR_NOT_SUPPORTED);
+
+ END_TEST;
+}
+
+// Tests binding an interrupt to a VCPU, after binding it to a port
+static bool interrupt_bind_vcpu_already_bound_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::port port;
+ zx::guest guest;
+ zx::vmar vmar;
+ zx::vcpu vcpu;
+
+ zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+ if (status == ZX_ERR_NOT_SUPPORTED) {
+ fprintf(stderr, "Guest creation not supported\n");
+ return true;
+ }
+ ASSERT_EQ(status, ZX_OK);
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
+
+ ASSERT_EQ(interrupt.bind(port.get(), 0, 0), ZX_OK);
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu.get(), 0), ZX_ERR_ALREADY_BOUND);
+
+ END_TEST;
+}
+
+// Tests binding an interrupt to VCPUs from different guests
+static bool interrupt_bind_vcpu_multiple_guests_test() {
+ BEGIN_TEST;
+
+ zx::unowned_resource resource(get_root_resource());
+ zx::interrupt interrupt;
+ zx::guest guest1;
+ zx::guest guest2;
+ zx::vmar vmar1;
+ zx::vmar vmar2;
+ zx::vcpu vcpu1;
+ zx::vcpu vcpu2;
+
+ zx_status_t status = zx::guest::create(*resource, 0, &guest1, &vmar1);
+ if (status == ZX_ERR_NOT_SUPPORTED) {
+ fprintf(stderr, "Guest creation not supported\n");
+ return true;
+ }
+ ASSERT_EQ(status, ZX_OK);
+
+ ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest1, 0, 0, &vcpu1), ZX_OK);
+ ASSERT_EQ(zx::guest::create(*resource, 0, &guest2, &vmar2), ZX_OK);
+ ASSERT_EQ(zx::vcpu::create(guest2, 0, 0, &vcpu2), ZX_OK);
+
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu1.get(), 0), ZX_OK);
+ ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu2.get(), 0), ZX_ERR_INVALID_ARGS);
+
+ END_TEST;
+}
+
+BEGIN_TEST_CASE(interrupt_tests)
+RUN_TEST(interrupt_test)
+RUN_TEST(interrupt_port_bound_test)
+RUN_TEST(interrupt_port_non_bindable_test)
+RUN_TEST(interrupt_suspend_test)
+RUN_TEST(interrupt_bind_vcpu_test)
+RUN_TEST(interrupt_bind_vcpu_not_supported_test)
+RUN_TEST(interrupt_bind_vcpu_already_bound_test)
+RUN_TEST(interrupt_bind_vcpu_multiple_guests_test)
+END_TEST_CASE(interrupt_tests)