1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <unittest/unittest.h>
6#include <zircon/syscalls.h>
7#include <zircon/process.h>
8#include <zircon/syscalls/port.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <poll.h>
12#include <stdio.h>
13#include <unistd.h>
14#include <threads.h>
15
16extern zx_handle_t get_root_resource(void);
17
18static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
19    return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
20}
21
22static bool wait_thread(zx_handle_t thread, uint32_t reason) {
23    while (true) {
24        zx_info_thread_t info;
25        ASSERT_TRUE(get_thread_info(thread, &info), "");
26        if (info.state == reason)
27            break;
28        zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
29    }
30    return true;
31}
32
33static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
34    zx_handle_t vinth = *(zx_handle_t*)arg1;
35    while(1) {
36        zx_interrupt_wait(vinth, NULL);
37    }
38    zx_thread_exit();
39}
40
41// Tests to bind interrupt to a non-bindable port
42static bool interrupt_port_non_bindable_test(void) {
43    BEGIN_TEST;
44
45    zx_handle_t port_handle;
46    zx_handle_t virt_interrupt_port_handle;
47    zx_handle_t rsrc = get_root_resource();
48    uint32_t key = 789;
49
50    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
51                                  &virt_interrupt_port_handle), ZX_OK, "");
52    ASSERT_EQ(zx_port_create(0, &port_handle), ZX_OK, "");
53
54    ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle,
55                                port_handle, key, 0), ZX_ERR_WRONG_TYPE, "");
56
57    ASSERT_EQ(zx_handle_close(port_handle), ZX_OK, "");
58    ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
59
60    END_TEST;
61}
62
63// Tests Interrupts bound to a port
64static bool interrupt_port_bound_test(void) {
65    BEGIN_TEST;
66
67    zx_handle_t virt_interrupt_port_handle;
68    zx_handle_t port_handle_bind;
69    zx_time_t signaled_timestamp_1 = 12345;
70    zx_time_t signaled_timestamp_2 = 67890;
71    uint32_t key = 789;
72    zx_port_packet_t out;
73    zx_handle_t rsrc = get_root_resource();
74
75    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
76                                  &virt_interrupt_port_handle), ZX_OK, "");
77    ASSERT_EQ(zx_port_create(1, &port_handle_bind), ZX_OK, "");
78
79    // Test port binding
80    ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle, port_handle_bind, key, 0), ZX_OK, "");
81    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
82    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
83    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
84
85    // Triggering 2nd time, ACKing it causes port packet to be delivered
86    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
87    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
88    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
89    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
90    ASSERT_EQ(out.key, key, "");
91    ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT, "");
92    ASSERT_EQ(out.status, ZX_OK, "");
93    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
94
95    // Triggering it twice
96    // the 2nd timestamp is recorded and upon ACK another packet is queued
97    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
98    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_2), ZX_OK, "");
99    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
100    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
101    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
102    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
103    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2, "");
104
105    // Try to destroy now, expecting to return error telling packet
106    // has been read but the interrupt has not been re-armed
107    ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_port_handle), ZX_ERR_NOT_FOUND,"");
108    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_ERR_CANCELED, "");
109    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0,
110                                   signaled_timestamp_1), ZX_ERR_CANCELED, "");
111
112    ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
113    ASSERT_EQ(zx_handle_close(port_handle_bind), ZX_OK, "");
114
115    END_TEST;
116}
117
118// Tests support for virtual interrupts
119static bool interrupt_test(void) {
120    BEGIN_TEST;
121
122    zx_handle_t virt_interrupt_handle;
123    zx_handle_t virt_interrupt_handle_cancelled;
124    zx_time_t timestamp;
125    zx_time_t signaled_timestamp = 12345;
126    zx_handle_t rsrc = get_root_resource();
127
128    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
129                                  &virt_interrupt_handle), ZX_OK, "");
130    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
131                                  &virt_interrupt_handle_cancelled), ZX_OK, "");
132    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_SLOT_USER,
133                                  &virt_interrupt_handle), ZX_ERR_INVALID_ARGS, "");
134
135
136    ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_handle_cancelled), ZX_OK, "");
137    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle_cancelled,
138                                   0, signaled_timestamp), ZX_ERR_CANCELED, "");
139
140    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
141
142    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle_cancelled, &timestamp), ZX_ERR_CANCELED, "");
143    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, &timestamp), ZX_OK, "");
144    ASSERT_EQ(timestamp, signaled_timestamp, "");
145
146    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
147    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, NULL), ZX_OK, "");
148
149    ASSERT_EQ(zx_handle_close(virt_interrupt_handle), ZX_OK, "");
150    ASSERT_EQ(zx_handle_close(virt_interrupt_handle_cancelled), ZX_OK, "");
151    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle,
152                                   0, signaled_timestamp), ZX_ERR_BAD_HANDLE, "");
153
154    END_TEST;
155}
156
157// Tests interrupt thread after suspend/resume
158static bool interrupt_suspend_test(void) {
159    BEGIN_TEST;
160
161    zx_handle_t thread_h;
162    const char* thread_name = "interrupt_test_thread";
163    // preallocated stack to satisfy the thread we create
164    static uint8_t stack[1024] __ALIGNED(16);
165    zx_handle_t rsrc = get_root_resource();
166    zx_handle_t vinth;
167    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL, &vinth), ZX_OK, "");
168
169    // Create and start a thread which waits for an IRQ
170    ASSERT_EQ(zx_thread_create(zx_process_self(), thread_name, strlen(thread_name),
171                               0, &thread_h), ZX_OK, "");
172
173    ASSERT_EQ(zx_thread_start(thread_h, (uintptr_t)thread_entry,
174                             (uintptr_t)stack + sizeof(stack),
175                             (uintptr_t)&vinth, 0), ZX_OK, "");
176
177    // Wait till the thread is in blocked state
178    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
179
180    // Suspend the thread, wait till it is suspended
181    zx_handle_t suspend_token = ZX_HANDLE_INVALID;
182    ASSERT_EQ(zx_task_suspend_token(thread_h, &suspend_token), ZX_OK, "");
183    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_SUSPENDED), "");
184
185    // Resume the thread, wait till it is back to being in blocked state
186    ASSERT_EQ(zx_handle_close(suspend_token), ZX_OK, "");
187    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
188
189    END_TEST;
190}
191
192BEGIN_TEST_CASE(interrupt_tests)
193RUN_TEST(interrupt_test)
194RUN_TEST(interrupt_port_bound_test)
195RUN_TEST(interrupt_port_non_bindable_test)
196RUN_TEST(interrupt_suspend_test)
197END_TEST_CASE(interrupt_tests)
198