459 lines
13 KiB
C
459 lines
13 KiB
C
/*
|
|
* 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 <trusty/trusty_dev.h>
|
|
#include <trusty/trusty_ipc.h>
|
|
#include <trusty/trusty_mem.h>
|
|
#include <trusty/util.h>
|
|
|
|
#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 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;
|
|
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_init_ipc(dev->tdev, &dev->buf_ns, 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:
|
|
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_ns, dev->buf_size);
|
|
trusty_assert(!rc);
|
|
if (rc != 0) {
|
|
trusty_error("%s: failed (%d) to shutdown Trusty IPC device\n",
|
|
__func__, rc);
|
|
}
|
|
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_ns, 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_ns, 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;
|
|
}
|
|
|
|
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_ns, 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_ns, 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_ns, 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)
|
|
{
|
|
trusty_idle(dev->tdev);
|
|
}
|
|
|