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, ×tamp), ZX_ERR_CANCELED, ""); 143 ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, ×tamp), 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