1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <lib/unittest/unittest.h>
25
26 #include <lib/trusty/handle_set.h>
27 #include <lib/trusty/ipc.h>
28 #include <lib/trusty/ipc_msg.h>
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <uapi/err.h>
35
36 #define LOCAL_TRACE (0)
37
38 #include <lk/trace.h>
39
40 #define MAX_PORT_BUF_SIZE 4096 /* max size of per port buffer */
41
42 enum test_message_header {
43 TEST_PASSED = 0,
44 TEST_FAILED = 1,
45 TEST_MESSAGE = 2,
46 TEST_MESSAGE_HEADER_COUNT = 3,
47 };
48
49 static struct handle* ipc_printf_handle;
50 static struct mutex unittest_lock = MUTEX_INITIAL_VALUE(unittest_lock);
51 static struct handle* unittest_handle_set;
52 static thread_t* unittest_thread;
53
get_current_time_ns(void)54 uint64_t get_current_time_ns(void) {
55 return current_time_ns();
56 }
57
send_msg_wait(struct handle * handle,struct ipc_msg_kern * msg)58 static int send_msg_wait(struct handle* handle, struct ipc_msg_kern* msg) {
59 int ret;
60 uint32_t event;
61
62 ASSERT(is_mutex_held(&unittest_lock));
63
64 ret = ipc_send_msg(handle, msg);
65 if (ret != ERR_NOT_ENOUGH_BUFFER) {
66 return ret;
67 }
68
69 ret = handle_wait(handle, &event, INFINITE_TIME);
70 if (ret < 0) {
71 return ret;
72 }
73
74 if (event & IPC_HANDLE_POLL_SEND_UNBLOCKED) {
75 return ipc_send_msg(handle, msg);
76 }
77
78 if (event & IPC_HANDLE_POLL_MSG) {
79 return ERR_BUSY;
80 }
81
82 if (event & IPC_HANDLE_POLL_HUP) {
83 return ERR_CHANNEL_CLOSED;
84 }
85
86 return ret;
87 }
88
89 /**
90 * unittest_printf - Print a message that gets sent back to the client
91 * @fmt: Format string.
92 *
93 * Print a message that gets sent back to the currently connected client. Should
94 * only be called while the run_test function registered with unittest_add runs.
95 * The length of the formatted string is limited to 254 bytes.
96 *
97 * Return: Formatted string length or (negative) error code.
98 */
unittest_printf(const char * fmt,...)99 int unittest_printf(const char* fmt, ...) {
100 char buf[256];
101 struct iovec_kern tx_iov = {buf, 1};
102 struct ipc_msg_kern tx_msg = {1, &tx_iov, 0, NULL};
103 va_list ap;
104 int ret;
105 int slen;
106
107 va_start(ap, fmt);
108 /* Format string into buf[1...]. buf[0] contains the message header. */
109 ret = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap);
110 va_end(ap);
111
112 if (ret < 0) {
113 return ret;
114 }
115
116 /*
117 * vsnprintf returns the length of the string it would produce if the buffer
118 * was big enough. Compute the actual string length by clamping the return
119 * value to the largest string that can fit in the buffer.
120 */
121 slen = MIN(ret, (int)sizeof(buf) - 1 - 1);
122
123 buf[0] = TEST_MESSAGE;
124 tx_iov.iov_len = 1 + slen;
125 mutex_acquire(&unittest_lock);
126 ret = send_msg_wait(ipc_printf_handle, &tx_msg);
127 mutex_release(&unittest_lock);
128 if (ret < 0) {
129 return ret;
130 }
131
132 return slen;
133 }
134
135 /**
136 * unittest_loop - Thread function handling all kernel unit-tests
137 * arg: Unused thread argument.
138 *
139 * Wait on handle-set for a client to connect. When a client connects, run the
140 * test function for the port the client connected to then sent the test status
141 * back to the client. The test function can call unittest_printf to send text
142 * back to the client.
143 *
144 * Return: error code is there was an unexpected error.
145 */
unittest_loop(void * arg)146 static int unittest_loop(void* arg) {
147 int ret;
148 struct handle* chandle;
149 struct handle_ref evt;
150 const uuid_t* unused_uuid_p;
151 struct unittest* test;
152
153 LTRACEF("waiting for connection\n");
154 for (;;) {
155 ret = handle_set_wait(unittest_handle_set, &evt, INFINITE_TIME);
156 if (ret < 0) {
157 TRACEF("handle_set_wait failed: %d\n", ret);
158 break;
159 }
160 test = evt.cookie;
161 LTRACEF("got event (ret=%d): ev=%x handle=%p port=%s\n", ret, evt.emask,
162 evt.handle, test->port_name);
163 if (evt.emask & IPC_HANDLE_POLL_READY) {
164 /* get connection request */
165 ret = ipc_port_accept(evt.handle, &chandle, &unused_uuid_p);
166 LTRACEF("accept returned %d\n", ret);
167 if (ret >= 0) {
168 char tx_buffer[1];
169 struct iovec_kern tx_iov = {
170 tx_buffer,
171 sizeof(tx_buffer),
172 };
173 struct ipc_msg_kern tx_msg = {1, &tx_iov, 0, NULL};
174
175 /* then run unittest test */
176 ipc_printf_handle = chandle;
177 tx_buffer[0] = test->run_test(test) ? TEST_PASSED : TEST_FAILED;
178 mutex_acquire(&unittest_lock);
179 ipc_printf_handle = NULL;
180
181 send_msg_wait(chandle, &tx_msg);
182 mutex_release(&unittest_lock);
183
184 /* and close it */
185 handle_close(chandle);
186 }
187 }
188 }
189
190 return ret;
191 }
192
193 /**
194 * unittest_add_locked - Internal helper function to add a kernel unit-test
195 * @test: See unittest_add.
196 *
197 * unittest_lock must be locked before calling this.
198 *
199 * Return: See unittest_add.
200 */
unittest_add_locked(struct unittest * test)201 static int unittest_add_locked(struct unittest* test) {
202 int ret;
203 struct handle* phandle;
204
205 ASSERT(is_mutex_held(&unittest_lock));
206
207 if (!unittest_handle_set) {
208 unittest_handle_set = handle_set_create();
209 if (!unittest_handle_set) {
210 ret = ERR_NO_MEMORY;
211 goto err_handle_set_create;
212 }
213 }
214 ret = ipc_port_create(&kernel_uuid, test->port_name, 1, MAX_PORT_BUF_SIZE,
215 IPC_PORT_ALLOW_NS_CONNECT | IPC_PORT_ALLOW_TA_CONNECT,
216 &phandle);
217 if (ret) {
218 goto err_port_create;
219 }
220
221 ret = ipc_port_publish(phandle);
222 if (ret) {
223 goto err_port_publish;
224 }
225 handle_incref(phandle);
226 test->_href.handle = phandle;
227 test->_href.emask = ~0U;
228 test->_href.cookie = test;
229 ret = handle_set_attach(unittest_handle_set, &test->_href);
230 if (ret < 0) {
231 goto err_handle_set_attach;
232 }
233 LTRACEF("added port %s handle, %p, to handleset %p\n", test->port_name,
234 test->_href.handle, unittest_handle_set);
235
236 if (!unittest_thread) {
237 unittest_thread = thread_create("unittest", unittest_loop, NULL,
238 HIGH_PRIORITY, DEFAULT_STACK_SIZE);
239 if (!unittest_thread) {
240 ret = ERR_NO_MEMORY;
241 goto err_thread_create;
242 }
243 thread_resume(unittest_thread);
244 }
245 return 0;
246
247 err_thread_create:
248 handle_set_detach_ref(&test->_href);
249 err_handle_set_attach:
250 handle_decref(phandle);
251 err_port_publish:
252 handle_close(phandle);
253 err_port_create:
254 err_handle_set_create:
255 TRACEF("Failed to add unittest: %d\n", ret);
256 return ret;
257 }
258
259 /**
260 * unittest_add - Add a kernel unit-test
261 * @test: Test descriptor with port name and callback to start the test when
262 * a client connects to the port. @test is used after unittest_add
263 * returns so it must not be a temporary allocation.
264 *
265 * Creates a port for @test. when the first test is added, create a handle set
266 * and thread that will be shared between all tests.
267 *
268 * Return: 0 if test was added, error-code otherwise.
269 */
unittest_add(struct unittest * test)270 int unittest_add(struct unittest* test) {
271 int ret;
272
273 mutex_acquire(&unittest_lock);
274 ret = unittest_add_locked(test);
275 mutex_release(&unittest_lock);
276
277 return ret;
278 }
279