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