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