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 <lib/zx/guest.h>
6 #include <lib/zx/interrupt.h>
7 #include <lib/zx/port.h>
8 #include <lib/zx/process.h>
9 #include <lib/zx/thread.h>
10 #include <lib/zx/vcpu.h>
11 #include <unittest/unittest.h>
12
13 extern "C" zx_handle_t get_root_resource(void);
14
get_thread_info(zx_handle_t thread,zx_info_thread_t * info)15 static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
16 return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
17 }
18
wait_thread(const zx::thread & thread,uint32_t reason)19 static bool wait_thread(const zx::thread& thread, uint32_t reason) {
20 while (true) {
21 zx_info_thread_t info;
22 ASSERT_EQ(thread.get_info(ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr), ZX_OK);
23 if (info.state == reason) {
24 return true;
25 }
26 zx::nanosleep(zx::deadline_after(zx::msec(1)));
27 }
28 }
29
thread_entry(uintptr_t arg1,uintptr_t arg2)30 static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
31 zx_handle_t interrupt = static_cast<zx_handle_t>(arg1);
32 while (zx_interrupt_wait(interrupt, nullptr) == ZX_OK) {}
33 }
34
35 // Tests to bind interrupt to a non-bindable port
interrupt_port_non_bindable_test()36 static bool interrupt_port_non_bindable_test() {
37 BEGIN_TEST;
38
39 zx::unowned_resource resource(get_root_resource());
40 zx::interrupt interrupt;
41 zx::port port;
42 const uint32_t key = 789;
43
44 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
45 ASSERT_EQ(zx::port::create(0, &port), ZX_OK);
46
47 ASSERT_EQ(interrupt.bind(port, key, 0), ZX_ERR_WRONG_TYPE);
48
49 END_TEST;
50 }
51
52 // Tests Interrupts bound to a port
interrupt_port_bound_test()53 static bool interrupt_port_bound_test() {
54 BEGIN_TEST;
55
56 zx::unowned_resource resource(get_root_resource());
57 zx::interrupt interrupt;
58 zx::port port;
59 const zx::time signaled_timestamp_1(12345);
60 const zx::time signaled_timestamp_2(67890);
61 const uint32_t key = 789;
62 zx_port_packet_t out;
63
64 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
65 ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
66
67 // Test port binding
68 ASSERT_EQ(interrupt.bind(port, key, 0), ZX_OK);
69 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
70 ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
71 ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
72
73 // Triggering 2nd time, ACKing it causes port packet to be delivered
74 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
75 ASSERT_EQ(interrupt.ack(), ZX_OK);
76 ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
77 ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
78 ASSERT_EQ(out.key, key);
79 ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT);
80 ASSERT_EQ(out.status, ZX_OK);
81 ASSERT_EQ(interrupt.ack(), ZX_OK);
82
83 // Triggering it twice
84 // the 2nd timestamp is recorded and upon ACK another packet is queued
85 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
86 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_2), ZX_OK);
87 ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
88 ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
89 ASSERT_EQ(interrupt.ack(), ZX_OK);
90 ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
91 ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2.get());
92
93 // Try to destroy now, expecting to return error telling packet
94 // has been read but the interrupt has not been re-armed
95 ASSERT_EQ(interrupt.destroy(), ZX_ERR_NOT_FOUND,"");
96 ASSERT_EQ(interrupt.ack(), ZX_ERR_CANCELED);
97 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_ERR_CANCELED);
98
99 END_TEST;
100 }
101
102 // Tests support for virtual interrupts
interrupt_test()103 static bool interrupt_test() {
104 BEGIN_TEST;
105
106 zx::unowned_resource resource(get_root_resource());
107 zx::interrupt interrupt;
108 zx::interrupt interrupt_cancelled;
109 const zx::time signaled_timestamp(12345);
110 zx::time timestamp;
111
112 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_SLOT_USER, &interrupt),
113 ZX_ERR_INVALID_ARGS);
114 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
115 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt_cancelled),
116 ZX_OK);
117
118 ASSERT_EQ(interrupt_cancelled.destroy(), ZX_OK);
119 ASSERT_EQ(interrupt_cancelled.trigger(0, signaled_timestamp), ZX_ERR_CANCELED);
120
121 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
122
123 ASSERT_EQ(interrupt_cancelled.wait(×tamp), ZX_ERR_CANCELED);
124 ASSERT_EQ(interrupt.wait(×tamp), ZX_OK);
125 ASSERT_EQ(timestamp.get(), signaled_timestamp.get());
126
127 ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
128 ASSERT_EQ(interrupt.wait(×tamp), ZX_OK);
129
130 END_TEST;
131 }
132
133 // Tests interrupt thread after suspend/resume
interrupt_suspend_test()134 static bool interrupt_suspend_test() {
135 BEGIN_TEST;
136
137 zx::unowned_resource resource(get_root_resource());
138 zx::interrupt interrupt;
139 zx::thread thread;
140 const char name[] = "interrupt_test_thread";
141 // preallocated stack to satisfy the thread we create
142 static uint8_t stack[1024] __ALIGNED(16);
143
144 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
145
146 // Create and start a thread which waits for an IRQ
147 ASSERT_EQ(zx::thread::create(*zx::process::self(), name, sizeof(name), 0, &thread), ZX_OK);
148
149 ASSERT_EQ(thread.start(reinterpret_cast<uintptr_t>(thread_entry),
150 reinterpret_cast<uintptr_t>(stack) + sizeof(stack),
151 static_cast<uintptr_t>(interrupt.get()), 0),
152 ZX_OK);
153
154 // Wait till the thread is in blocked state
155 ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
156
157 // Suspend the thread, wait till it is suspended
158 zx::suspend_token suspend_token;
159 ASSERT_EQ(thread.suspend(&suspend_token), ZX_OK);
160 ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_SUSPENDED));
161
162 // Resume the thread, wait till it is back to being in blocked state
163 suspend_token.reset();
164 ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
165 thread.kill();
166
167 END_TEST;
168 }
169
170 // Tests binding an interrupt to multiple VCPUs
interrupt_bind_vcpu_test()171 static bool interrupt_bind_vcpu_test() {
172 BEGIN_TEST;
173
174 zx::unowned_resource resource(get_root_resource());
175 zx::interrupt interrupt;
176 zx::guest guest;
177 zx::vmar vmar;
178 zx::vcpu vcpu1;
179 zx::vcpu vcpu2;
180
181 zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
182 if (status == ZX_ERR_NOT_SUPPORTED) {
183 fprintf(stderr, "Guest creation not supported\n");
184 return true;
185 }
186 ASSERT_EQ(status, ZX_OK);
187
188 ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
189 ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu1), ZX_OK);
190 ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu2), ZX_OK);
191
192 ASSERT_EQ(interrupt.bind_vcpu(vcpu1, 0), ZX_OK);
193 ASSERT_EQ(interrupt.bind_vcpu(vcpu2, 0), ZX_OK);
194
195 END_TEST;
196 }
197
198 // Tests binding a virtual interrupt to a VCPU
interrupt_bind_vcpu_not_supported_test()199 static bool interrupt_bind_vcpu_not_supported_test() {
200 BEGIN_TEST;
201
202 zx::unowned_resource resource(get_root_resource());
203 zx::interrupt interrupt;
204 zx::port port;
205 zx::guest guest;
206 zx::vmar vmar;
207 zx::vcpu vcpu;
208
209 zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
210 if (status == ZX_ERR_NOT_SUPPORTED) {
211 fprintf(stderr, "Guest creation not supported\n");
212 return true;
213 }
214 ASSERT_EQ(status, ZX_OK);
215
216 ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
217 ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
218 ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
219
220 ASSERT_EQ(interrupt.bind(port, 0, 0), ZX_OK);
221 ASSERT_EQ(interrupt.bind_vcpu(vcpu, 0), ZX_ERR_NOT_SUPPORTED);
222
223 END_TEST;
224 }
225
226 // Tests binding an interrupt to a VCPU, after binding it to a port
interrupt_bind_vcpu_already_bound_test()227 static bool interrupt_bind_vcpu_already_bound_test() {
228 BEGIN_TEST;
229
230 zx::unowned_resource resource(get_root_resource());
231 zx::interrupt interrupt;
232 zx::port port;
233 zx::guest guest;
234 zx::vmar vmar;
235 zx::vcpu vcpu;
236
237 zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
238 if (status == ZX_ERR_NOT_SUPPORTED) {
239 fprintf(stderr, "Guest creation not supported\n");
240 return true;
241 }
242 ASSERT_EQ(status, ZX_OK);
243
244 ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
245 ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
246 ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
247
248 ASSERT_EQ(interrupt.bind(port, 0, 0), ZX_OK);
249 ASSERT_EQ(interrupt.bind_vcpu(vcpu, 0), ZX_ERR_ALREADY_BOUND);
250
251 END_TEST;
252 }
253
254 // Tests binding an interrupt to VCPUs from different guests
interrupt_bind_vcpu_multiple_guests_test()255 static bool interrupt_bind_vcpu_multiple_guests_test() {
256 BEGIN_TEST;
257
258 zx::unowned_resource resource(get_root_resource());
259 zx::interrupt interrupt;
260 zx::guest guest1;
261 zx::guest guest2;
262 zx::vmar vmar1;
263 zx::vmar vmar2;
264 zx::vcpu vcpu1;
265 zx::vcpu vcpu2;
266
267 zx_status_t status = zx::guest::create(*resource, 0, &guest1, &vmar1);
268 if (status == ZX_ERR_NOT_SUPPORTED) {
269 fprintf(stderr, "Guest creation not supported\n");
270 return true;
271 }
272 ASSERT_EQ(status, ZX_OK);
273
274 ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
275 ASSERT_EQ(zx::vcpu::create(guest1, 0, 0, &vcpu1), ZX_OK);
276 ASSERT_EQ(zx::guest::create(*resource, 0, &guest2, &vmar2), ZX_OK);
277 ASSERT_EQ(zx::vcpu::create(guest2, 0, 0, &vcpu2), ZX_OK);
278
279 ASSERT_EQ(interrupt.bind_vcpu(vcpu1, 0), ZX_OK);
280 ASSERT_EQ(interrupt.bind_vcpu(vcpu2, 0), ZX_ERR_INVALID_ARGS);
281
282 END_TEST;
283 }
284
285 BEGIN_TEST_CASE(interrupt_tests)
286 RUN_TEST(interrupt_test)
287 RUN_TEST(interrupt_port_bound_test)
288 RUN_TEST(interrupt_port_non_bindable_test)
289 RUN_TEST(interrupt_suspend_test)
290 RUN_TEST(interrupt_bind_vcpu_test)
291 RUN_TEST(interrupt_bind_vcpu_not_supported_test)
292 RUN_TEST(interrupt_bind_vcpu_already_bound_test)
293 RUN_TEST(interrupt_bind_vcpu_multiple_guests_test)
294 END_TEST_CASE(interrupt_tests)
295