1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, 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
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #include <trusty/trusty_dev.h>
26 #include <trusty/trusty_ipc.h>
27 #include <trusty/trusty_mem.h>
28 #include <trusty/util.h>
29
30 #define NS_PTE_PHYSADDR(pte) ((pte)&0xFFFFFFFFF000ULL)
31
32 #define QL_TIPC_DEV_RESP 0x8000
33 #define QL_TIPC_DEV_CONNECT 0x1
34 #define QL_TIPC_DEV_GET_EVENT 0x2
35 #define QL_TIPC_DEV_SEND 0x3
36 #define QL_TIPC_DEV_RECV 0x4
37 #define QL_TIPC_DEV_DISCONNECT 0x5
38
39 #define QL_TIPC_DEV_FC_HAS_EVENT 0x100
40
41 #define LOCAL_LOG 0
42
43 struct trusty_ipc_cmd_hdr {
44 uint16_t opcode;
45 uint16_t flags;
46 uint32_t status;
47 uint32_t handle;
48 uint32_t payload_len;
49 uint8_t payload[0];
50 };
51
52 struct trusty_ipc_wait_req {
53 uint64_t reserved;
54 };
55
56 struct trusty_ipc_connect_req {
57 uint64_t cookie;
58 uint64_t reserved;
59 uint8_t name[0];
60 };
61
iovec_size(const struct trusty_ipc_iovec * iovs,size_t iovs_cnt)62 static size_t iovec_size(const struct trusty_ipc_iovec* iovs, size_t iovs_cnt) {
63 size_t i;
64 size_t cb = 0;
65
66 trusty_assert(iovs);
67
68 for (i = 0; i < iovs_cnt; i++) {
69 cb += iovs[i].len;
70 }
71
72 return cb;
73 }
74
iovec_to_buf(void * buf,size_t buf_len,const struct trusty_ipc_iovec * iovs,size_t iovs_cnt)75 static size_t iovec_to_buf(void* buf,
76 size_t buf_len,
77 const struct trusty_ipc_iovec* iovs,
78 size_t iovs_cnt) {
79 size_t i;
80 size_t buf_pos = 0;
81
82 trusty_assert(iovs);
83
84 for (i = 0; i < iovs_cnt; i++) {
85 size_t to_copy = (size_t)iovs[i].len;
86
87 if (!to_copy)
88 continue;
89
90 if (to_copy > buf_len)
91 to_copy = buf_len;
92
93 trusty_memcpy((uint8_t*)buf + buf_pos, iovs[i].base, to_copy);
94
95 buf_pos += to_copy;
96 buf_len -= to_copy;
97
98 if (buf_len == 0)
99 break;
100 }
101
102 return buf_pos;
103 }
104
buf_to_iovec(const struct trusty_ipc_iovec * iovs,size_t iovs_cnt,const void * buf,size_t buf_len)105 static size_t buf_to_iovec(const struct trusty_ipc_iovec* iovs,
106 size_t iovs_cnt,
107 const void* buf,
108 size_t buf_len) {
109 size_t i;
110 size_t copied = 0;
111 const uint8_t* buf_ptr = buf;
112
113 trusty_assert(buf_ptr);
114 trusty_assert(iovs);
115
116 if (iovs_cnt == 0 || buf_len == 0)
117 return 0;
118
119 for (i = 0; i < iovs_cnt; i++) {
120 size_t to_copy = buf_len;
121
122 if (to_copy > iovs[i].len)
123 to_copy = iovs[i].len;
124
125 if (!to_copy)
126 continue;
127
128 trusty_memcpy(iovs[i].base, buf_ptr, to_copy);
129
130 copied += to_copy;
131 buf_ptr += to_copy;
132 buf_len -= to_copy;
133
134 if (buf_len == 0)
135 break;
136 }
137
138 return copied;
139 }
140
check_response(struct trusty_ipc_dev * dev,volatile struct trusty_ipc_cmd_hdr * hdr,uint16_t cmd)141 static int check_response(struct trusty_ipc_dev* dev,
142 volatile struct trusty_ipc_cmd_hdr* hdr,
143 uint16_t cmd) {
144 if (hdr->opcode != (cmd | QL_TIPC_DEV_RESP)) {
145 /* malformed response */
146 trusty_error("%s: malformed response cmd: 0x%x\n", __func__,
147 hdr->opcode);
148 return TRUSTY_ERR_SECOS_ERR;
149 }
150
151 if (hdr->status) {
152 /* secure OS responded with error: TODO need error code */
153 trusty_error("%s: cmd 0x%x: status = %d\n", __func__, hdr->opcode,
154 hdr->status);
155 return TRUSTY_ERR_SECOS_ERR;
156 }
157
158 return TRUSTY_ERR_NONE;
159 }
160
trusty_ipc_dev_create(struct trusty_ipc_dev ** idev,struct trusty_dev * tdev,size_t shared_buf_size)161 int trusty_ipc_dev_create(struct trusty_ipc_dev** idev,
162 struct trusty_dev* tdev,
163 size_t shared_buf_size) {
164 int rc;
165 int rc2;
166 struct trusty_ipc_dev* dev;
167
168 trusty_assert(idev);
169 trusty_assert(!(shared_buf_size % PAGE_SIZE));
170 trusty_debug("%s: Create new Trusty IPC device (%zu)\n", __func__,
171 shared_buf_size);
172
173 /* allocate device context */
174 dev = trusty_calloc(1, sizeof(*dev));
175 if (!dev) {
176 trusty_error("%s: failed to allocate Trusty IPC device\n", __func__);
177 return TRUSTY_ERR_NO_MEMORY;
178 }
179 dev->tdev = tdev;
180
181 /* allocate shared buffer */
182 dev->buf_size = shared_buf_size;
183 dev->buf_vaddr = trusty_alloc_pages(shared_buf_size / PAGE_SIZE);
184 if (!dev->buf_vaddr) {
185 trusty_error("%s: failed to allocate shared memory\n", __func__);
186 rc = TRUSTY_ERR_NO_MEMORY;
187 goto err_alloc_pages;
188 }
189
190 /* Get memory attributes */
191 rc = trusty_encode_page_info(&dev->buf_ns, dev->buf_vaddr);
192 if (rc != 0) {
193 trusty_error("%s: failed to get shared memory attributes\n", __func__);
194 rc = TRUSTY_ERR_GENERIC;
195 goto err_page_info;
196 }
197 /* call secure OS to register shared buffer */
198 rc = trusty_dev_share_memory(dev->tdev, &dev->buf_id, &dev->buf_ns,
199 dev->buf_size / PAGE_SIZE);
200 if (rc != 0) {
201 trusty_error("%s: failed (%d) to share memory\n", __func__, rc);
202 rc = TRUSTY_ERR_SECOS_ERR;
203 goto err_share_memory;
204 }
205
206 rc = trusty_dev_init_ipc(dev->tdev, dev->buf_id, dev->buf_size);
207 if (rc != 0) {
208 trusty_error("%s: failed (%d) to create Trusty IPC device\n", __func__,
209 rc);
210 rc = TRUSTY_ERR_SECOS_ERR;
211 goto err_create_sec_dev;
212 }
213
214 trusty_debug("%s: new Trusty IPC device (%p)\n", __func__, dev);
215
216 *idev = dev;
217 return TRUSTY_ERR_NONE;
218
219 err_page_info:
220 err_create_sec_dev:
221 rc2 = trusty_dev_reclaim_memory(dev->tdev, dev->buf_id);
222 if (rc2) {
223 trusty_fatal("%s: failed to remove shared memory\n", __func__);
224 }
225 err_share_memory:
226 trusty_free_pages(dev->buf_vaddr, dev->buf_size / PAGE_SIZE);
227 err_alloc_pages:
228 trusty_free(dev);
229 return rc;
230 }
231
trusty_ipc_dev_shutdown(struct trusty_ipc_dev * dev)232 void trusty_ipc_dev_shutdown(struct trusty_ipc_dev* dev) {
233 int rc;
234 trusty_assert(dev);
235
236 trusty_debug("%s: shutting down Trusty IPC device (%p)\n", __func__, dev);
237
238 /* shutdown Trusty IPC device */
239 rc = trusty_dev_shutdown_ipc(dev->tdev, dev->buf_id, dev->buf_size);
240 trusty_assert(!rc);
241 if (rc != 0) {
242 trusty_error("%s: failed (%d) to shutdown Trusty IPC device\n",
243 __func__, rc);
244 }
245 rc = trusty_dev_reclaim_memory(dev->tdev, dev->buf_id);
246 if (rc) {
247 trusty_fatal("%s: failed to remove shared memory\n", __func__);
248 }
249 trusty_free_pages(dev->buf_vaddr, dev->buf_size / PAGE_SIZE);
250 trusty_free(dev);
251 }
252
trusty_ipc_dev_connect(struct trusty_ipc_dev * dev,const char * port,uint64_t cookie)253 int trusty_ipc_dev_connect(struct trusty_ipc_dev* dev,
254 const char* port,
255 uint64_t cookie) {
256 int rc;
257 size_t port_len;
258 volatile struct trusty_ipc_cmd_hdr* cmd;
259 struct trusty_ipc_connect_req* req;
260
261 trusty_assert(dev);
262 trusty_assert(port);
263
264 trusty_debug("%s: connecting to '%s'\n", __func__, port);
265
266 /* check port name length */
267 port_len = trusty_strlen(port) + 1;
268 if (port_len > (dev->buf_size - sizeof(*cmd) - sizeof(*req))) {
269 /* it would not fit into buffer */
270 trusty_error("%s: port name is too long (%zu)\n", __func__, port_len);
271 return TRUSTY_ERR_INVALID_ARGS;
272 }
273
274 /* prepare command */
275 cmd = dev->buf_vaddr;
276 trusty_memset((void*)cmd, 0, sizeof(*cmd));
277 cmd->opcode = QL_TIPC_DEV_CONNECT;
278
279 /* prepare payload */
280 req = (struct trusty_ipc_connect_req*)cmd->payload;
281 trusty_memset((void*)req, 0, sizeof(*req));
282 req->cookie = cookie;
283 trusty_strcpy((char*)req->name, port);
284 cmd->payload_len = sizeof(*req) + port_len;
285
286 /* call secure os */
287 rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id,
288 sizeof(*cmd) + cmd->payload_len);
289 if (rc) {
290 /* secure OS returned an error */
291 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
292 return TRUSTY_ERR_SECOS_ERR;
293 }
294
295 rc = check_response(dev, cmd, QL_TIPC_DEV_CONNECT);
296 if (rc) {
297 trusty_error("%s: connect cmd failed (%d)\n", __func__, rc);
298 return rc;
299 }
300
301 /* success */
302 return cmd->handle;
303 }
304
trusty_ipc_dev_close(struct trusty_ipc_dev * dev,handle_t handle)305 int trusty_ipc_dev_close(struct trusty_ipc_dev* dev, handle_t handle) {
306 int rc;
307 volatile struct trusty_ipc_cmd_hdr* cmd;
308
309 trusty_assert(dev);
310
311 trusty_debug("%s: chan %d: closing\n", __func__, handle);
312
313 /* prepare command */
314 cmd = dev->buf_vaddr;
315 trusty_memset((void*)cmd, 0, sizeof(*cmd));
316 cmd->opcode = QL_TIPC_DEV_DISCONNECT;
317 cmd->handle = handle;
318 /* no payload */
319
320 /* call into secure os */
321 rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id,
322 sizeof(*cmd) + cmd->payload_len);
323 if (rc) {
324 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
325 return TRUSTY_ERR_SECOS_ERR;
326 }
327
328 rc = check_response(dev, cmd, QL_TIPC_DEV_DISCONNECT);
329 if (rc) {
330 trusty_error("%s: disconnect cmd failed (%d)\n", __func__, rc);
331 return rc;
332 }
333
334 trusty_debug("%s: chan %d: closed\n", __func__, handle);
335
336 return TRUSTY_ERR_NONE;
337 }
338
trusty_ipc_dev_has_event(struct trusty_ipc_dev * dev,handle_t chan)339 bool trusty_ipc_dev_has_event(struct trusty_ipc_dev* dev, handle_t chan) {
340 int rc;
341 bool has_event;
342 volatile struct trusty_ipc_cmd_hdr* cmd;
343
344 trusty_assert(dev);
345
346 /* prepare command */
347 cmd = dev->buf_vaddr;
348 trusty_memset((void*)cmd, 0, sizeof(*cmd));
349 cmd->opcode = QL_TIPC_DEV_FC_HAS_EVENT;
350 cmd->handle = chan;
351
352 /* prepare payload */
353 cmd->payload_len = 0;
354
355 /* call into secure os */
356 rc = trusty_dev_exec_fc_ipc(dev->tdev, dev->buf_id,
357 sizeof(*cmd) + cmd->payload_len);
358 if (rc) {
359 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
360 return false;
361 }
362
363 rc = check_response(dev, cmd, QL_TIPC_DEV_FC_HAS_EVENT);
364 if (rc) {
365 trusty_error("%s: get event cmd failed (%d)\n", __func__, rc);
366 return false;
367 }
368
369 if ((size_t)cmd->payload_len < sizeof(has_event)) {
370 trusty_error("%s: invalid response length (%zd)\n", __func__,
371 (size_t)cmd->payload_len);
372 return false;
373 }
374
375 /* copy out event */
376 trusty_memcpy(&has_event, (const void*)cmd->payload, sizeof(has_event));
377 return has_event;
378 }
379
trusty_ipc_dev_get_event(struct trusty_ipc_dev * dev,handle_t chan,struct trusty_ipc_event * event)380 int trusty_ipc_dev_get_event(struct trusty_ipc_dev* dev,
381 handle_t chan,
382 struct trusty_ipc_event* event) {
383 int rc;
384 volatile struct trusty_ipc_cmd_hdr* cmd;
385
386 trusty_assert(dev);
387 trusty_assert(event);
388
389 /* prepare command */
390 cmd = dev->buf_vaddr;
391 trusty_memset((void*)cmd, 0, sizeof(*cmd));
392 cmd->opcode = QL_TIPC_DEV_GET_EVENT;
393 cmd->handle = chan;
394
395 /* prepare payload */
396 trusty_memset((void*)cmd->payload, 0, sizeof(struct trusty_ipc_wait_req));
397 cmd->payload_len = sizeof(struct trusty_ipc_wait_req);
398
399 /* call into secure os */
400 rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id,
401 sizeof(*cmd) + cmd->payload_len);
402 if (rc) {
403 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
404 return TRUSTY_ERR_SECOS_ERR;
405 }
406
407 rc = check_response(dev, cmd, QL_TIPC_DEV_GET_EVENT);
408 if (rc) {
409 trusty_error("%s: get event cmd failed (%d)\n", __func__, rc);
410 return rc;
411 }
412
413 if ((size_t)cmd->payload_len < sizeof(*event)) {
414 trusty_error("%s: invalid response length (%zd)\n", __func__,
415 (size_t)cmd->payload_len);
416 return TRUSTY_ERR_SECOS_ERR;
417 }
418
419 /* copy out event */
420 trusty_memcpy(event, (const void*)cmd->payload, sizeof(*event));
421 return TRUSTY_ERR_NONE;
422 }
423
trusty_ipc_dev_send(struct trusty_ipc_dev * dev,handle_t chan,const struct trusty_ipc_iovec * iovs,size_t iovs_cnt)424 int trusty_ipc_dev_send(struct trusty_ipc_dev* dev,
425 handle_t chan,
426 const struct trusty_ipc_iovec* iovs,
427 size_t iovs_cnt) {
428 int rc;
429 size_t msg_size;
430 volatile struct trusty_ipc_cmd_hdr* cmd;
431
432 trusty_assert(dev);
433 /* calc message length */
434 msg_size = iovec_size(iovs, iovs_cnt);
435 if (msg_size > dev->buf_size - sizeof(*cmd)) {
436 /* msg is too big to fit provided buffer */
437 trusty_error("%s: chan %d: msg is too long (%zu)\n", __func__, chan,
438 msg_size);
439 return TRUSTY_ERR_MSG_TOO_BIG;
440 }
441
442 /* prepare command */
443 cmd = dev->buf_vaddr;
444 trusty_memset((void*)cmd, 0, sizeof(*cmd));
445 cmd->opcode = QL_TIPC_DEV_SEND;
446 cmd->handle = chan;
447
448 /* copy in message data */
449 cmd->payload_len = (uint32_t)msg_size;
450 msg_size = iovec_to_buf(dev->buf_vaddr + sizeof(*cmd),
451 dev->buf_size - sizeof(*cmd), iovs, iovs_cnt);
452 trusty_assert(msg_size == (size_t)cmd->payload_len);
453
454 /* call into secure os */
455 rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id,
456 sizeof(*cmd) + cmd->payload_len);
457 if (rc < 0) {
458 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
459 return TRUSTY_ERR_SECOS_ERR;
460 }
461
462 rc = check_response(dev, cmd, QL_TIPC_DEV_SEND);
463 if (rc) {
464 trusty_error("%s: send msg failed (%d)\n", __func__, rc);
465 }
466
467 return rc;
468 }
469
trusty_ipc_dev_recv(struct trusty_ipc_dev * dev,handle_t chan,const struct trusty_ipc_iovec * iovs,size_t iovs_cnt)470 int trusty_ipc_dev_recv(struct trusty_ipc_dev* dev,
471 handle_t chan,
472 const struct trusty_ipc_iovec* iovs,
473 size_t iovs_cnt) {
474 int rc;
475 size_t copied;
476 volatile struct trusty_ipc_cmd_hdr* cmd;
477
478 trusty_assert(dev);
479
480 /* prepare command */
481 cmd = dev->buf_vaddr;
482 trusty_memset((void*)cmd, 0, sizeof(*cmd));
483 cmd->opcode = QL_TIPC_DEV_RECV;
484 cmd->handle = chan;
485 /* no payload */
486
487 /* call into secure os */
488 rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id,
489 sizeof(*cmd) + cmd->payload_len);
490 if (rc < 0) {
491 trusty_error("%s: secure OS returned (%d)\n", __func__, rc);
492 return TRUSTY_ERR_SECOS_ERR;
493 }
494
495 rc = check_response(dev, cmd, QL_TIPC_DEV_RECV);
496 if (rc) {
497 trusty_error("%s: recv cmd failed (%d)\n", __func__, rc);
498 return rc;
499 }
500
501 /* copy data out to proper destination */
502 copied = buf_to_iovec(iovs, iovs_cnt, (const void*)cmd->payload,
503 cmd->payload_len);
504 if (copied != (size_t)cmd->payload_len) {
505 /* msg is too big to fit provided buffer */
506 trusty_error("%s: chan %d: buffer too small (%zu vs. %zu)\n", __func__,
507 chan, copied, (size_t)cmd->payload_len);
508 return TRUSTY_ERR_MSG_TOO_BIG;
509 }
510
511 return (int)copied;
512 }
513
trusty_ipc_dev_idle(struct trusty_ipc_dev * dev,bool event_poll)514 void trusty_ipc_dev_idle(struct trusty_ipc_dev* dev, bool event_poll) {
515 trusty_idle(dev->tdev, event_poll);
516 }
517