• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&notifier);
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