/* * Copyright (C) 2016 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #define NS_PTE_PHYSADDR(pte) ((pte)&0xFFFFFFFFF000ULL) #define QL_TIPC_DEV_RESP 0x8000 #define QL_TIPC_DEV_CONNECT 0x1 #define QL_TIPC_DEV_GET_EVENT 0x2 #define QL_TIPC_DEV_SEND 0x3 #define QL_TIPC_DEV_RECV 0x4 #define QL_TIPC_DEV_DISCONNECT 0x5 #define QL_TIPC_DEV_FC_HAS_EVENT 0x100 #define LOCAL_LOG 0 struct trusty_ipc_cmd_hdr { uint16_t opcode; uint16_t flags; uint32_t status; uint32_t handle; uint32_t payload_len; uint8_t payload[0]; }; struct trusty_ipc_wait_req { uint64_t reserved; }; struct trusty_ipc_connect_req { uint64_t cookie; uint64_t reserved; uint8_t name[0]; }; static size_t iovec_size(const struct trusty_ipc_iovec* iovs, size_t iovs_cnt) { size_t i; size_t cb = 0; trusty_assert(iovs); for (i = 0; i < iovs_cnt; i++) { cb += iovs[i].len; } return cb; } static size_t iovec_to_buf(void* buf, size_t buf_len, const struct trusty_ipc_iovec* iovs, size_t iovs_cnt) { size_t i; size_t buf_pos = 0; trusty_assert(iovs); for (i = 0; i < iovs_cnt; i++) { size_t to_copy = (size_t)iovs[i].len; if (!to_copy) continue; if (to_copy > buf_len) to_copy = buf_len; trusty_memcpy((uint8_t*)buf + buf_pos, iovs[i].base, to_copy); buf_pos += to_copy; buf_len -= to_copy; if (buf_len == 0) break; } return buf_pos; } static size_t buf_to_iovec(const struct trusty_ipc_iovec* iovs, size_t iovs_cnt, const void* buf, size_t buf_len) { size_t i; size_t copied = 0; const uint8_t* buf_ptr = buf; trusty_assert(buf_ptr); trusty_assert(iovs); if (iovs_cnt == 0 || buf_len == 0) return 0; for (i = 0; i < iovs_cnt; i++) { size_t to_copy = buf_len; if (to_copy > iovs[i].len) to_copy = iovs[i].len; if (!to_copy) continue; trusty_memcpy(iovs[i].base, buf_ptr, to_copy); copied += to_copy; buf_ptr += to_copy; buf_len -= to_copy; if (buf_len == 0) break; } return copied; } static int check_response(struct trusty_ipc_dev* dev, volatile struct trusty_ipc_cmd_hdr* hdr, uint16_t cmd) { if (hdr->opcode != (cmd | QL_TIPC_DEV_RESP)) { /* malformed response */ trusty_error("%s: malformed response cmd: 0x%x\n", __func__, hdr->opcode); return TRUSTY_ERR_SECOS_ERR; } if (hdr->status) { /* secure OS responded with error: TODO need error code */ trusty_error("%s: cmd 0x%x: status = %d\n", __func__, hdr->opcode, hdr->status); return TRUSTY_ERR_SECOS_ERR; } return TRUSTY_ERR_NONE; } int trusty_ipc_dev_create(struct trusty_ipc_dev** idev, struct trusty_dev* tdev, size_t shared_buf_size) { int rc; int rc2; struct trusty_ipc_dev* dev; trusty_assert(idev); trusty_assert(!(shared_buf_size % PAGE_SIZE)); trusty_debug("%s: Create new Trusty IPC device (%zu)\n", __func__, shared_buf_size); /* allocate device context */ dev = trusty_calloc(1, sizeof(*dev)); if (!dev) { trusty_error("%s: failed to allocate Trusty IPC device\n", __func__); return TRUSTY_ERR_NO_MEMORY; } dev->tdev = tdev; /* allocate shared buffer */ dev->buf_size = shared_buf_size; dev->buf_vaddr = trusty_alloc_pages(shared_buf_size / PAGE_SIZE); if (!dev->buf_vaddr) { trusty_error("%s: failed to allocate shared memory\n", __func__); rc = TRUSTY_ERR_NO_MEMORY; goto err_alloc_pages; } /* Get memory attributes */ rc = trusty_encode_page_info(&dev->buf_ns, dev->buf_vaddr); if (rc != 0) { trusty_error("%s: failed to get shared memory attributes\n", __func__); rc = TRUSTY_ERR_GENERIC; goto err_page_info; } /* call secure OS to register shared buffer */ rc = trusty_dev_share_memory(dev->tdev, &dev->buf_id, &dev->buf_ns, dev->buf_size / PAGE_SIZE); if (rc != 0) { trusty_error("%s: failed (%d) to share memory\n", __func__, rc); rc = TRUSTY_ERR_SECOS_ERR; goto err_share_memory; } rc = trusty_dev_init_ipc(dev->tdev, dev->buf_id, dev->buf_size); if (rc != 0) { trusty_error("%s: failed (%d) to create Trusty IPC device\n", __func__, rc); rc = TRUSTY_ERR_SECOS_ERR; goto err_create_sec_dev; } trusty_debug("%s: new Trusty IPC device (%p)\n", __func__, dev); *idev = dev; return TRUSTY_ERR_NONE; err_page_info: err_create_sec_dev: rc2 = trusty_dev_reclaim_memory(dev->tdev, dev->buf_id); if (rc2) { trusty_fatal("%s: failed to remove shared memory\n", __func__); } err_share_memory: trusty_free_pages(dev->buf_vaddr, dev->buf_size / PAGE_SIZE); err_alloc_pages: trusty_free(dev); return rc; } void trusty_ipc_dev_shutdown(struct trusty_ipc_dev* dev) { int rc; trusty_assert(dev); trusty_debug("%s: shutting down Trusty IPC device (%p)\n", __func__, dev); /* shutdown Trusty IPC device */ rc = trusty_dev_shutdown_ipc(dev->tdev, dev->buf_id, dev->buf_size); trusty_assert(!rc); if (rc != 0) { trusty_error("%s: failed (%d) to shutdown Trusty IPC device\n", __func__, rc); } rc = trusty_dev_reclaim_memory(dev->tdev, dev->buf_id); if (rc) { trusty_fatal("%s: failed to remove shared memory\n", __func__); } trusty_free_pages(dev->buf_vaddr, dev->buf_size / PAGE_SIZE); trusty_free(dev); } int trusty_ipc_dev_connect(struct trusty_ipc_dev* dev, const char* port, uint64_t cookie) { int rc; size_t port_len; volatile struct trusty_ipc_cmd_hdr* cmd; struct trusty_ipc_connect_req* req; trusty_assert(dev); trusty_assert(port); trusty_debug("%s: connecting to '%s'\n", __func__, port); /* check port name length */ port_len = trusty_strlen(port) + 1; if (port_len > (dev->buf_size - sizeof(*cmd) - sizeof(*req))) { /* it would not fit into buffer */ trusty_error("%s: port name is too long (%zu)\n", __func__, port_len); return TRUSTY_ERR_INVALID_ARGS; } /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_CONNECT; /* prepare payload */ req = (struct trusty_ipc_connect_req*)cmd->payload; trusty_memset((void*)req, 0, sizeof(*req)); req->cookie = cookie; trusty_strcpy((char*)req->name, port); cmd->payload_len = sizeof(*req) + port_len; /* call secure os */ rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc) { /* secure OS returned an error */ trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return TRUSTY_ERR_SECOS_ERR; } rc = check_response(dev, cmd, QL_TIPC_DEV_CONNECT); if (rc) { trusty_error("%s: connect cmd failed (%d)\n", __func__, rc); return rc; } /* success */ return cmd->handle; } int trusty_ipc_dev_close(struct trusty_ipc_dev* dev, handle_t handle) { int rc; volatile struct trusty_ipc_cmd_hdr* cmd; trusty_assert(dev); trusty_debug("%s: chan %d: closing\n", __func__, handle); /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_DISCONNECT; cmd->handle = handle; /* no payload */ /* call into secure os */ rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc) { trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return TRUSTY_ERR_SECOS_ERR; } rc = check_response(dev, cmd, QL_TIPC_DEV_DISCONNECT); if (rc) { trusty_error("%s: disconnect cmd failed (%d)\n", __func__, rc); return rc; } trusty_debug("%s: chan %d: closed\n", __func__, handle); return TRUSTY_ERR_NONE; } bool trusty_ipc_dev_has_event(struct trusty_ipc_dev* dev, handle_t chan) { int rc; bool has_event; volatile struct trusty_ipc_cmd_hdr* cmd; trusty_assert(dev); /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_FC_HAS_EVENT; cmd->handle = chan; /* prepare payload */ cmd->payload_len = 0; /* call into secure os */ rc = trusty_dev_exec_fc_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc) { trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return false; } rc = check_response(dev, cmd, QL_TIPC_DEV_FC_HAS_EVENT); if (rc) { trusty_error("%s: get event cmd failed (%d)\n", __func__, rc); return false; } if ((size_t)cmd->payload_len < sizeof(has_event)) { trusty_error("%s: invalid response length (%zd)\n", __func__, (size_t)cmd->payload_len); return false; } /* copy out event */ trusty_memcpy(&has_event, (const void*)cmd->payload, sizeof(has_event)); return has_event; } int trusty_ipc_dev_get_event(struct trusty_ipc_dev* dev, handle_t chan, struct trusty_ipc_event* event) { int rc; volatile struct trusty_ipc_cmd_hdr* cmd; trusty_assert(dev); trusty_assert(event); /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_GET_EVENT; cmd->handle = chan; /* prepare payload */ trusty_memset((void*)cmd->payload, 0, sizeof(struct trusty_ipc_wait_req)); cmd->payload_len = sizeof(struct trusty_ipc_wait_req); /* call into secure os */ rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc) { trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return TRUSTY_ERR_SECOS_ERR; } rc = check_response(dev, cmd, QL_TIPC_DEV_GET_EVENT); if (rc) { trusty_error("%s: get event cmd failed (%d)\n", __func__, rc); return rc; } if ((size_t)cmd->payload_len < sizeof(*event)) { trusty_error("%s: invalid response length (%zd)\n", __func__, (size_t)cmd->payload_len); return TRUSTY_ERR_SECOS_ERR; } /* copy out event */ trusty_memcpy(event, (const void*)cmd->payload, sizeof(*event)); return TRUSTY_ERR_NONE; } int trusty_ipc_dev_send(struct trusty_ipc_dev* dev, handle_t chan, const struct trusty_ipc_iovec* iovs, size_t iovs_cnt) { int rc; size_t msg_size; volatile struct trusty_ipc_cmd_hdr* cmd; trusty_assert(dev); /* calc message length */ msg_size = iovec_size(iovs, iovs_cnt); if (msg_size > dev->buf_size - sizeof(*cmd)) { /* msg is too big to fit provided buffer */ trusty_error("%s: chan %d: msg is too long (%zu)\n", __func__, chan, msg_size); return TRUSTY_ERR_MSG_TOO_BIG; } /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_SEND; cmd->handle = chan; /* copy in message data */ cmd->payload_len = (uint32_t)msg_size; msg_size = iovec_to_buf(dev->buf_vaddr + sizeof(*cmd), dev->buf_size - sizeof(*cmd), iovs, iovs_cnt); trusty_assert(msg_size == (size_t)cmd->payload_len); /* call into secure os */ rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc < 0) { trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return TRUSTY_ERR_SECOS_ERR; } rc = check_response(dev, cmd, QL_TIPC_DEV_SEND); if (rc) { trusty_error("%s: send msg failed (%d)\n", __func__, rc); } return rc; } int trusty_ipc_dev_recv(struct trusty_ipc_dev* dev, handle_t chan, const struct trusty_ipc_iovec* iovs, size_t iovs_cnt) { int rc; size_t copied; volatile struct trusty_ipc_cmd_hdr* cmd; trusty_assert(dev); /* prepare command */ cmd = dev->buf_vaddr; trusty_memset((void*)cmd, 0, sizeof(*cmd)); cmd->opcode = QL_TIPC_DEV_RECV; cmd->handle = chan; /* no payload */ /* call into secure os */ rc = trusty_dev_exec_ipc(dev->tdev, dev->buf_id, sizeof(*cmd) + cmd->payload_len); if (rc < 0) { trusty_error("%s: secure OS returned (%d)\n", __func__, rc); return TRUSTY_ERR_SECOS_ERR; } rc = check_response(dev, cmd, QL_TIPC_DEV_RECV); if (rc) { trusty_error("%s: recv cmd failed (%d)\n", __func__, rc); return rc; } /* copy data out to proper destination */ copied = buf_to_iovec(iovs, iovs_cnt, (const void*)cmd->payload, cmd->payload_len); if (copied != (size_t)cmd->payload_len) { /* msg is too big to fit provided buffer */ trusty_error("%s: chan %d: buffer too small (%zu vs. %zu)\n", __func__, chan, copied, (size_t)cmd->payload_len); return TRUSTY_ERR_MSG_TOO_BIG; } return (int)copied; } void trusty_ipc_dev_idle(struct trusty_ipc_dev* dev, bool event_poll) { trusty_idle(dev->tdev, event_poll); }