1 /*
2 * Copyright (c) 2021, Google Inc. All rights reserved
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 <assert.h>
25 #include <err.h>
26 #include <interface/metrics/consumer.h>
27 #include <kernel/mutex.h>
28 #include <lib/dpc.h>
29 #include <lib/trusty/handle.h>
30 #include <lib/trusty/ipc.h>
31 #include <lib/trusty/ipc_msg.h>
32 #include <lib/trusty/trusty_app.h>
33 #include <lk/init.h>
34 #include <lk/trace.h>
35 #include <string.h>
36 #include <trusty/uuid.h>
37
38 #define LOCAL_TRACE (0)
39
40 /*
41 * Format of the payload is "<UUID>:<app name>", with neither UUID nor app name
42 * being null-terminated. However, unlike APP_NAME_MAX_SIZE, UUID_STR_SIZE
43 * counts the null character. Hence, the maximum size of an app name is
44 * METRICS_MAX_APP_ID_LEN - UUID_STR_SIZE.
45 */
46 static_assert(UUID_STR_SIZE <= METRICS_MAX_APP_ID_LEN);
47 #define APP_NAME_MAX_SIZE (METRICS_MAX_APP_ID_LEN - UUID_STR_SIZE)
48
49 /**
50 * enum chan_state - states of the metrics consumer channel event handler
51 * CHAN_STATE_WAITING_CHAN_READY:
52 * Inital state of the channel handler. At this point we are waiting for an
53 * IPC_HANDLE_POLL_READY channel event that signifies that metrics consumer
54 * connection is ready for use. After consuming this event, we transition
55 * to %CHAN_STATE_IDLE state.
56 * CHAN_STATE_IDLE:
57 * While in this state we (2) can not consume any events from the channel
58 * (1) can only send one message over the channel. Once a message is sent,
59 * we transition to either %CHAN_STATE_WAITING_CRASH_RESP or
60 * %CHAN_STATE_WAITING_EXIT_RESP or %CHAN_STATE_WAITING_EVENT_DROP_RESP
61 * depending on what message was sent.
62 * CHAN_STATE_WAITING_CRASH_RESP:
63 * In this state we are waiting for a response to a message about an app
64 * crash. After receiving the response message, we transition to
65 * %CHAN_STATE_IDLE state.
66 * CHAN_STATE_WAITING_EXIT_RESP:
67 * In this state we are waiting for a response to a message about an app
68 * crash. After receiving the response message, we transition to
69 * %CHAN_STATE_IDLE state.
70 * CHAN_STATE_WAITING_EVENT_DROP_RESP:
71 * In this state we are waiting for a response to a message about an event
72 * drop. After receiving the response message, we transition to
73 * %CHAN_STATE_IDLE state.
74 */
75 enum chan_state {
76 CHAN_STATE_WAITING_CHAN_READY = 0,
77 CHAN_STATE_IDLE = 1,
78 CHAN_STATE_WAITING_CRASH_RESP = 2,
79 CHAN_STATE_WAITING_EXIT_RESP = 3,
80 CHAN_STATE_WAITING_EVENT_DROP_RESP = 4,
81 };
82
83 struct metrics_ctx {
84 struct handle* chan;
85 enum chan_state chan_state;
86 bool event_dropped;
87 };
88
89 static struct metrics_ctx ctx;
90 static mutex_t ctx_lock = MUTEX_INITIAL_VALUE(ctx_lock);
91
recv_resp(struct handle * chan,uint32_t cmd)92 static int recv_resp(struct handle* chan, uint32_t cmd) {
93 int rc;
94 struct ipc_msg_info msg_info;
95 struct metrics_resp resp;
96
97 rc = ipc_get_msg(chan, &msg_info);
98 if (rc != NO_ERROR) {
99 TRACEF("failed (%d) to get message\n", rc);
100 return rc;
101 }
102
103 struct iovec_kern iov = {
104 .iov_base = &resp,
105 .iov_len = sizeof(resp),
106 };
107 struct ipc_msg_kern ipc_msg = {
108 .num_iov = 1,
109 .iov = &iov,
110 .num_handles = 0,
111 .handles = NULL,
112 };
113 rc = ipc_read_msg(chan, msg_info.id, 0, &ipc_msg);
114 ipc_put_msg(chan, msg_info.id);
115
116 if (rc < 0) {
117 TRACEF("failed (%d) ipc_read_msg().\n", rc);
118 return rc;
119 }
120
121 if (rc != sizeof(resp)) {
122 TRACEF("unexpected number of bytes received: %d.\n", rc);
123 return ERR_BAD_LEN;
124 }
125
126 if (resp.cmd != (cmd | METRICS_CMD_RESP_BIT)) {
127 TRACEF("unknown command received: %u %u.\n", resp.cmd, cmd);
128 return ERR_CMD_UNKNOWN;
129 }
130
131 if (resp.status != METRICS_NO_ERROR) {
132 TRACEF("event report failure: %d.\n", resp.status);
133 /* This error is not severe enough to close the connection. */
134 }
135
136 return NO_ERROR;
137 }
138
send_req(struct handle * chan,struct ipc_msg_kern * ipc_msg,size_t total_len)139 static int send_req(struct handle* chan,
140 struct ipc_msg_kern* ipc_msg,
141 size_t total_len) {
142 int rc = ipc_send_msg(chan, ipc_msg);
143 if (rc < 0) {
144 TRACEF("failed (%d) to send message\n", rc);
145 return rc;
146 }
147
148 if (rc != (int)total_len) {
149 TRACEF("unexpected number of bytes sent: %d\n", rc);
150 return ERR_BAD_LEN;
151 }
152
153 return NO_ERROR;
154 }
155
report_crash(struct handle * chan,struct trusty_app * app,uint32_t crash_reason)156 static int report_crash(struct handle* chan,
157 struct trusty_app* app,
158 uint32_t crash_reason) {
159 int rc;
160 struct metrics_req req = {};
161 struct metrics_report_crash_req args = {};
162 size_t total_len;
163
164 DEBUG_ASSERT(is_mutex_held(&ctx_lock));
165
166 uuid_to_str(&app->props.uuid, args.app_id);
167
168 req.cmd = METRICS_CMD_REPORT_CRASH;
169 args.crash_reason = crash_reason;
170
171 struct iovec_kern iovs[] = {
172 {
173 .iov_base = &req,
174 .iov_len = sizeof(req),
175 },
176 {
177 .iov_base = &args,
178 .iov_len = sizeof(args),
179 },
180 };
181 struct ipc_msg_kern ipc_msg = {
182 .num_iov = countof(iovs),
183 .iov = iovs,
184 };
185
186 total_len = sizeof(req) + sizeof(args);
187 rc = send_req(chan, &ipc_msg, total_len);
188 if (rc != NO_ERROR) {
189 TRACEF("failed (%d) report app crash\n", rc);
190 return rc;
191 }
192
193 return NO_ERROR;
194 }
195
report_exit(struct handle * chan,struct trusty_app * app,uint32_t exit_code)196 static int report_exit(struct handle* chan,
197 struct trusty_app* app,
198 uint32_t exit_code) {
199 int rc;
200 struct metrics_req req = {};
201 struct metrics_report_exit_req args = {};
202 size_t total_len;
203
204 DEBUG_ASSERT(is_mutex_held(&ctx_lock));
205
206 uuid_to_str(&app->props.uuid, args.app_id);
207
208 req.cmd = METRICS_CMD_REPORT_EXIT;
209 args.exit_code = exit_code;
210
211 struct iovec_kern iovs[] = {
212 {
213 .iov_base = &req,
214 .iov_len = sizeof(req),
215 },
216 {
217 .iov_base = &args,
218 .iov_len = sizeof(args),
219 },
220 };
221 struct ipc_msg_kern ipc_msg = {
222 .num_iov = countof(iovs),
223 .iov = iovs,
224 };
225
226 total_len = sizeof(req) + sizeof(args);
227 rc = send_req(chan, &ipc_msg, total_len);
228 if (rc != NO_ERROR) {
229 TRACEF("failed (%d) report app exit\n", rc);
230 return rc;
231 }
232
233 return NO_ERROR;
234 }
235
report_event_drop(struct handle * chan)236 static int report_event_drop(struct handle* chan) {
237 int rc;
238 struct metrics_req req;
239
240 DEBUG_ASSERT(is_mutex_held(&ctx_lock));
241
242 req.cmd = METRICS_CMD_REPORT_EVENT_DROP;
243 req.reserved = 0;
244
245 struct iovec_kern iov = {
246 .iov_base = &req,
247 .iov_len = sizeof(req),
248 };
249 struct ipc_msg_kern ipc_msg = {
250 .num_iov = 1,
251 .iov = &iov,
252 };
253
254 rc = send_req(chan, &ipc_msg, sizeof(req));
255 if (rc != NO_ERROR) {
256 TRACEF("failed (%d) report event drop\n", rc);
257 return rc;
258 }
259
260 return NO_ERROR;
261 }
262
on_ta_crash(struct trusty_app * app,uint32_t reason,bool is_crash)263 static int on_ta_crash(struct trusty_app* app, uint32_t reason, bool is_crash) {
264 int rc;
265
266 mutex_acquire(&ctx_lock);
267
268 if (ctx.chan_state != CHAN_STATE_IDLE) {
269 TRACEF("there is a metrics event still in progress or metrics TA "
270 "is unavailable\n");
271 ctx.event_dropped = true;
272 goto out;
273 }
274
275 if (!ctx.chan) {
276 TRACEF("failed get metrics consumer channel\n");
277 goto out;
278 }
279
280 if(is_crash) {
281 rc = report_crash(ctx.chan, app, reason);
282 ctx.chan_state = CHAN_STATE_WAITING_CRASH_RESP;
283 }
284 else {
285 rc = report_exit(ctx.chan, app, reason);
286 ctx.chan_state = CHAN_STATE_WAITING_EXIT_RESP;
287 }
288 if (rc != NO_ERROR) {
289 TRACEF("failed (%d) report app crash\n", rc);
290 goto err;
291 }
292
293 goto out;
294
295 err:
296 handle_close(ctx.chan);
297 ctx.chan = NULL;
298 out:
299 mutex_release(&ctx_lock);
300 /*
301 * Returning an error here will bring down the kernel. Metrics reporting
302 * isn't critical. So, we always return NO_ERROR. If something goes wrong,
303 * printing an error should suffice.
304 */
305 return NO_ERROR;
306 }
307
308 static struct trusty_app_notifier notifier = {
309 .crash = on_ta_crash,
310 };
311
handle_chan(struct dpc * work)312 static void handle_chan(struct dpc* work) {
313 int rc;
314 uint32_t event;
315
316 mutex_acquire(&ctx_lock);
317
318 event = ctx.chan->ops->poll(ctx.chan, ~0U, true);
319 if (event & IPC_HANDLE_POLL_HUP) {
320 TRACEF("received IPC_HANDLE_POLL_HUP, closing channel\n");
321 goto err;
322 }
323
324 switch (ctx.chan_state) {
325 case CHAN_STATE_WAITING_CHAN_READY:
326 if (!(event & IPC_HANDLE_POLL_READY)) {
327 TRACEF("unexpected channel event: 0x%x\n", event);
328 goto err;
329 }
330
331 ctx.chan_state = CHAN_STATE_IDLE;
332 goto out;
333
334 case CHAN_STATE_IDLE:
335 TRACEF("unexpected channel event: 0x%x\n", event);
336 goto err;
337
338 case CHAN_STATE_WAITING_CRASH_RESP:
339 if (!(event & IPC_HANDLE_POLL_MSG)) {
340 TRACEF("unexpected channel event: 0x%x\n", event);
341 goto err;
342 }
343
344 rc = recv_resp(ctx.chan, METRICS_CMD_REPORT_CRASH);
345 if (rc != NO_ERROR) {
346 TRACEF("failed (%d) receive response\n", rc);
347 goto err;
348 }
349
350 ctx.chan_state = CHAN_STATE_IDLE;
351
352 if (ctx.event_dropped) {
353 rc = report_event_drop(ctx.chan);
354 if (rc != NO_ERROR) {
355 TRACEF("failed (%d) report event drop\n", rc);
356 goto err;
357 }
358 ctx.chan_state = CHAN_STATE_WAITING_EVENT_DROP_RESP;
359 goto out;
360 }
361
362 goto out;
363
364 case CHAN_STATE_WAITING_EXIT_RESP:
365 if (!(event & IPC_HANDLE_POLL_MSG)) {
366 TRACEF("unexpected channel event: 0x%x\n", event);
367 goto err;
368 }
369
370 rc = recv_resp(ctx.chan, METRICS_CMD_REPORT_EXIT);
371 if (rc != NO_ERROR) {
372 TRACEF("failed (%d) receive response\n", rc);
373 goto err;
374 }
375
376 ctx.chan_state = CHAN_STATE_IDLE;
377
378 if (ctx.event_dropped) {
379 rc = report_event_drop(ctx.chan);
380 if (rc != NO_ERROR) {
381 TRACEF("failed (%d) report event drop\n", rc);
382 goto err;
383 }
384 ctx.chan_state = CHAN_STATE_WAITING_EVENT_DROP_RESP;
385 goto out;
386 }
387
388 goto out;
389
390 case CHAN_STATE_WAITING_EVENT_DROP_RESP:
391 if (!(event & IPC_HANDLE_POLL_MSG)) {
392 TRACEF("unexpected channel event: 0x%x\n", event);
393 goto err;
394 }
395
396 rc = recv_resp(ctx.chan, METRICS_CMD_REPORT_EVENT_DROP);
397 if (rc != NO_ERROR) {
398 TRACEF("failed (%d) receive response\n", rc);
399 goto err;
400 }
401
402 ctx.chan_state = CHAN_STATE_IDLE;
403 ctx.event_dropped = false;
404 goto out;
405 }
406
407 err:
408 handle_close(ctx.chan);
409 ctx.chan = NULL;
410 out:
411 mutex_release(&ctx_lock);
412 }
413
414 static struct dpc chan_event_work = {
415 .node = LIST_INITIAL_CLEARED_VALUE,
416 .cb = handle_chan,
417 };
418
on_handle_event(struct handle_waiter * waiter)419 static void on_handle_event(struct handle_waiter* waiter) {
420 int rc = dpc_enqueue_work(NULL, &chan_event_work, false);
421 if (rc != NO_ERROR) {
422 TRACEF("failed (%d) to enqueue dpc work\n", rc);
423 }
424 }
425
426 static struct handle_waiter waiter = {
427 .node = LIST_INITIAL_CLEARED_VALUE,
428 .notify_proc = on_handle_event,
429 };
430
metrics_init(uint level)431 static void metrics_init(uint level) {
432 int rc = ipc_port_connect_async(&kernel_uuid, METRICS_CONSUMER_PORT,
433 IPC_PORT_PATH_MAX,
434 IPC_CONNECT_WAIT_FOR_PORT, &ctx.chan);
435 if (rc) {
436 TRACEF("failed (%d) to connect to port\n", rc);
437 goto err_port_connect;
438 }
439
440 rc = trusty_register_app_notifier(¬ifier);
441 if (rc) {
442 TRACEF("failed (%d) to register app notifier\n", rc);
443 goto err_app_notifier;
444 }
445
446 ctx.chan_state = CHAN_STATE_WAITING_CHAN_READY;
447 handle_add_waiter(ctx.chan, &waiter);
448
449 return;
450
451 err_app_notifier:
452 handle_close(ctx.chan);
453 ctx.chan = NULL;
454 err_port_connect:
455 return;
456 }
457
458 /* Need to init before (LK_INIT_LEVEL_APPS - 1) to register an app notifier. */
459 LK_INIT_HOOK(metrics, metrics_init, LK_INIT_LEVEL_APPS - 2);
460