From a23c1813fce08599d36325e900fd654ebe82eb83 Mon Sep 17 00:00:00 2001 From: Jefferson Capovilla Date: Thu, 20 Feb 2020 16:06:07 -0300 Subject: [PATCH] Add Trustonic TEE driver module Cherry-picked from: (CR) Updated mcp.c, mcp.h and platform.h to make driver compatible to kernel 4.19. Add the Trustonic TEE driver module to the kernel. This is needed to run trusted applications in the Trustonic TEE. Updated Kbuild with current files. Kinibi version t-base-QC8996-Android-410a-V106. Change-Id: Id237e0acb2b0e6fe37045796106e3d1e5cb4b1b0 Reviewed-on: https://gerrit.mot.com/1685214 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Joel Voss Reviewed-by: Konstantin Makariev Reviewed-by: Igor Kovalenko Submit-Approved: Jira Key --- drivers/gud/MobiCoreDriver/Kbuild | 7 +- drivers/gud/MobiCoreDriver/admin.c | 305 +++-- drivers/gud/MobiCoreDriver/admin.h | 1 + drivers/gud/MobiCoreDriver/arm.h | 3 +- drivers/gud/MobiCoreDriver/build_tag.h | 5 +- drivers/gud/MobiCoreDriver/client.c | 547 ++++---- drivers/gud/MobiCoreDriver/client.h | 74 +- drivers/gud/MobiCoreDriver/clientlib.c | 66 +- drivers/gud/MobiCoreDriver/clock.c | 93 +- drivers/gud/MobiCoreDriver/clock.h | 3 +- drivers/gud/MobiCoreDriver/fastcall.c | 731 +++------- drivers/gud/MobiCoreDriver/fastcall.h | 28 +- drivers/gud/MobiCoreDriver/iwp.c | 738 ++++++++-- drivers/gud/MobiCoreDriver/iwp.h | 34 +- drivers/gud/MobiCoreDriver/logging.c | 91 +- drivers/gud/MobiCoreDriver/logging.h | 12 +- drivers/gud/MobiCoreDriver/main.c | 451 +++---- drivers/gud/MobiCoreDriver/main.h | 55 +- drivers/gud/MobiCoreDriver/mci/gptci.h | 3 +- drivers/gud/MobiCoreDriver/mci/mcifc.h | 19 +- drivers/gud/MobiCoreDriver/mci/mciiwp.h | 8 +- drivers/gud/MobiCoreDriver/mci/mcimcp.h | 44 +- drivers/gud/MobiCoreDriver/mci/mcinq.h | 3 +- drivers/gud/MobiCoreDriver/mci/mcitime.h | 3 +- drivers/gud/MobiCoreDriver/mci/mcloadformat.h | 11 +- drivers/gud/MobiCoreDriver/mcp.c | 614 +++++---- drivers/gud/MobiCoreDriver/mcp.h | 64 +- drivers/gud/MobiCoreDriver/mmu.c | 676 +++++----- drivers/gud/MobiCoreDriver/mmu.h | 46 +- drivers/gud/MobiCoreDriver/nq.c | 806 ++++++++--- drivers/gud/MobiCoreDriver/nq.h | 97 +- drivers/gud/MobiCoreDriver/platform.h | 96 +- drivers/gud/MobiCoreDriver/pm.c | 68 - .../MobiCoreDriver/public/GP/tee_client_api.h | 3 +- .../public/GP/tee_client_api_imp.h | 14 +- .../public/GP/tee_client_error.h | 3 +- .../public/GP/tee_client_types.h | 3 +- drivers/gud/MobiCoreDriver/public/mc_admin.h | 20 +- drivers/gud/MobiCoreDriver/public/mc_user.h | 23 +- .../public/mobicore_driver_api.h | 3 +- drivers/gud/MobiCoreDriver/scheduler.c | 244 ---- drivers/gud/MobiCoreDriver/scheduler.h | 25 - drivers/gud/MobiCoreDriver/session.c | 555 ++++---- drivers/gud/MobiCoreDriver/session.h | 60 +- drivers/gud/MobiCoreDriver/teeclientapi.c | 25 +- drivers/gud/MobiCoreDriver/user.c | 39 +- drivers/gud/MobiCoreDriver/user.h | 3 +- drivers/gud/MobiCoreDriver/xen_be.c | 1152 ++++++++++++++++ drivers/gud/MobiCoreDriver/{pm.h => xen_be.h} | 25 +- drivers/gud/MobiCoreDriver/xen_common.c | 62 + drivers/gud/MobiCoreDriver/xen_common.h | 179 +++ drivers/gud/MobiCoreDriver/xen_fe.c | 1194 +++++++++++++++++ drivers/gud/MobiCoreDriver/xen_fe.h | 67 + 53 files changed, 6393 insertions(+), 3108 deletions(-) delete mode 100644 drivers/gud/MobiCoreDriver/pm.c delete mode 100644 drivers/gud/MobiCoreDriver/scheduler.c delete mode 100644 drivers/gud/MobiCoreDriver/scheduler.h create mode 100644 drivers/gud/MobiCoreDriver/xen_be.c rename drivers/gud/MobiCoreDriver/{pm.h => xen_be.h} (58%) create mode 100644 drivers/gud/MobiCoreDriver/xen_common.c create mode 100644 drivers/gud/MobiCoreDriver/xen_common.h create mode 100644 drivers/gud/MobiCoreDriver/xen_fe.c create mode 100644 drivers/gud/MobiCoreDriver/xen_fe.h diff --git a/drivers/gud/MobiCoreDriver/Kbuild b/drivers/gud/MobiCoreDriver/Kbuild index 041f808cfeed..da7c9880456b 100644 --- a/drivers/gud/MobiCoreDriver/Kbuild +++ b/drivers/gud/MobiCoreDriver/Kbuild @@ -37,8 +37,9 @@ mcDrvModule-y := \ mcp.o \ mmu.o \ nq.o \ - pm.o \ - scheduler.o \ session.o \ teeclientapi.o \ - user.o + user.o \ + xen_be.o \ + xen_common.o \ + xen_fe.o diff --git a/drivers/gud/MobiCoreDriver/admin.c b/drivers/gud/MobiCoreDriver/admin.c index e2b211035db9..8c8232bd4dd2 100644 --- a/drivers/gud/MobiCoreDriver/admin.c +++ b/drivers/gud/MobiCoreDriver/admin.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -25,13 +26,15 @@ #include #include #include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* signal_pending */ +#endif #include #include "public/mc_user.h" #include "public/mc_admin.h" -#include "mci/mcloadformat.h" - #include "main.h" #include "mmu.h" /* For load_check and load_token */ #include "mcp.h" @@ -44,8 +47,9 @@ static struct { pid_t admin_tgid; int (*tee_start_cb)(void); void (*tee_stop_cb)(void); - int last_start_ret; -} admin_ctx; + int last_tee_ret; + struct notifier_block tee_stop_notifier; +} l_ctx; static struct mc_admin_driver_request { /* Global */ @@ -75,18 +79,6 @@ static struct mc_admin_driver_request { bool lock_channel_during_freeze;/* Is freezing ongoing ? */ } g_request; -#if KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE -static inline void reinit_completion_local(struct completion *x) -{ - reinit_completion(x); -} -#else -static inline void reinit_completion_local(struct completion *x) -{ - INIT_COMPLETION(*x); -} -#endif - /* The mutex around the channel communication has to be wrapped in order * to handle this use case : * client 1 calls request_send() @@ -146,6 +138,18 @@ static void channel_unlock(void) mutex_unlock(&g_request.mutex); } +#if KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE +static inline void reinit_completion_local(struct completion *x) +{ + reinit_completion(x); +} +#else +static inline void reinit_completion_local(struct completion *x) +{ + INIT_COMPLETION(*x); +} +#endif + static struct tee_object *tee_object_alloc(bool is_sp_trustlet, size_t length) { struct tee_object *obj; @@ -160,8 +164,9 @@ static struct tee_object *tee_object_alloc(bool is_sp_trustlet, size_t length) } /* Check size for overflow */ - if (size < length) { - mc_dev_err("cannot allocate object of size %zu", length); + if (size < length || size > OBJECT_LENGTH_MAX) { + mc_dev_err(-ENOMEM, "cannot allocate object of size %zu", + length); return NULL; } @@ -176,9 +181,9 @@ static struct tee_object *tee_object_alloc(bool is_sp_trustlet, size_t length) return obj; } -void tee_object_free(struct tee_object *robj) +void tee_object_free(struct tee_object *obj) { - vfree(robj); + vfree(obj); } static inline void client_state_change(enum client_state state) @@ -224,14 +229,25 @@ static void request_cancel(void); static int request_send(u32 command, const struct mc_uuid_t *uuid, bool is_gp, u32 spid) { - int counter = 10; + int counter = 0; + int wait_tens = 0; int ret = 0; /* Prepare request */ mutex_lock(&g_request.states_mutex); /* Wait a little for daemon to connect */ - while ((g_request.server_state == NOT_CONNECTED) && counter--) { + while (g_request.server_state == NOT_CONNECTED) { mutex_unlock(&g_request.states_mutex); + if (signal_pending(current)) + return -ERESTARTSYS; + + if (counter++ == 10) { + wait_tens++; + mc_dev_info("daemon not connected after %d0s, waiting", + wait_tens); + counter = 0; + } + ssleep(1); mutex_lock(&g_request.states_mutex); } @@ -240,13 +256,13 @@ static int request_send(u32 command, const struct mc_uuid_t *uuid, bool is_gp, if (g_request.server_state != READY) { mutex_unlock(&g_request.states_mutex); if (g_request.server_state != NOT_CONNECTED) { - mc_dev_err("invalid daemon state %d", - g_request.server_state); ret = -EPROTO; + mc_dev_err(ret, "invalid daemon state %d", + g_request.server_state); goto end; } else { - mc_dev_err("daemon not connected"); ret = -EHOSTUNREACH; + mc_dev_err(ret, "daemon not connected"); goto end; } } @@ -294,10 +310,12 @@ static int request_send(u32 command, const struct mc_uuid_t *uuid, bool is_gp, case NOT_CONNECTED: /* Daemon gone */ ret = -EPIPE; + mc_dev_devel("daemon disconnected"); break; case READY: /* No data to come, likely an error */ ret = -g_request.response.error_no; + mc_dev_devel("daemon ret=%d", ret); break; case RESPONSE_SENT: case DATA_SENT: @@ -306,9 +324,9 @@ static int request_send(u32 command, const struct mc_uuid_t *uuid, bool is_gp, break; case REQUEST_RECEIVED: /* Should not happen as complete means the state changed */ - mc_dev_err("daemon is in a bad state: %d", - g_request.server_state); ret = -EPIPE; + mc_dev_err(ret, "daemon is in a bad state: %d", + g_request.server_state); break; } @@ -318,7 +336,7 @@ end: if (ret) request_cancel(); - mc_dev_devel("%s ret=%d", __func__, ret); + mc_dev_devel("ret=%d", ret); return ret; } @@ -337,10 +355,12 @@ static int request_receive(void *address, u32 size) (g_request.server_state == DATA_SENT); mutex_unlock(&g_request.states_mutex); if (!server_ok) { - mc_dev_err("expected server state %d or %d, not %d", + int ret = -EPIPE; + + mc_dev_err(ret, "expected server state %d or %d, not %d", RESPONSE_SENT, DATA_SENT, g_request.server_state); request_cancel(); - return -EPIPE; + return ret; } /* Setup reception buffer */ @@ -404,8 +424,8 @@ static int admin_get_root_container(void *address) /* Check length against max */ if (g_request.response.length >= MAX_SO_CONT_SIZE) { request_cancel(); - mc_dev_err("response length exceeds maximum"); ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); goto end; } @@ -434,8 +454,8 @@ static int admin_get_sp_container(void *address, u32 spid) /* Check length against max */ if (g_request.response.length >= MAX_SO_CONT_SIZE) { request_cancel(); - mc_dev_err("response length exceeds maximum"); ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); goto end; } @@ -465,8 +485,8 @@ static int admin_get_trustlet_container(void *address, /* Check length against max */ if (g_request.response.length >= MAX_SO_CONT_SIZE) { request_cancel(); - mc_dev_err("response length exceeds maximum"); ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); goto end; } @@ -520,9 +540,6 @@ static void mc_admin_sendcrashdump(void) { int ret = 0; - /* Prevent daemon reconnection */ - admin_ctx.last_start_ret = -EHOSTUNREACH; - /* Lock communication channel */ channel_lock(); @@ -538,6 +555,14 @@ end: channel_unlock(); } +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mc_admin_sendcrashdump(); + l_ctx.last_tee_ret = -EHOSTUNREACH; + return 0; +} + static int tee_object_make(u32 spid, struct tee_object *obj) { struct mc_blob_len_info *l_info = (struct mc_blob_len_info *)obj->data; @@ -604,14 +629,24 @@ struct tee_object *tee_object_read(u32 spid, uintptr_t address, size_t length) /* Check length */ if (length < sizeof(thdr)) { - mc_dev_err("buffer shorter than header size"); - return ERR_PTR(-EFAULT); + ret = -EFAULT; + mc_dev_err(ret, "buffer shorter than header size"); + return ERR_PTR(ret); } /* Read header */ if (copy_from_user(&thdr, addr, sizeof(thdr))) { - mc_dev_err("header: copy_from_user failed"); - return ERR_PTR(-EFAULT); + ret = -EFAULT; + mc_dev_err(ret, "header: copy_from_user failed"); + return ERR_PTR(ret); + } + + /* Check header */ + if (thdr.intro.magic != MC_SERVICE_HEADER_MAGIC_BE && + thdr.intro.magic != MC_SERVICE_HEADER_MAGIC_LE) { + ret = -EINVAL; + mc_dev_err(ret, "header: invalid magic"); + return ERR_PTR(ret); } /* Allocate memory */ @@ -626,9 +661,10 @@ struct tee_object *tee_object_read(u32 spid, uintptr_t address, size_t length) /* Copy the rest of the data */ data += sizeof(thdr); if (copy_from_user(data, &addr[sizeof(thdr)], length - sizeof(thdr))) { - mc_dev_err("data: copy_from_user failed"); + ret = -EFAULT; + mc_dev_err(ret, "data: copy_from_user failed"); vfree(obj); - return ERR_PTR(-EFAULT); + return ERR_PTR(ret); } if (obj->header_length) { @@ -687,47 +723,41 @@ struct tee_object *tee_object_get(const struct mc_uuid_t *uuid, bool is_gp) } static inline int load_driver(struct tee_client *client, - struct mc_admin_load_info *info) + struct mc_admin_load_info *load_info) { - struct tee_object *obj; - struct mclf_header_v2 *thdr; - struct mc_identity identity = { - .login_type = LOGIN_PUBLIC, + struct mcp_open_info info = { + .spid = load_info->spid, + .va = load_info->address, + .len = load_info->length, + .uuid = &load_info->uuid, + .tci_len = PAGE_SIZE, + .user = true, }; - uintptr_t dci = 0; - u32 dci_len = 0; - u32 sid; + + u32 session_id = 0; int ret; - obj = tee_object_read(info->spid, info->address, info->length); - if (IS_ERR(obj)) - return PTR_ERR(obj); + if (info.va) + info.type = TEE_MC_DRIVER; + else + info.type = TEE_MC_DRIVER_UUID; - thdr = (struct mclf_header_v2 *)&obj->data[obj->header_length]; - if (!(thdr->flags & MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE)) { - /* - * The driver requires a DCI, although we won't be able to use - * it to communicate. - */ - dci_len = PAGE_SIZE; - ret = client_cbuf_create(client, dci_len, &dci, NULL); - if (ret) - goto end; - } + /* Create DCI in case it's needed */ + ret = client_cbuf_create(client, info.tci_len, &info.tci_va, NULL); + if (ret) + return ret; /* Open session */ - ret = client_add_session(client, obj, dci, dci_len, &sid, false, - &identity, -1); + ret = client_mc_open_common(client, &info, &session_id); if (!ret) - mc_dev_devel("driver loaded with sid %x", sid); + mc_dev_devel("driver loaded with session id %x", session_id); /* * Always 'free' the buffer (will remain as long as used), never freed * otherwise */ - client_cbuf_free(client, dci); -end: - vfree(obj); + client_cbuf_free(client, info.tci_va); + return ret; } @@ -747,7 +777,7 @@ static inline int load_token(struct mc_admin_load_info *token) tee_mmu_buffer(mmu, &map); ret = mcp_load_token(token->address, &map); - tee_mmu_delete(mmu); + tee_mmu_put(mmu); return ret; } @@ -772,7 +802,27 @@ static inline int load_check(struct mc_admin_load_info *info) tee_mmu_buffer(mmu, &map); ret = mcp_load_check(obj, &map); - tee_mmu_delete(mmu); + tee_mmu_put(mmu); + return ret; +} + +static inline int load_key_so(struct mc_admin_load_info *key_so) +{ + struct tee_mmu *mmu; + struct mcp_buffer_map map; + struct mc_ioctl_buffer buf; + int ret; + + buf.va = (uintptr_t)key_so->address; + buf.len = key_so->length; + buf.flags = MC_IO_MAP_INPUT; + mmu = tee_mmu_create(current->mm, &buf); + if (IS_ERR(mmu)) + return PTR_ERR(mmu); + + tee_mmu_buffer(mmu, &map); + ret = mcp_load_key_so(key_so->address, &map); + tee_mmu_put(mmu); return ret; } @@ -781,41 +831,41 @@ static ssize_t admin_write(struct file *file, const char __user *user, { int ret; - /* No offset allowed [yet] */ + /* No offset allowed */ if (*off) { - mc_dev_err("offset not supported"); - g_request.response.error_no = EPIPE; ret = -ECOMM; + mc_dev_err(ret, "offset not supported"); + g_request.response.error_no = EPIPE; goto err; } if (server_state_is(REQUEST_RECEIVED)) { /* Check client state */ if (!client_state_is(REQUEST_SENT)) { - mc_dev_err("expected client state %d, not %d", + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", REQUEST_SENT, g_request.client_state); g_request.response.error_no = EPIPE; - ret = -EPIPE; goto err; } /* Receive response header */ if (copy_from_user(&g_request.response, user, sizeof(g_request.response))) { - mc_dev_err("failed to get response from daemon"); - g_request.response.error_no = EPIPE; ret = -ECOMM; + mc_dev_err(ret, "failed to get response from daemon"); + g_request.response.error_no = EPIPE; goto err; } /* Check request ID */ if (g_request.request.request_id != g_request.response.request_id) { - mc_dev_err("expected id %d, not %d", + ret = -EBADE; + mc_dev_err(ret, "expected id %d, not %d", g_request.request.request_id, g_request.response.request_id); g_request.response.error_no = EPIPE; - ret = -EBADE; goto err; } @@ -843,10 +893,10 @@ static ssize_t admin_write(struct file *file, const char __user *user, /* Check client state */ if (!client_state_is(BUFFERS_READY)) { - mc_dev_err("expected client state %d, not %d", + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", BUFFERS_READY, g_request.client_state); g_request.response.error_no = EPIPE; - ret = -EPIPE; goto err; } @@ -856,9 +906,9 @@ static ssize_t admin_write(struct file *file, const char __user *user, ret = copy_from_user(g_request.buffer, user, len); if (ret) { - mc_dev_err("failed to get data from daemon"); - g_request.response.error_no = EPIPE; ret = -ECOMM; + mc_dev_err(ret, "failed to get data from daemon"); + g_request.response.error_no = EPIPE; goto err; } @@ -877,6 +927,20 @@ end: return ret; } +static ssize_t admin_read(struct file *file, char __user *user, size_t len, + loff_t *off) +{ + /* No offset allowed */ + if (*off) { + int ret = -ECOMM; + + mc_dev_err(ret, "offset not supported"); + return ret; + } + + return nq_get_stop_message(user, len); +} + static long admin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -888,10 +952,10 @@ static long admin_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case MC_ADMIN_IO_GET_DRIVER_REQUEST: { /* Update TGID as it may change (when becoming a daemon) */ - if (admin_ctx.admin_tgid != current->tgid) { - admin_ctx.admin_tgid = current->tgid; + if (l_ctx.admin_tgid != current->tgid) { + l_ctx.admin_tgid = current->tgid; mc_dev_info("daemon PID changed to %d", - admin_ctx.admin_tgid); + l_ctx.admin_tgid); } /* Block until a request is available */ @@ -904,11 +968,11 @@ static long admin_ioctl(struct file *file, unsigned int cmd, /* Check client state */ if (!client_state_is(REQUEST_SENT)) { - mc_dev_err("expected client state %d, not %d", + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", REQUEST_SENT, g_request.client_state); g_request.response.error_no = EPIPE; complete(&g_request.server_complete); - ret = -EPIPE; break; } @@ -983,6 +1047,17 @@ static long admin_ioctl(struct file *file, unsigned int cmd, ret = load_check(&info); break; } + case MC_ADMIN_IO_LOAD_KEY_SO: { + struct mc_admin_load_info info; + + if (copy_from_user(&info, uarg, sizeof(info))) { + ret = -EFAULT; + break; + } + + ret = load_key_so(&info); + break; + } default: ret = -ENOIOCTLCMD; } @@ -1016,9 +1091,8 @@ static int admin_release(struct inode *inode, struct file *file) complete(&g_request.server_complete); } mutex_unlock(&g_request.states_mutex); - mc_dev_info("daemon connection closed, TGID %d", - admin_ctx.admin_tgid); - admin_ctx.admin_tgid = 0; + mc_dev_info("daemon connection closed, TGID %d", l_ctx.admin_tgid); + l_ctx.admin_tgid = 0; /* * ret is quite irrelevant here as most apps don't care about the @@ -1032,15 +1106,15 @@ static int admin_open(struct inode *inode, struct file *file) int ret = 0; /* Only one connection allowed to admin interface */ - mutex_lock(&admin_ctx.admin_tgid_mutex); - if (admin_ctx.admin_tgid) { - mc_dev_err("daemon connection already open, PID %d", - admin_ctx.admin_tgid); + mutex_lock(&l_ctx.admin_tgid_mutex); + if (l_ctx.admin_tgid) { ret = -EBUSY; + mc_dev_err(ret, "daemon connection already open, PID %d", + l_ctx.admin_tgid); } else { - admin_ctx.admin_tgid = current->tgid; + l_ctx.admin_tgid = current->tgid; } - mutex_unlock(&admin_ctx.admin_tgid_mutex); + mutex_unlock(&l_ctx.admin_tgid_mutex); if (ret) return ret; @@ -1051,21 +1125,21 @@ static int admin_open(struct inode *inode, struct file *file) * daemon is connected so now we can safely suppose * the secure world is loaded too */ - if (admin_ctx.last_start_ret > 0) - admin_ctx.last_start_ret = admin_ctx.tee_start_cb(); + if (l_ctx.last_tee_ret == TEE_START_NOT_TRIGGERED) + l_ctx.last_tee_ret = l_ctx.tee_start_cb(); /* Failed to start the TEE, either now or before */ - if (admin_ctx.last_start_ret) { - mutex_lock(&admin_ctx.admin_tgid_mutex); - admin_ctx.admin_tgid = 0; - mutex_unlock(&admin_ctx.admin_tgid_mutex); - return admin_ctx.last_start_ret; + if (l_ctx.last_tee_ret) { + mutex_lock(&l_ctx.admin_tgid_mutex); + l_ctx.admin_tgid = 0; + mutex_unlock(&l_ctx.admin_tgid_mutex); + return l_ctx.last_tee_ret; } reinit_completion_local(&g_request.client_complete); reinit_completion_local(&g_request.server_complete); /* Requests from driver to daemon */ - mc_dev_info("daemon connection open, TGID %d", admin_ctx.admin_tgid); + mc_dev_info("daemon connection open, TGID %d", l_ctx.admin_tgid); return 0; } @@ -1079,30 +1153,33 @@ static const struct file_operations mc_admin_fops = { .compat_ioctl = admin_ioctl, #endif .write = admin_write, + .read = admin_read, }; int mc_admin_init(struct cdev *cdev, int (*tee_start_cb)(void), void (*tee_stop_cb)(void)) { - mutex_init(&admin_ctx.admin_tgid_mutex); + mutex_init(&l_ctx.admin_tgid_mutex); /* Requests from driver to daemon */ mutex_init(&g_request.mutex); mutex_init(&g_request.states_mutex); g_request.request_id = 42; init_completion(&g_request.client_complete); init_completion(&g_request.server_complete); - nq_register_crash_handler(mc_admin_sendcrashdump); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); /* Create char device */ cdev_init(cdev, &mc_admin_fops); /* Register the call back for starting the secure world */ - admin_ctx.tee_start_cb = tee_start_cb; - admin_ctx.tee_stop_cb = tee_stop_cb; - admin_ctx.last_start_ret = TEE_START_NOT_TRIGGERED; + l_ctx.tee_start_cb = tee_start_cb; + l_ctx.tee_stop_cb = tee_stop_cb; + l_ctx.last_tee_ret = TEE_START_NOT_TRIGGERED; return 0; } void mc_admin_exit(void) { - if (!admin_ctx.last_start_ret) - admin_ctx.tee_stop_cb(); + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); + if (!l_ctx.last_tee_ret) + l_ctx.tee_stop_cb(); } diff --git a/drivers/gud/MobiCoreDriver/admin.h b/drivers/gud/MobiCoreDriver/admin.h index 8f8667ecb145..f08d59e9c3ea 100644 --- a/drivers/gud/MobiCoreDriver/admin.h +++ b/drivers/gud/MobiCoreDriver/admin.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. diff --git a/drivers/gud/MobiCoreDriver/arm.h b/drivers/gud/MobiCoreDriver/arm.h index 85ff70125e12..9e2f541c1934 100644 --- a/drivers/gud/MobiCoreDriver/arm.h +++ b/drivers/gud/MobiCoreDriver/arm.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/build_tag.h b/drivers/gud/MobiCoreDriver/build_tag.h index ef5ccd80cf69..e18e9b1594c4 100644 --- a/drivers/gud/MobiCoreDriver/build_tag.h +++ b/drivers/gud/MobiCoreDriver/build_tag.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -13,5 +14,5 @@ */ #ifndef MOBICORE_COMPONENT_BUILD_TAG #define MOBICORE_COMPONENT_BUILD_TAG \ - "t-base-QC-MSM8996-Android-400C-V002-20180809_045827_53704_83249" + "t-base-QC8996-Android-410a-V106-20191127_223906_75100_101470" #endif diff --git a/drivers/gud/MobiCoreDriver/client.c b/drivers/gud/MobiCoreDriver/client.c index 837d76f9138c..cfdf577adc95 100644 --- a/drivers/gud/MobiCoreDriver/client.c +++ b/drivers/gud/MobiCoreDriver/client.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -29,7 +30,6 @@ #include "public/mc_admin.h" #include "main.h" -#include "admin.h" /* tee_object* */ #include "mmu.h" #include "session.h" #include "client.h" @@ -139,7 +139,7 @@ static void cbuf_release(struct kref *kref) atomic_dec(&g_ctx.c_cbufs); } -static inline void cbuf_put(struct cbuf *cbuf) +void tee_cbuf_put(struct cbuf *cbuf) { struct tee_client *client = cbuf->client; @@ -166,8 +166,9 @@ static int cbuf_map(struct vm_area_struct *vmarea, uintptr_t addr, u32 len, return -EINVAL; if (len != (u32)(vmarea->vm_end - vmarea->vm_start)) { - mc_dev_err("cbuf incompatible with vma"); - return -EINVAL; + ret = -EINVAL; + mc_dev_err(ret, "cbuf incompatible with vma"); + return ret; } vmarea->vm_flags |= VM_IO; @@ -177,7 +178,7 @@ static int cbuf_map(struct vm_area_struct *vmarea, uintptr_t addr, u32 len, vmarea->vm_page_prot); if (ret) { *uaddr = 0; - mc_dev_err("User mapping failed"); + mc_dev_err(ret, "User mapping failed"); return ret; } @@ -193,102 +194,73 @@ static inline bool client_is_kernel(struct tee_client *client) return !client->pid; } -/* - * The proxy gives us the fd of its server-side socket, so we can find out the - * task then the mm of its client. mmput() must be called to free the resource. - */ -static struct mm_struct *get_mm_from_client_fd(int client_fd) -{ - struct mm_struct *mm = NULL; - struct socket *sock; - int err; - - if (client_fd < 0) - return get_task_mm(current); - - sock = sockfd_lookup(client_fd, &err); - if (!sock) - return NULL; - - if (sock->sk && sock->sk->sk_peer_pid) { - struct task_struct *task; - - rcu_read_lock(); - task = pid_task(sock->sk->sk_peer_pid, PIDTYPE_PID); - if (task) - mm = get_task_mm(task); - - rcu_read_unlock(); - } - - sockfd_put(sock); - return mm; -} - static struct cwsm *cwsm_create(struct tee_client *client, + struct tee_mmu *mmu, const struct gp_shared_memory *memref, - struct gp_return *gp_ret, int client_fd) + struct gp_return *gp_ret) { struct cwsm *cwsm; - struct mcp_buffer_map map; - struct mc_ioctl_buffer buf; + u32 sva; int ret; cwsm = kzalloc(sizeof(*cwsm), GFP_KERNEL); if (!cwsm) return ERR_PTR(iwp_set_ret(-ENOMEM, gp_ret)); - buf.va = (uintptr_t)memref->buffer; - buf.len = memref->size; - buf.flags = memref->flags; - if (client_is_kernel(client)) { - cwsm->mmu = tee_mmu_create(NULL, &buf); + if (mmu) { + cwsm->mmu = mmu; + tee_mmu_get(cwsm->mmu); } else { - struct mm_struct *mm = get_mm_from_client_fd(client_fd); + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)memref->buffer, + .len = memref->size, + .flags = memref->flags, + }; - if (!mm) { - mc_dev_err("can't get mm from client fd %d", client_fd); - ret = -EPERM; - goto err_cwsm; + if (client_is_kernel(client)) { + cwsm->mmu = tee_mmu_create(NULL, &buf); + } else { + struct mm_struct *mm = get_task_mm(current); + + if (!mm) { + ret = -EPERM; + mc_dev_err(ret, "can't get mm"); + goto err_cwsm; + } + + /* Build MMU table for buffer */ + cwsm->mmu = tee_mmu_create(mm, &buf); + mmput(mm); } - /* Build MMU table for buffer */ - cwsm->mmu = tee_mmu_create(mm, &buf); - mmput(mm); + if (IS_ERR(cwsm->mmu)) { + ret = iwp_set_ret(PTR_ERR(cwsm->mmu), gp_ret); + goto err_cwsm; + } } - if (IS_ERR(cwsm->mmu)) { - ret = iwp_set_ret(PTR_ERR(cwsm->mmu), gp_ret); - goto err_cwsm; - } - - /* Initialise maps */ - memset(&map, 0, sizeof(map)); - tee_mmu_buffer(cwsm->mmu, &map); - /* FIXME Flags must be stored in MMU (needs recent trunk change) */ - map.flags = memref->flags; - ret = iwp_register_shared_mem(&map, gp_ret); + ret = iwp_register_shared_mem(cwsm->mmu, &sva, gp_ret); if (ret) goto err_mmu; - cwsm->client = client; - memcpy(&cwsm->memref, memref, sizeof(cwsm->memref)); - cwsm->sva = map.secure_va; - kref_init(&cwsm->kref); - INIT_LIST_HEAD(&cwsm->list); /* Get a token on the client */ client_get(client); + cwsm->client = client; + memcpy(&cwsm->memref, memref, sizeof(cwsm->memref)); + cwsm->sva = sva; + kref_init(&cwsm->kref); + INIT_LIST_HEAD(&cwsm->list); /* Add buffer to list */ mutex_lock(&client->quick_lock); list_add_tail(&cwsm->list, &client->cwsms); mutex_unlock(&client->quick_lock); - mc_dev_devel("created cwsm %p: client %p", cwsm, client); + mc_dev_devel("created cwsm %p: client %p sva %x", cwsm, client, sva); /* Increment debug counter */ atomic_inc(&g_ctx.c_cwsms); return cwsm; err_mmu: - tee_mmu_delete(cwsm->mmu); + tee_mmu_put(cwsm->mmu); err_cwsm: kfree(cwsm); return ERR_PTR(ret); @@ -313,7 +285,7 @@ static void cwsm_release(struct kref *kref) map.secure_va = cwsm->sva; iwp_release_shared_mem(&map); /* Release MMU */ - tee_mmu_delete(cwsm->mmu); + tee_mmu_put(cwsm->mmu); /* Release client token */ client_put(client); /* Free */ @@ -383,6 +355,7 @@ u32 client_get_cwsm_sva(struct tee_client *client, if (!cwsm) return 0; + mc_dev_devel("found sva %x", cwsm->sva); return cwsm->sva; } @@ -550,7 +523,7 @@ static void client_close_kernel_cbufs(struct tee_client *client) if (!cbuf) break; - cbuf_put(cbuf); + tee_cbuf_put(cbuf); } } @@ -574,7 +547,7 @@ static void client_release_gp_operations(struct tee_client *client) mutex_lock(&client->quick_lock); list_for_each_entry_safe(op, nop, &client->operations, list) { /* Only cancelled operations are kzalloc'd */ - mc_dev_devel("flush cancelled operation %p for started %u", + mc_dev_devel("flush cancelled operation %p for started %llu", op, op->started); if (op->cancelled) kfree(op); @@ -606,18 +579,14 @@ void client_close(struct tee_client *client) } /* - * The TEE is going to die, so get rid of whatever is shared with it + * Clean all structures shared with the SWd (note: incomplete but unused) */ -void clients_kill_sessions(void) +void client_cleanup(void) { struct tee_client *client; mutex_lock(&client_ctx.clients_lock); list_for_each_entry(client, &client_ctx.clients, list) { - /* - * session_kill() will put the session which should get freed - * and free its wsms/mmus and put any cbuf concerned - */ mutex_lock(&client->sessions_lock); while (!list_empty(&client->sessions)) { struct tee_session *session; @@ -625,7 +594,7 @@ void clients_kill_sessions(void) session = list_first_entry(&client->sessions, struct tee_session, list); list_del(&session->list); - session_kill(session); + session_mc_cleanup_session(session); } mutex_unlock(&client->sessions_lock); } @@ -637,42 +606,22 @@ void clients_kill_sessions(void) * @param * @return driver error code */ -int client_open_session(struct tee_client *client, u32 *session_id, - const struct mc_uuid_t *uuid, uintptr_t tci, - size_t tci_len, bool is_gp_uuid, - struct mc_identity *identity, int client_fd) +int client_mc_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + uintptr_t tci_va, size_t tci_len, u32 *session_id) { - int err = 0; - u32 sid = 0; - struct tee_object *obj; + struct mcp_open_info info = { + .type = TEE_MC_UUID, + .uuid = uuid, + .tci_va = tci_va, + .tci_len = tci_len, + .user = !client_is_kernel(client), + }; + int ret; - /* Get secure object */ - obj = tee_object_get(uuid, is_gp_uuid); - if (IS_ERR(obj)) { - /* Try to select secure object inside the SWd if not found */ - if ((PTR_ERR(obj) == -ENOENT) && g_ctx.f_ta_auth) - obj = tee_object_select(uuid); - - if (IS_ERR(obj)) { - err = PTR_ERR(obj); - goto end; - } - } - - /* Open session */ - err = client_add_session(client, obj, tci, tci_len, &sid, is_gp_uuid, - identity, client_fd); - /* Fill in return parameter */ - if (!err) - *session_id = sid; - - /* Delete secure object */ - tee_object_free(obj); - -end: - - mc_dev_devel("session %x, exit with %d", sid, err); - return err; + ret = client_mc_open_common(client, &info, session_id); + mc_dev_devel("session %x, exit with %d", *session_id, ret); + return ret; } /* @@ -680,54 +629,34 @@ end: * @param * @return driver error code */ -int client_open_trustlet(struct tee_client *client, u32 *session_id, u32 spid, - uintptr_t trustlet, size_t trustlet_len, - uintptr_t tci, size_t tci_len, int client_fd) +int client_mc_open_trustlet(struct tee_client *client, + u32 spid, uintptr_t ta_va, size_t ta_len, + uintptr_t tci_va, size_t tci_len, u32 *session_id) { - struct tee_object *obj; - struct mc_identity identity = { - .login_type = LOGIN_PUBLIC, + struct mcp_open_info info = { + .type = TEE_MC_TA, + .spid = spid, + .va = ta_va, + .len = ta_len, + .tci_va = tci_va, + .tci_len = tci_len, + .user = !client_is_kernel(client), }; - u32 sid = 0; - int err = 0; + int ret; - if (client_is_kernel(client)) - /* Create secure object from kernel-space trustlet binary */ - obj = tee_object_copy(trustlet, trustlet_len); - else - /* Create secure object from user-space trustlet binary */ - obj = tee_object_read(spid, trustlet, trustlet_len); - if (IS_ERR(obj)) { - err = PTR_ERR(obj); - goto end; - } - - /* Open session */ - err = client_add_session(client, obj, tci, tci_len, &sid, false, - &identity, client_fd); - /* Fill in return parameter */ - if (!err) - *session_id = sid; - - /* Delete secure object */ - tee_object_free(obj); - -end: - mc_dev_devel("session %x, exit with %d", sid, err); - return err; + ret = client_mc_open_common(client, &info, session_id); + mc_dev_devel("session %x, exit with %d", *session_id, ret); + return ret; } /* * Opens a TA and add corresponding session object to given client * return: driver error code */ -int client_add_session(struct tee_client *client, const struct tee_object *obj, - uintptr_t tci, size_t len, u32 *session_id, bool is_gp, - struct mc_identity *identity, int client_fd) +int client_mc_open_common(struct tee_client *client, struct mcp_open_info *info, + u32 *session_id) { struct tee_session *session = NULL; - struct tee_mmu *obj_mmu = NULL; - struct mc_ioctl_buffer buf; int ret = 0; /* @@ -737,31 +666,18 @@ int client_add_session(struct tee_client *client, const struct tee_object *obj, * Adding session to list must be done AFTER it is started (once we have * sid), therefore it cannot be done within session_create(). */ - session = session_create(client, is_gp, identity, client_fd); + session = session_create(client, NULL); if (IS_ERR(session)) return PTR_ERR(session); - /* Create blob L2 table (blob is allocated by driver, so task=NULL) */ - buf.va = (uintptr_t)obj->data; - buf.len = obj->length; - buf.flags = MC_IO_MAP_INPUT; - obj_mmu = tee_mmu_create(NULL, &buf); - if (IS_ERR(obj_mmu)) { - ret = PTR_ERR(obj_mmu); - goto err; - } - - /* Open session */ - ret = session_open(session, obj, obj_mmu, tci, len, client_fd); - /* Blob table no more needed in any case */ - tee_mmu_delete(obj_mmu); + ret = session_mc_open_session(session, info); if (ret) goto err; mutex_lock(&client->sessions_lock); /* Add session to client */ list_add_tail(&session->list, &client->sessions); - /* Set sid returned by SWd */ + /* Set session ID returned by SWd */ *session_id = session->mcp_session.sid; mutex_unlock(&client->sessions_lock); @@ -779,65 +695,6 @@ err: return ret; } -/* - * Opens a TA and add corresponding session object to given client - * return: driver error code - */ -static int client_add_gp_session(struct tee_client *client, - const struct tee_object *obj, - u32 *session_id, - struct gp_operation *operation, - struct mc_identity *identity, - int client_fd, struct gp_return *gp_ret) -{ - struct tee_session *session = NULL; - struct tee_mmu *obj_mmu = NULL; - struct mc_ioctl_buffer buf; - int ret = 0; - - /* - * Create session object with temp sid=0 BEFORE session is started, - * otherwise if a GP TA is started and NWd session object allocation - * fails, we cannot handle the potentially delayed GP closing. - * Adding session to list must be done AFTER it is started (once we have - * sid), therefore it cannot be done within session_create(). - */ - session = session_create(client, true, identity, client_fd); - if (IS_ERR(session)) - return iwp_set_ret(PTR_ERR(session), gp_ret); - - /* Create blob L2 table (blob is allocated by driver, so task=NULL) */ - buf.va = (uintptr_t)obj->data; - buf.len = obj->length; - buf.flags = MC_IO_MAP_INPUT; - obj_mmu = tee_mmu_create(NULL, &buf); - if (IS_ERR(obj_mmu)) { - ret = PTR_ERR(obj_mmu); - goto end; - } - - /* Open session */ - ret = session_gp_open_session(session, obj, obj_mmu, operation, gp_ret, - client_fd); - /* Blob table no more needed in any case */ - tee_mmu_delete(obj_mmu); - if (ret) - goto end; - - mutex_lock(&client->sessions_lock); - /* Add session to client */ - list_add_tail(&session->list, &client->sessions); - mutex_unlock(&client->sessions_lock); - /* Set sid returned by SWd */ - *session_id = session->iwp_session.sid; - -end: - if (ret) - session_put(session); - - return ret; -} - /* * Remove a session object from client and close corresponding TA * Return: true if session was found and closed @@ -890,7 +747,7 @@ static struct tee_session *client_get_session(struct tee_client *client, mutex_unlock(&client->sessions_lock); if (!session) - mc_dev_err("session %x not found", session_id); + mc_dev_err(-ENXIO, "session %x not found", session_id); return session; } @@ -910,7 +767,7 @@ int client_notify_session(struct tee_client *client, u32 session_id) return -ENXIO; /* Send command to SWd */ - ret = session_notify_swd(session); + ret = session_mc_notify(session); /* Put session */ session_put(session); mc_dev_devel("session %x, exit with %d", session_id, ret); @@ -932,7 +789,7 @@ int client_waitnotif_session(struct tee_client *client, u32 session_id, if (!session) return -ENXIO; - ret = session_waitnotif(session, timeout, silent_expiry); + ret = session_mc_wait(session, timeout, silent_expiry); /* Put session */ session_put(session); mc_dev_devel("session %x, exit with %d", session_id, ret); @@ -943,9 +800,10 @@ int client_waitnotif_session(struct tee_client *client, u32 session_id, * Read session exit/termination code */ int client_get_session_exitcode(struct tee_client *client, u32 session_id, - s32 *exit_code) + s32 *err) { struct tee_session *session; + int ret; /* Find/get session */ session = client_get_session(client, session_id); @@ -953,16 +811,16 @@ int client_get_session_exitcode(struct tee_client *client, u32 session_id, return -ENXIO; /* Retrieve error */ - *exit_code = session_exitcode(session); + ret = session_mc_get_err(session, err); /* Put session */ session_put(session); - mc_dev_devel("session %x, exit code %d", session_id, *exit_code); - return 0; + mc_dev_devel("session %x, exit code %d", session_id, *err); + return ret; } /* Share a buffer with given TA in SWd */ -int client_map_session_wsms(struct tee_client *client, u32 session_id, - struct mc_ioctl_buffer *bufs, int client_fd) +int client_mc_map(struct tee_client *client, u32 session_id, + struct tee_mmu *mmu, struct mc_ioctl_buffer *buf) { struct tee_session *session; int ret; @@ -973,7 +831,7 @@ int client_map_session_wsms(struct tee_client *client, u32 session_id, return -ENXIO; /* Add buffer to the session */ - ret = session_map(session, bufs, client_fd); + ret = session_mc_map(session, mmu, buf); /* Put session */ session_put(session); mc_dev_devel("session %x, exit with %d", session_id, ret); @@ -981,8 +839,8 @@ int client_map_session_wsms(struct tee_client *client, u32 session_id, } /* Stop sharing a buffer with SWd */ -int client_unmap_session_wsms(struct tee_client *client, u32 session_id, - const struct mc_ioctl_buffer *bufs) +int client_mc_unmap(struct tee_client *client, u32 session_id, + const struct mc_ioctl_buffer *buf) { struct tee_session *session; int ret; @@ -993,7 +851,7 @@ int client_unmap_session_wsms(struct tee_client *client, u32 session_id, return -ENXIO; /* Remove buffer from session */ - ret = session_unmap(session, bufs); + ret = session_mc_unmap(session, buf); /* Put session */ session_put(session); mc_dev_devel("session %x, exit with %d", session_id, ret); @@ -1007,19 +865,31 @@ int client_gp_initialize_context(struct tee_client *client, } int client_gp_register_shared_mem(struct tee_client *client, + struct tee_mmu *mmu, u32 *sva, const struct gp_shared_memory *memref, - struct gp_return *gp_ret, int client_fd) + struct gp_return *gp_ret) { - struct cwsm *cwsm; + struct cwsm *cwsm = NULL; - /* cwsm_find automatically takes a reference */ - cwsm = cwsm_find(client, memref); - if (!cwsm) { - cwsm = cwsm_create(client, memref, gp_ret, client_fd); - if (IS_ERR(cwsm)) - return iwp_set_ret(PTR_ERR(cwsm), gp_ret); + if (memref->size > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %llu too big", memref->size); + return -EINVAL; } + if (!mmu) + /* cwsm_find automatically takes a reference */ + cwsm = cwsm_find(client, memref); + + if (!cwsm) + cwsm = cwsm_create(client, mmu, memref, gp_ret); + + /* gp_ret set by callee */ + if (IS_ERR(cwsm)) + return PTR_ERR(cwsm); + + if (sva) + *sva = cwsm->sva; + return iwp_set_ret(0, gp_ret); } @@ -1045,44 +915,84 @@ end: } /* - * Open TA for given client. TA binary is provided by the daemon. - * @param - * @return driver error code + * Opens a TA and add corresponding session object to given client + * return: driver error code */ -int client_gp_open_session(struct tee_client *client, u32 *session_id, +int client_gp_open_session(struct tee_client *client, const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct mc_identity *identity, - struct gp_return *gp_ret, int client_fd) + const struct mc_identity *identity, + struct gp_return *gp_ret, + u32 *session_id) { - struct tee_object *obj; + struct tee_session *session = NULL; int ret = 0; - /* Get secure object */ - obj = tee_object_get(uuid, true); - if (IS_ERR(obj)) { - /* Try to select secure object inside the SWd if not found */ - if ((PTR_ERR(obj) == -ENOENT) && g_ctx.f_ta_auth) - obj = tee_object_select(uuid); - - if (IS_ERR(obj)) { - ret = PTR_ERR(obj); - goto end; - } - } + /* + * Create session object with temp sid=0 BEFORE session is started, + * otherwise if a GP TA is started and NWd session object allocation + * fails, we cannot handle the potentially delayed GP closing. + * Adding session to list must be done AFTER it is started (once we have + * sid), therefore it cannot be done within session_create(). + */ + session = session_create(client, identity); + if (IS_ERR(session)) + return iwp_set_ret(PTR_ERR(session), gp_ret); /* Open session */ - ret = client_add_gp_session(client, obj, session_id, operation, - identity, client_fd, gp_ret); + ret = session_gp_open_session(session, uuid, operation, gp_ret); + if (ret) + goto end; - /* Delete secure object */ - tee_object_free(obj); + mutex_lock(&client->sessions_lock); + /* Add session to client */ + list_add_tail(&session->list, &client->sessions); + mutex_unlock(&client->sessions_lock); + /* Set sid returned by SWd */ + *session_id = session->iwp_session.sid; end: + if (ret) + session_put(session); + mc_dev_devel("gp session %x, exit with %d", *session_id, ret); return ret; } +int client_gp_open_session_domu(struct tee_client *client, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct tee_session *session = NULL; + int ret = 0; + + /* Don't pass NULL for identity as it would make a MC session */ + session = session_create(client, ERR_PTR(-ENOENT)); + if (IS_ERR(session)) + return iwp_set_ret(PTR_ERR(session), gp_ret); + + /* Open session */ + ret = session_gp_open_session_domu(session, uuid, started, iws, + mmus, gp_ret); + if (ret) + goto end; + + mutex_lock(&client->sessions_lock); + /* Add session to client */ + list_add_tail(&session->list, &client->sessions); + mutex_unlock(&client->sessions_lock); + +end: + if (ret) + session_put(session); + + mc_dev_devel("gp session %x, exit with %d", + session->iwp_session.sid, ret); + return ret; +} + int client_gp_close_session(struct tee_client *client, u32 session_id) { struct tee_session *session = NULL, *candidate; @@ -1117,7 +1027,7 @@ int client_gp_close_session(struct tee_client *client, u32 session_id) int client_gp_invoke_command(struct tee_client *client, u32 session_id, u32 command_id, struct gp_operation *operation, - struct gp_return *gp_ret, int client_fd) + struct gp_return *gp_ret) { struct tee_session *session; int ret = 0; @@ -1126,18 +1036,37 @@ int client_gp_invoke_command(struct tee_client *client, u32 session_id, if (!session) return iwp_set_ret(-ENXIO, gp_ret); - ret = session_gp_invoke_command(session, command_id, operation, gp_ret, - client_fd); + ret = session_gp_invoke_command(session, command_id, operation, gp_ret); /* Put session */ session_put(session); return ret; } -void client_gp_request_cancellation(struct tee_client *client, u32 started) +int client_gp_invoke_command_domu(struct tee_client *client, u32 session_id, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct tee_session *session; + int ret = 0; + + session = client_get_session(client, session_id); + if (!session) + return iwp_set_ret(-ENXIO, gp_ret); + + ret = session_gp_invoke_command_domu(session, started, iws, mmus, + gp_ret); + + /* Put session */ + session_put(session); + return ret; +} + +void client_gp_request_cancellation(struct tee_client *client, u64 started) { struct client_gp_operation *op; - u32 slot; + u64 slot; bool found = false; /* Look for operation */ @@ -1146,8 +1075,9 @@ void client_gp_request_cancellation(struct tee_client *client, u32 started) if (op->started == started) { slot = op->slot; found = true; - mc_dev_devel("found to operation cancel for started %u", - started); + mc_dev_devel( + "found no operation cancel for started %llu", + started); break; } @@ -1158,8 +1088,9 @@ void client_gp_request_cancellation(struct tee_client *client, u32 started) op->started = started; op->cancelled = true; list_add_tail(&op->list, &client->operations); - mc_dev_devel("add cancelled operation %p for started %u" - , op, op->started); + mc_dev_devel( + "add cancelled operation %p for started %llu", + op, op->started); } } mutex_unlock(&client->quick_lock); @@ -1185,7 +1116,7 @@ static void cbuf_vm_close(struct vm_area_struct *vmarea) { struct cbuf *cbuf = vmarea->vm_private_data; - cbuf_put(cbuf); + tee_cbuf_put(cbuf); } static const struct vm_operations_struct cbuf_vm_ops = { @@ -1199,20 +1130,28 @@ static const struct vm_operations_struct cbuf_vm_ops = { int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, struct vm_area_struct *vmarea) { - int err = 0; struct cbuf *cbuf = NULL; unsigned int order; + int ret = 0; if (!client) return -EINVAL; - if (!len || len > BUFFER_LENGTH_MAX) + if (!len) { + mc_dev_err(-EINVAL, "buffer size 0 not supported"); return -EINVAL; + } + + if (len > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %u too big", len); + return -EINVAL; + } order = get_order(len); if (order > MAX_ORDER) { - mc_dev_err("Buffer size too large"); - return -ENOMEM; + ret = -ENOMEM; + mc_dev_err(ret, "Buffer size too large"); + return ret; } /* Allocate buffer descriptor structure */ @@ -1233,13 +1172,13 @@ int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, /* Map to user space if applicable */ if (!client_is_kernel(client)) { - err = cbuf_map(vmarea, cbuf->addr, len, &cbuf->uaddr); - if (err) { + ret = cbuf_map(vmarea, cbuf->addr, len, &cbuf->uaddr); + if (ret) { free_pages(cbuf->addr, order); kfree(cbuf); /* Decrement debug counter */ atomic_dec(&g_ctx.c_cbufs); - return err; + return ret; } } @@ -1270,7 +1209,7 @@ int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, mutex_unlock(&client->cbufs_lock); mc_dev_devel("created cbuf %p: client %p addr %lx uaddr %lx len %u", cbuf, client, cbuf->addr, cbuf->uaddr, cbuf->len); - return err; + return ret; } /* @@ -1321,16 +1260,16 @@ int client_cbuf_free(struct tee_client *client, uintptr_t addr) struct cbuf *cbuf = cbuf_get_by_addr(client, addr); if (!cbuf) { - mc_dev_err("cbuf %lu not found", addr); + mc_dev_err(-EINVAL, "cbuf %lu not found", addr); return -EINVAL; } /* Release reference taken by cbuf_get_by_addr */ - cbuf_put(cbuf); + tee_cbuf_put(cbuf); mutex_lock(&client->cbufs_lock); cbuf->api_freed = true; mutex_unlock(&client->cbufs_lock); - cbuf_put(cbuf); + tee_cbuf_put(cbuf); return 0; } @@ -1349,12 +1288,12 @@ bool client_gp_operation_add(struct tee_client *client, if (found) { list_del(&op->list); - mc_dev_devel("found cancelled operation %p for started %u", + mc_dev_devel("found cancelled operation %p for started %llu", op, op->started); kfree(op); } else { list_add_tail(&operation->list, &client->operations); - mc_dev_devel("add operation for started %u", + mc_dev_devel("add operation for started %llu", operation->started); } mutex_unlock(&client->quick_lock); @@ -1371,7 +1310,7 @@ void client_gp_operation_remove(struct tee_client *client, struct tee_mmu *client_mmu_create(struct tee_client *client, const struct mc_ioctl_buffer *buf_in, - struct cbuf **cbuf_p, int client_fd) + struct cbuf **cbuf_p) { /* Check if buffer is contained in a cbuf */ struct mc_ioctl_buffer buf = *buf_in; @@ -1392,14 +1331,14 @@ struct tee_mmu *client_mmu_create(struct tee_client *client, } if ((offset + buf.len) > cbuf->len) { - mc_dev_err("crosses cbuf boundary"); - cbuf_put(cbuf); + mc_dev_err(-EINVAL, "crosses cbuf boundary"); + tee_cbuf_put(cbuf); return ERR_PTR(-EINVAL); } } else if (!client_is_kernel(client)) { - mm = get_mm_from_client_fd(client_fd); + mm = get_task_mm(current); if (!mm) { - mc_dev_err("can't get mm from client fd %d", client_fd); + mc_dev_err(-EPERM, "can't get mm"); return ERR_PTR(-EPERM); } } @@ -1410,19 +1349,11 @@ struct tee_mmu *client_mmu_create(struct tee_client *client, mmput(mm); if (IS_ERR_OR_NULL(mmu) && cbuf) - cbuf_put(cbuf); + tee_cbuf_put(cbuf); return mmu; } -void client_mmu_free(struct tee_client *client, uintptr_t va, - struct tee_mmu *mmu, struct cbuf *cbuf) -{ - tee_mmu_delete(mmu); - if (cbuf) - cbuf_put(cbuf); -} - void client_init(void) { INIT_LIST_HEAD(&client_ctx.clients); diff --git a/drivers/gud/MobiCoreDriver/client.h b/drivers/gud/MobiCoreDriver/client.h index ff8c6edffa66..1762157bf07f 100644 --- a/drivers/gud/MobiCoreDriver/client.h +++ b/drivers/gud/MobiCoreDriver/client.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -18,8 +19,12 @@ #include #include /* TASK_COMM_LEN */ -struct tee_object; +#include "public/mc_user.h" /* many types */ + struct tee_client; +struct mcp_open_info; +struct tee_mmu; +struct interworld_session; /* Client */ struct tee_client *client_create(bool is_from_kernel); @@ -27,52 +32,58 @@ void client_get(struct tee_client *client); int client_put(struct tee_client *client); bool client_has_sessions(struct tee_client *client); void client_close(struct tee_client *client); - -/* All clients */ -void clients_kill_sessions(void); +void client_cleanup(void); /* MC */ -int client_open_session(struct tee_client *client, u32 *session_id, - const struct mc_uuid_t *uuid, uintptr_t tci, - size_t tci_len, bool is_gp_uuid, - struct mc_identity *identity, int client_fd); -int client_open_trustlet(struct tee_client *client, u32 *session_id, u32 spid, - uintptr_t trustlet, size_t trustlet_len, - uintptr_t tci, size_t tci_len, int client_fd); -int client_add_session(struct tee_client *client, - const struct tee_object *obj, uintptr_t tci, size_t len, - u32 *p_sid, bool is_gp_uuid, - struct mc_identity *identity, int client_fd); +int client_mc_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + uintptr_t tci_va, size_t tci_len, u32 *session_id); +int client_mc_open_trustlet(struct tee_client *client, + u32 spid, uintptr_t ta_va, size_t ta_len, + uintptr_t tci_va, size_t tci_len, u32 *session_id); +int client_mc_open_common(struct tee_client *client, struct mcp_open_info *info, + u32 *session_id); int client_remove_session(struct tee_client *client, u32 session_id); int client_notify_session(struct tee_client *client, u32 session_id); int client_waitnotif_session(struct tee_client *client, u32 session_id, s32 timeout, bool silent_expiry); int client_get_session_exitcode(struct tee_client *client, u32 session_id, s32 *exit_code); -int client_map_session_wsms(struct tee_client *client, u32 session_id, - struct mc_ioctl_buffer *bufs, int client_fd); -int client_unmap_session_wsms(struct tee_client *client, u32 session_id, - const struct mc_ioctl_buffer *bufs); +int client_mc_map(struct tee_client *client, u32 session_id, + struct tee_mmu *mmu, struct mc_ioctl_buffer *buf); +int client_mc_unmap(struct tee_client *client, u32 session_id, + const struct mc_ioctl_buffer *buf); /* GP */ int client_gp_initialize_context(struct tee_client *client, struct gp_return *gp_ret); int client_gp_register_shared_mem(struct tee_client *client, + struct tee_mmu *mmu, u32 *sva, const struct gp_shared_memory *memref, - struct gp_return *gp_ret, int client_fd); + struct gp_return *gp_ret); int client_gp_release_shared_mem(struct tee_client *client, const struct gp_shared_memory *memref); -int client_gp_open_session(struct tee_client *client, u32 *session_id, +int client_gp_open_session(struct tee_client *client, const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct mc_identity *identity, - struct gp_return *gp_ret, int client_fd); + const struct mc_identity *identity, + struct gp_return *gp_ret, + u32 *session_id); +int client_gp_open_session_domu(struct tee_client *client, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); int client_gp_close_session(struct tee_client *client, u32 session_id); int client_gp_invoke_command(struct tee_client *client, u32 session_id, u32 command_id, struct gp_operation *operation, - struct gp_return *gp_ret, int client_fd); -void client_gp_request_cancellation(struct tee_client *client, u32 started); + struct gp_return *gp_ret); +int client_gp_invoke_command_domu(struct tee_client *client, u32 session_id, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +void client_gp_request_cancellation(struct tee_client *client, u64 started); /* Contiguous buffer */ int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, @@ -81,10 +92,10 @@ int client_cbuf_free(struct tee_client *client, uintptr_t addr); /* GP internal */ struct client_gp_operation { - u32 started; - u32 slot; - bool cancelled; struct list_head list; + u64 started; + u64 slot; + int cancelled; }; /* Called from session when a new operation starts/ends */ @@ -98,9 +109,8 @@ struct cbuf; struct tee_mmu *client_mmu_create(struct tee_client *client, const struct mc_ioctl_buffer *buf_in, - struct cbuf **cbuf_p, int client_fd); -void client_mmu_free(struct tee_client *client, uintptr_t buf, - struct tee_mmu *mmu, struct cbuf *cbuf); + struct cbuf **cbuf_p); +void tee_cbuf_put(struct cbuf *cbuf); /* Buffer shared with SWd at client level */ u32 client_get_cwsm_sva(struct tee_client *client, diff --git a/drivers/gud/MobiCoreDriver/clientlib.c b/drivers/gud/MobiCoreDriver/clientlib.c index 44f90e75c717..c3b62e426506 100644 --- a/drivers/gud/MobiCoreDriver/clientlib.c +++ b/drivers/gud/MobiCoreDriver/clientlib.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -120,7 +121,7 @@ enum mc_result mc_open_device(u32 device_id) /* Make sure TEE was started */ ret = mc_wait_tee_start(); if (ret) { - mc_dev_err("TEE failed to start, now or in the past"); + mc_dev_err(ret, "TEE failed to start, now or in the past"); mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE; goto end; } @@ -133,7 +134,7 @@ enum mc_result mc_open_device(u32 device_id) mc_dev_devel("successfully opened the device"); } else { mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE; - mc_dev_err("could not open device"); + mc_dev_err(-ENOMEM, "could not open device"); } end: @@ -177,11 +178,9 @@ end: EXPORT_SYMBOL(mc_close_device); enum mc_result mc_open_session(struct mc_session_handle *session, - const struct mc_uuid_t *uuid, u8 *tci, u32 len) + const struct mc_uuid_t *uuid, + u8 *tci_va, u32 tci_len) { - struct mc_identity identity = { - .login_type = LOGIN_PUBLIC, - }; enum mc_result ret; /* Check parameters */ @@ -195,22 +194,21 @@ enum mc_result mc_open_session(struct mc_session_handle *session, return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; /* Call core api */ - ret = convert(client_open_session(client, &session->session_id, uuid, - (uintptr_t)tci, len, false, - &identity, -1)); + ret = convert( + client_mc_open_session(client, uuid, (uintptr_t)tci_va, tci_len, + &session->session_id)); clientlib_client_put(); return ret; } EXPORT_SYMBOL(mc_open_session); enum mc_result mc_open_trustlet(struct mc_session_handle *session, u32 spid, - u8 *trustlet, u32 trustlet_len, - u8 *tci, u32 len) + u8 *ta_va, u32 ta_len, u8 *tci_va, u32 tci_len) { enum mc_result ret; /* Check parameters */ - if (!session || !trustlet) + if (!session || !ta_va || !ta_len) return MC_DRV_ERR_INVALID_PARAMETER; if (!is_valid_device(session->device_id)) @@ -220,9 +218,10 @@ enum mc_result mc_open_trustlet(struct mc_session_handle *session, u32 spid, return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; /* Call core api */ - ret = convert(client_open_trustlet(client, &session->session_id, spid, - (uintptr_t)trustlet, trustlet_len, - (uintptr_t)tci, len, -1)); + ret = convert( + client_mc_open_trustlet(client, spid, (uintptr_t)ta_va, ta_len, + (uintptr_t)tci_va, tci_len, + &session->session_id)); clientlib_client_put(); return ret; } @@ -350,8 +349,11 @@ enum mc_result mc_map(struct mc_session_handle *session, void *address, u32 length, struct mc_bulk_map *map_info) { enum mc_result ret; - struct mc_ioctl_buffer bufs[MC_MAP_MAX]; - u32 i; + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)address, + .len = length, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; /* Check parameters */ if (!session) @@ -367,16 +369,10 @@ enum mc_result mc_map(struct mc_session_handle *session, void *address, return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; /* Call core api */ - bufs[0].va = (uintptr_t)address; - bufs[0].len = length; - for (i = 1; i < MC_MAP_MAX; i++) - bufs[i].va = 0; - - ret = convert(client_map_session_wsms(client, session->session_id, - bufs, -1)); + ret = convert(client_mc_map(client, session->session_id, NULL, &buf)); if (ret == MC_DRV_OK) { - map_info->secure_virt_addr = bufs[0].sva; - map_info->secure_virt_len = bufs[0].len; + map_info->secure_virt_addr = buf.sva; + map_info->secure_virt_len = buf.len; } clientlib_client_put(); @@ -388,8 +384,10 @@ enum mc_result mc_unmap(struct mc_session_handle *session, void *address, struct mc_bulk_map *map_info) { enum mc_result ret; - struct mc_ioctl_buffer bufs[MC_MAP_MAX]; - u32 i; + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)address, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; /* Check parameters */ if (!session) @@ -405,14 +403,10 @@ enum mc_result mc_unmap(struct mc_session_handle *session, void *address, return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; /* Call core api */ - bufs[0].va = (uintptr_t)address; - bufs[0].len = map_info->secure_virt_len; - bufs[0].sva = map_info->secure_virt_addr; - for (i = 1; i < MC_MAP_MAX; i++) - bufs[i].va = 0; + buf.len = map_info->secure_virt_len; + buf.sva = map_info->secure_virt_addr; - ret = convert(client_unmap_session_wsms(client, session->session_id, - bufs)); + ret = convert(client_mc_unmap(client, session->session_id, &buf)); clientlib_client_put(); return ret; } diff --git a/drivers/gud/MobiCoreDriver/clock.c b/drivers/gud/MobiCoreDriver/clock.c index 32119f5d1217..1b2e5772802a 100644 --- a/drivers/gud/MobiCoreDriver/clock.c +++ b/drivers/gud/MobiCoreDriver/clock.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -29,41 +30,65 @@ static struct clk_context { struct clk *mc_ce_core_clk; struct clk *mc_ce_bus_clk; struct clk *mc_ce_core_src_clk; + /* Clocks are managed by Linux Kernel. No need to do anything */ + bool no_clock_support; } clk_ctx; int mc_clock_init(void) { - int ret = 0; + int ret; #ifdef MC_CLOCK_CORESRC_DEFAULTRATE int core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; #ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME u32 of_core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; #endif - /* Get core clk src */ - clk_ctx.mc_ce_core_src_clk = clk_get(g_ctx.mcd, "core_clk_src"); - if (IS_ERR(clk_ctx.mc_ce_core_src_clk)) { - ret = PTR_ERR(clk_ctx.mc_ce_core_src_clk); - mc_dev_err("cannot get core src clock: %d", ret); +#endif +#ifdef TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE + struct device_node *np; + + np = of_find_node_by_name(NULL, TT_CLOCK_DEVICE_NAME); + if (!np) { + ret = -ENOENT; + mc_dev_err(ret, "cannot get clock device from DT"); goto error; } + clk_ctx.no_clock_support = + of_property_read_bool(np, TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE); + if (clk_ctx.no_clock_support) + return 0; +#endif /* TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE */ + +#ifdef MC_CLOCK_CORESRC_DEFAULTRATE #ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME - if (of_property_read_u32(g_ctx.mcd->of_node, - MC_CRYPTO_CLOCK_CORESRC_PROPNAME, - &of_core_src_rate)) { + /* Get core clk src */ + clk_ctx.mc_ce_core_src_clk = clk_get(g_ctx.mcd, "core_clk_src"); + if (IS_ERR_OR_NULL(clk_ctx.mc_ce_core_src_clk)) { + ret = PTR_ERR(clk_ctx.mc_ce_core_src_clk); + mc_dev_err(ret, "cannot get core src clock"); + goto error; + } +#endif + +#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME + ret = of_property_read_u32(g_ctx.mcd->of_node, + MC_CRYPTO_CLOCK_CORESRC_PROPNAME, + &of_core_src_rate); + if (ret) { core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; - mc_dev_err("cannot get ce clock frequency from DT, use %d", - core_src_rate); + mc_dev_info("cannot get clock frequency from DT, use %d", + core_src_rate); } else { core_src_rate = of_core_src_rate; } + #endif /* MC_CRYPTO_CLOCK_CORESRC_PROPNAME */ ret = clk_set_rate(clk_ctx.mc_ce_core_src_clk, core_src_rate); if (ret) { clk_put(clk_ctx.mc_ce_core_src_clk); clk_ctx.mc_ce_core_src_clk = NULL; - mc_dev_err("cannot set core clock src rate: %d", ret); + mc_dev_err(ret, "cannot set core clock src rate"); ret = -EIO; goto error; } @@ -73,27 +98,30 @@ int mc_clock_init(void) clk_ctx.mc_ce_core_clk = clk_get(g_ctx.mcd, "core_clk"); if (IS_ERR(clk_ctx.mc_ce_core_clk)) { ret = PTR_ERR(clk_ctx.mc_ce_core_clk); - mc_dev_err("cannot get core clock: %d", ret); + mc_dev_err(ret, "cannot get core clock"); goto error; } + /* Get Interface clk */ clk_ctx.mc_ce_iface_clk = clk_get(g_ctx.mcd, "iface_clk"); if (IS_ERR(clk_ctx.mc_ce_iface_clk)) { clk_put(clk_ctx.mc_ce_core_clk); ret = PTR_ERR(clk_ctx.mc_ce_iface_clk); - mc_dev_err("cannot get iface clock: %d", ret); + mc_dev_err(ret, "cannot get iface clock"); goto error; } + /* Get AXI clk */ clk_ctx.mc_ce_bus_clk = clk_get(g_ctx.mcd, "bus_clk"); if (IS_ERR(clk_ctx.mc_ce_bus_clk)) { clk_put(clk_ctx.mc_ce_iface_clk); clk_put(clk_ctx.mc_ce_core_clk); ret = PTR_ERR(clk_ctx.mc_ce_bus_clk); - mc_dev_err("cannot get AXI bus clock: %d", ret); + mc_dev_err(ret, "cannot get AXI bus clock"); goto error; } - return ret; + + return 0; error: clk_ctx.mc_ce_core_clk = NULL; @@ -105,6 +133,9 @@ error: void mc_clock_exit(void) { + if (clk_ctx.no_clock_support) + return; + if (clk_ctx.mc_ce_iface_clk) clk_put(clk_ctx.mc_ce_iface_clk); @@ -120,23 +151,26 @@ void mc_clock_exit(void) int mc_clock_enable(void) { - int rc; + int ret; - rc = clk_prepare_enable(clk_ctx.mc_ce_core_clk); - if (rc) { - mc_dev_err("cannot enable core clock"); + if (clk_ctx.no_clock_support) + return 0; + + ret = clk_prepare_enable(clk_ctx.mc_ce_core_clk); + if (ret) { + mc_dev_err(ret, "cannot enable core clock"); goto err_core; } - rc = clk_prepare_enable(clk_ctx.mc_ce_iface_clk); - if (rc) { - mc_dev_err("cannot enable interface clock"); + ret = clk_prepare_enable(clk_ctx.mc_ce_iface_clk); + if (ret) { + mc_dev_err(ret, "cannot enable interface clock"); goto err_iface; } - rc = clk_prepare_enable(clk_ctx.mc_ce_bus_clk); - if (rc) { - mc_dev_err("cannot enable bus clock"); + ret = clk_prepare_enable(clk_ctx.mc_ce_bus_clk); + if (ret) { + mc_dev_err(ret, "cannot enable bus clock"); goto err_bus; } @@ -147,11 +181,14 @@ err_bus: err_iface: clk_disable_unprepare(clk_ctx.mc_ce_core_clk); err_core: - return rc; + return ret; } void mc_clock_disable(void) { + if (clk_ctx.no_clock_support) + return; + if (clk_ctx.mc_ce_iface_clk) clk_disable_unprepare(clk_ctx.mc_ce_iface_clk); diff --git a/drivers/gud/MobiCoreDriver/clock.h b/drivers/gud/MobiCoreDriver/clock.h index 21095499efb5..b0b2cabb7b84 100644 --- a/drivers/gud/MobiCoreDriver/clock.h +++ b/drivers/gud/MobiCoreDriver/clock.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/fastcall.c b/drivers/gud/MobiCoreDriver/fastcall.c index 85c3ef19928f..6d91f9c378e3 100644 --- a/drivers/gud/MobiCoreDriver/fastcall.c +++ b/drivers/gud/MobiCoreDriver/fastcall.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -11,467 +12,138 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ -#include -#include + #include -#include -#include -#include #include #include /* local_clock */ #include #if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE #include /* local_clock */ #endif -#include - -#include "public/mc_user.h" -#include "public/mc_linux_api.h" #include "mci/mcifc.h" -#include "platform.h" /* MC_FASTCALL_WORKER_THREAD and more */ +#include "platform.h" /* MC_SMC_FASTCALL */ #include "main.h" -#include "clock.h" /* mc_clock_enable, mc_clock_disable */ #include "fastcall.h" -#if KERNEL_VERSION(3, 15, 0) > LINUX_VERSION_CODE -#define MIN_NICE -20 +/* Use the arch_extension sec pseudo op before switching to secure world */ +#if defined(__GNUC__) && \ + defined(__GNUC_MINOR__) && \ + defined(__GNUC_PATCHLEVEL__) && \ + ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)) \ + >= 40502 +#ifndef CONFIG_ARM64 +#define MC_ARCH_EXTENSION_SEC +#endif #endif -struct fastcall_work { -#ifdef MC_FASTCALL_WORKER_THREAD - struct kthread_work work; -#else - struct work_struct work; -#endif - void *data; -}; - -/* generic fast call parameters */ -union mc_fc_generic { - struct mc_fc_as_in { +/* Base for all fastcalls, do not use outside of other structs */ +union fc_common { + struct { u32 cmd; u32 param[3]; - } as_in; + } in; + struct { u32 resp; u32 ret; u32 param[2]; - } as_out; + } out; }; -/* fast call init */ -union mc_fc_init { - union mc_fc_generic as_generic; +union fc_init { + union fc_common common; + struct { u32 cmd; u32 base; u32 nq_info; u32 mcp_info; - } as_in; + } in; + struct { u32 resp; u32 ret; u32 flags; u32 rfu; - } as_out; + } out; }; -/* fast call info parameters */ -union mc_fc_info { - union mc_fc_generic as_generic; +union fc_info { + union fc_common common; + struct { u32 cmd; u32 ext_info_id; - u32 rfu[2]; - } as_in; + } in; + struct { u32 resp; u32 ret; u32 state; u32 ext_info; - } as_out; + } out; }; -#ifdef TBASE_CORE_SWITCHER -/* fast call switch Core parameters */ -union mc_fc_swich_core { - union mc_fc_generic as_generic; +union fc_trace { + union fc_common common; + struct { u32 cmd; - u32 core_id; - u32 rfu[2]; - } as_in; + u32 buffer_low; + u32 buffer_high; + u32 size; + } in; + struct { u32 resp; u32 ret; - u32 state; - u32 ext_info; - } as_out; + } out; }; -#endif -#ifdef MC_FASTCALL_WORKER_THREAD -static struct task_struct *fastcall_thread; -static DEFINE_KTHREAD_WORKER(fastcall_worker); -#endif +union fc_nsiq { + union fc_common common; + + struct { + u32 cmd; + u32 debug_ret; + u32 debug_session_id; + u32 debug_payload; + } in; + + struct { + u32 resp; + u32 ret; + } out; +}; + +union fc_yield { + union fc_common common; + + struct { + u32 cmd; + u32 debug_ret; + u32 debug_timeslice; + } in; + + struct { + u32 resp; + u32 ret; + } out; +}; /* Structure to log SMC calls */ struct smc_log_entry { u64 cpu_clk; - struct mc_fc_as_in as_in; + int cpu_id; + union fc_common fc; }; -#define SMC_LOG_SIZE 256 +#define SMC_LOG_SIZE 1024 static struct smc_log_entry smc_log[SMC_LOG_SIZE]; static int smc_log_index; -/* - * _smc() - fast call to MobiCore - * - * @data: pointer to fast call data - */ -static inline int _smc(union mc_fc_generic *mc_fc_generic) -{ - if (!mc_fc_generic) - return -EINVAL; - - /* Log SMC call */ - smc_log[smc_log_index].cpu_clk = local_clock(); - smc_log[smc_log_index].as_in = mc_fc_generic->as_in; - if (++smc_log_index >= SMC_LOG_SIZE) - smc_log_index = 0; - -#ifdef MC_SMC_FASTCALL - return smc_fastcall(mc_fc_generic, sizeof(*mc_fc_generic)); -#else /* MC_SMC_FASTCALL */ - { -#ifdef CONFIG_ARM64 - /* SMC expect values in x0-x3 */ - register u64 reg0 __asm__("x0") = mc_fc_generic->as_in.cmd; - register u64 reg1 __asm__("x1") = mc_fc_generic->as_in.param[0]; - register u64 reg2 __asm__("x2") = mc_fc_generic->as_in.param[1]; - register u64 reg3 __asm__("x3") = mc_fc_generic->as_in.param[2]; - - /* - * According to AARCH64 SMC Calling Convention (ARM DEN 0028A), - * section 3.1: registers x4-x17 are unpredictable/scratch - * registers. So we have to make sure that the compiler does - * not allocate any of those registers by letting him know that - * the asm code might clobber them. - */ - __asm__ volatile ( - "smc #0\n" - : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) - : - : "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", - "x12", "x13", "x14", "x15", "x16", "x17" - ); -#else /* CONFIG_ARM64 */ - /* SMC expect values in r0-r3 */ - register u32 reg0 __asm__("r0") = mc_fc_generic->as_in.cmd; - register u32 reg1 __asm__("r1") = mc_fc_generic->as_in.param[0]; - register u32 reg2 __asm__("r2") = mc_fc_generic->as_in.param[1]; - register u32 reg3 __asm__("r3") = mc_fc_generic->as_in.param[2]; - - __asm__ volatile ( -#ifdef MC_ARCH_EXTENSION_SEC - /* - * This pseudo op is supported and required from - * binutils 2.21 on - */ - ".arch_extension sec\n" -#endif /* MC_ARCH_EXTENSION_SEC */ - "smc #0\n" - : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) - ); - -#ifdef __ARM_VE_A9X4_QEMU__ - /* - * Qemu does not return to the address following the SMC - * instruction so we have to insert several nop instructions to - * workaround this Qemu bug. - */ - __asm__ volatile ( - "nop\n" - "nop\n" - "nop\n" - "nop" - ); -#endif /* __ARM_VE_A9X4_QEMU__ */ -#endif /* !CONFIG_ARM64 */ - - /* set response */ - mc_fc_generic->as_out.resp = reg0; - mc_fc_generic->as_out.ret = reg1; - mc_fc_generic->as_out.param[0] = reg2; - mc_fc_generic->as_out.param[1] = reg3; - } - return 0; -#endif /* !MC_SMC_FASTCALL */ -} - -#ifdef TBASE_CORE_SWITCHER -static int active_cpu; - -#ifdef MC_FASTCALL_WORKER_THREAD -static int mc_cpu_offline(int cpu) -{ - int i; - - if (active_cpu != cpu) { - mc_dev_devel("not active CPU, no action taken"); - return 0; - } - - /* Chose the first online CPU and switch! */ - for_each_online_cpu(i) { - if (cpu != i) { - mc_dev_info("CPU %d is dying, switching to %d", - cpu, i); - return mc_switch_core(i); - } - - mc_dev_devel("Skipping CPU %d", cpu); - } - - return 0; -} - -#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE -static int mobicore_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - int cpu = (int)(uintptr_t)hcpu; - - switch (action) { - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - mc_dev_devel("Cpu %d is going to die", cpu); - mc_cpu_offline(cpu); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block mobicore_cpu_notifer = { - .notifier_call = mobicore_cpu_callback, -}; -#else -static int nq_cpu_down_prep(unsigned int cpu) -{ - mc_dev_info("CPU #%d is going to die", cpu); - return mc_cpu_offline(cpu); -} -#endif -#endif /* MC_FASTCALL_WORKER_THREAD */ - -static cpumask_t mc_exec_core_switch(union mc_fc_generic *mc_fc_generic) -{ - cpumask_t cpu; - int new_cpu; - u32 cpu_id[] = CPU_IDS; - - new_cpu = mc_fc_generic->as_in.param[0]; - mc_fc_generic->as_in.param[0] = cpu_id[mc_fc_generic->as_in.param[0]]; - - if (_smc(mc_fc_generic) != 0 || mc_fc_generic->as_out.ret != 0) { - mc_dev_err("CoreSwap failed %d -> %d (cpu %d still active)", - raw_smp_processor_id(), new_cpu, - raw_smp_processor_id()); - } else { - active_cpu = new_cpu; - mc_dev_devel("CoreSwap ok %d -> %d", - raw_smp_processor_id(), active_cpu); - } - cpumask_clear(&cpu); - cpumask_set_cpu(active_cpu, &cpu); - return cpu; -} - -static ssize_t debug_coreswitch_write(struct file *file, - const char __user *buffer, - size_t buffer_len, loff_t *x) -{ - int new_cpu = 0; - - /* Invalid data, nothing to do */ - if (buffer_len < 1) - return -EINVAL; - - if (kstrtoint_from_user(buffer, buffer_len, 0, &new_cpu)) - return -EINVAL; - - mc_dev_devel("Set active cpu to %d", new_cpu); - mc_switch_core(new_cpu); - return buffer_len; -} - -static ssize_t debug_coreswitch_read(struct file *file, char __user *buffer, - size_t buffer_len, loff_t *ppos) -{ - char cpu_str[8]; - int ret = 0; - - ret = snprintf(cpu_str, sizeof(cpu_str), "%d\n", mc_active_core()); - if (ret < 0) - return -EINVAL; - - return simple_read_from_buffer(buffer, buffer_len, ppos, - cpu_str, ret); -} - -static const struct file_operations mc_debug_coreswitch_ops = { - .write = debug_coreswitch_write, - .read = debug_coreswitch_read, -}; -#else /* TBASE_CORE_SWITCHER */ -static inline cpumask_t mc_exec_core_switch(union mc_fc_generic *mc_fc_generic) -{ - return CPU_MASK_CPU0; -} -#endif /* !TBASE_CORE_SWITCHER */ - -#ifdef MC_SMC_FASTCALL -static inline int nq_set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) -{ - return 0; -} -#else /* MC_SMC_FASTCALL */ -static inline int nq_set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) -{ - return set_cpus_allowed_ptr(p, &new_mask); -} -#endif /* ! MC_SMC_FASTCALL */ - -#ifdef MC_FASTCALL_WORKER_THREAD -static void fastcall_work_func(struct kthread_work *work) -#else -static void fastcall_work_func(struct work_struct *work) -#endif -{ - struct fastcall_work *fc_work = - container_of(work, struct fastcall_work, work); - union mc_fc_generic *mc_fc_generic = fc_work->data; - - if (!mc_fc_generic) - return; - - mc_clock_enable(); - - if (mc_fc_generic->as_in.cmd == MC_FC_SWAP_CPU) { -#ifdef MC_FASTCALL_WORKER_THREAD - cpumask_t new_msk = mc_exec_core_switch(mc_fc_generic); - - nq_set_cpus_allowed(fastcall_thread, new_msk); -#else - mc_exec_core_switch(mc_fc_generic); -#endif - } else { - _smc(mc_fc_generic); - } - - mc_clock_disable(); -} - -static bool mc_fastcall(void *data) -{ -#ifdef MC_FASTCALL_WORKER_THREAD - struct fastcall_work fc_work = { - KTHREAD_WORK_INIT(fc_work.work, fastcall_work_func), - .data = data, - }; - -#if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE - if (!kthread_queue_work(&fastcall_worker, &fc_work.work)) - return false; - - /* If work is queued or executing, wait for it to finish execution */ - kthread_flush_work(&fc_work.work); -#else - if (!queue_kthread_work(&fastcall_worker, &fc_work.work)) - return false; - - /* If work is queued or executing, wait for it to finish execution */ - flush_kthread_work(&fc_work.work); -#endif -#else - struct fastcall_work fc_work = { - .data = data, - }; - INIT_WORK_ONSTACK(&fc_work.work, fastcall_work_func); - - if (!schedule_work_on(0, &fc_work.work)) - return false; - - flush_work(&fc_work.work); -#endif - return true; -} - -int mc_fastcall_init(void) -{ - int ret = mc_clock_init(); - - if (ret) - return ret; - -#ifdef MC_FASTCALL_WORKER_THREAD - fastcall_thread = kthread_create(kthread_worker_fn, &fastcall_worker, - "tee_fastcall"); - if (IS_ERR(fastcall_thread)) { - ret = PTR_ERR(fastcall_thread); - fastcall_thread = NULL; - mc_dev_err("cannot create fastcall wq: %d", ret); - return ret; - } - - set_user_nice(fastcall_thread, MIN_NICE); - - /* this thread MUST run on CPU 0 at startup */ - nq_set_cpus_allowed(fastcall_thread, CPU_MASK_CPU0); - - wake_up_process(fastcall_thread); -#ifdef TBASE_CORE_SWITCHER -#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE - ret = register_cpu_notifier(&mobicore_cpu_notifer); -#else - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, - "tee/trustonic:online", - NULL, nq_cpu_down_prep); -#endif - if (ret < 0) { - mc_dev_err("cpu online callback setup failed: %d", ret); - return ret; - } - - /* Create debugfs structs entry */ - debugfs_create_file("active_cpu", 0600, g_ctx.debug_dir, NULL, - &mc_debug_coreswitch_ops); -#endif -#endif /* MC_FASTCALL_WORKER_THREAD */ - return 0; -} - -void mc_fastcall_exit(void) -{ -#ifdef MC_FASTCALL_WORKER_THREAD - if (!IS_ERR_OR_NULL(fastcall_thread)) { -#ifdef TBASE_CORE_SWITCHER -#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE - unregister_cpu_notifier(&mobicore_cpu_notifer); -#else - cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN); -#endif -#endif - kthread_stop(fastcall_thread); - fastcall_thread = NULL; - } -#endif /* MC_FASTCALL_WORKER_THREAD */ - mc_clock_exit(); -} - /* * convert fast call return code to linux driver module error code */ @@ -489,47 +161,122 @@ static int convert_fc_ret(u32 ret) } } -int mc_fc_init(uintptr_t base_pa, ptrdiff_t off, size_t q_len, size_t buf_len) +/* + * __smc() - fast call to MobiCore + * + * @data: pointer to fast call data + */ +static inline int __smc(union fc_common *fc, const char *func) { -#ifdef CONFIG_ARM64 - u32 base_high = (u32)(base_pa >> 32); -#else - u32 base_high = 0; -#endif - union mc_fc_init fc_init; - - /* Call the INIT fastcall to setup MobiCore initialization */ - memset(&fc_init, 0, sizeof(fc_init)); - fc_init.as_in.cmd = MC_FC_INIT; - /* base address of mci buffer PAGE_SIZE (default is 4KB) aligned */ - fc_init.as_in.base = (u32)base_pa; - /* notification buffer start/length [16:16] [start, length] */ - fc_init.as_in.nq_info = - (u32)(((base_high & 0xFFFF) << 16) | (q_len & 0xFFFF)); - /* mcp buffer start/length [16:16] [start, length] */ - fc_init.as_in.mcp_info = (u32)((off << 16) | (buf_len & 0xFFFF)); - mc_dev_devel("cmd=%d, base=0x%08x,nq_info=0x%08x, mcp_info=0x%08x", - fc_init.as_in.cmd, fc_init.as_in.base, - fc_init.as_in.nq_info, fc_init.as_in.mcp_info); - mc_fastcall(&fc_init.as_generic); - mc_dev_devel("out cmd=0x%08x, ret=0x%08x", fc_init.as_out.resp, - fc_init.as_out.ret); - if (fc_init.as_out.flags & MC_FC_INIT_FLAG_LPAE) - g_ctx.f_lpae = true; - - return convert_fc_ret(fc_init.as_out.ret); -} - -int mc_fc_info(u32 ext_info_id, u32 *state, u32 *ext_info) -{ - union mc_fc_info fc_info; int ret = 0; - memset(&fc_info, 0, sizeof(fc_info)); - fc_info.as_in.cmd = MC_FC_INFO; - fc_info.as_in.ext_info_id = ext_info_id; - mc_fastcall(&fc_info.as_generic); - ret = convert_fc_ret(fc_info.as_out.ret); + /* Log SMC call */ + smc_log[smc_log_index].cpu_clk = local_clock(); + smc_log[smc_log_index].cpu_id = raw_smp_processor_id(); + smc_log[smc_log_index].fc = *fc; + if (++smc_log_index >= SMC_LOG_SIZE) + smc_log_index = 0; + +#ifdef MC_SMC_FASTCALL + ret = smc_fastcall(fc, sizeof(*fc)); +#else /* MC_SMC_FASTCALL */ + { +#ifdef CONFIG_ARM64 + /* SMC expect values in x0-x3 */ + register u64 reg0 __asm__("x0") = fc->in.cmd; + register u64 reg1 __asm__("x1") = fc->in.param[0]; + register u64 reg2 __asm__("x2") = fc->in.param[1]; + register u64 reg3 __asm__("x3") = fc->in.param[2]; + + /* + * According to AARCH64 SMC Calling Convention (ARM DEN 0028A), + * section 3.1: registers x4-x17 are unpredictable/scratch + * registers. So we have to make sure that the compiler does + * not allocate any of those registers by letting him know that + * the asm code might clobber them. + */ + __asm__ volatile ( + "smc #0\n" + : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) + : + : "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", + "x12", "x13", "x14", "x15", "x16", "x17" + ); +#else /* CONFIG_ARM64 */ + /* SMC expect values in r0-r3 */ + register u32 reg0 __asm__("r0") = fc->in.cmd; + register u32 reg1 __asm__("r1") = fc->in.param[0]; + register u32 reg2 __asm__("r2") = fc->in.param[1]; + register u32 reg3 __asm__("r3") = fc->in.param[2]; + + __asm__ volatile ( +#ifdef MC_ARCH_EXTENSION_SEC + /* + * This pseudo op is supported and required from + * binutils 2.21 on + */ + ".arch_extension sec\n" +#endif /* MC_ARCH_EXTENSION_SEC */ + "smc #0\n" + : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) + ); + +#endif /* !CONFIG_ARM64 */ + + /* set response */ + fc->out.resp = reg0; + fc->out.ret = reg1; + fc->out.param[0] = reg2; + fc->out.param[1] = reg3; + } +#endif /* !MC_SMC_FASTCALL */ + + if (ret) { + mc_dev_err(ret, "failed for %s", func); + } else { + ret = convert_fc_ret(fc->out.ret); + if (ret) + mc_dev_err(ret, "%s failed (%x)", func, fc->out.ret); + } + + return ret; +} + +#define smc(__fc__) __smc(__fc__.common, __func__) + +int fc_init(uintptr_t addr, ptrdiff_t off, size_t q_len, size_t buf_len) +{ + union fc_init fc; +#ifdef CONFIG_ARM64 + u32 addr_high = (u32)(addr >> 32); +#else + u32 addr_high = 0; +#endif + + /* Call the INIT fastcall to setup MobiCore initialization */ + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_INIT; + /* base address of mci buffer PAGE_SIZE (default is 4KB) aligned */ + fc.in.base = (u32)addr; + /* notification buffer start/length [16:16] [start, length] */ + fc.in.nq_info = (u32)(((addr_high & 0xFFFF) << 16) | (q_len & 0xFFFF)); + /* mcp buffer start/length [16:16] [start, length] */ + fc.in.mcp_info = (u32)((off << 16) | (buf_len & 0xFFFF)); + mc_dev_devel("cmd=0x%08x, base=0x%08x, nq_info=0x%08x, mcp_info=0x%08x", + fc.in.cmd, fc.in.base, fc.in.nq_info, + fc.in.mcp_info); + return smc(&fc); +} + +int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info) +{ + union fc_info fc; + int ret = 0; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_INFO; + fc.in.ext_info_id = ext_info_id; + ret = smc(&fc); if (ret) { if (state) *state = MC_STATUS_NOT_INITIALIZED; @@ -537,70 +284,67 @@ int mc_fc_info(u32 ext_info_id, u32 *state, u32 *ext_info) if (ext_info) *ext_info = 0; - mc_dev_err("code %d for idx %d", ret, ext_info_id); + mc_dev_err(ret, "failed for index %d", ext_info_id); } else { if (state) - *state = fc_info.as_out.state; + *state = fc.out.state; if (ext_info) - *ext_info = fc_info.as_out.ext_info; + *ext_info = fc.out.ext_info; } return ret; } -int mc_fc_mem_trace(phys_addr_t buffer, u32 size) +int fc_trace_init(phys_addr_t buffer, u32 size) { - union mc_fc_generic mc_fc_generic; + union fc_trace fc; - memset(&mc_fc_generic, 0, sizeof(mc_fc_generic)); - mc_fc_generic.as_in.cmd = MC_FC_MEM_TRACE; - mc_fc_generic.as_in.param[0] = (u32)buffer; + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_MEM_TRACE; + fc.in.buffer_low = (u32)buffer; #ifdef CONFIG_ARM64 - mc_fc_generic.as_in.param[1] = (u32)(buffer >> 32); + fc.in.buffer_high = (u32)(buffer >> 32); #endif - mc_fc_generic.as_in.param[2] = size; - mc_fastcall(&mc_fc_generic); - return convert_fc_ret(mc_fc_generic.as_out.ret); + fc.in.size = size; + return smc(&fc); } -int mc_fc_nsiq(void) +int fc_trace_deinit(void) { - union mc_fc_generic fc; - int ret; - - memset(&fc, 0, sizeof(fc)); - fc.as_in.cmd = MC_SMC_N_SIQ; - mc_fastcall(&fc); - ret = convert_fc_ret(fc.as_out.ret); - if (ret) - mc_dev_err("failed: %d", ret); - - return ret; + return fc_trace_init(0, 0); } -int mc_fc_yield(void) +/* sid, payload only used for debug purpose */ +int fc_nsiq(u32 session_id, u32 payload) { - union mc_fc_generic fc; - int ret; + union fc_nsiq fc; memset(&fc, 0, sizeof(fc)); - fc.as_in.cmd = MC_SMC_N_YIELD; - mc_fastcall(&fc); - ret = convert_fc_ret(fc.as_out.ret); - if (ret) - mc_dev_err("failed: %d", ret); + fc.in.cmd = MC_SMC_N_SIQ; + fc.in.debug_session_id = session_id; + fc.in.debug_payload = payload; + return smc(&fc); +} - return ret; +/* timeslice only used for debug purpose */ +int fc_yield(u32 timeslice) +{ + union fc_yield fc; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_SMC_N_YIELD; + fc.in.debug_timeslice = timeslice; + return smc(&fc); } static int show_smc_log_entry(struct kasnprintf_buf *buf, struct smc_log_entry *entry) { - return kasnprintf(buf, "%20llu %10d 0x%08x 0x%08x 0x%08x\n", - entry->cpu_clk, (s32)entry->as_in.cmd, - entry->as_in.param[0], entry->as_in.param[1], - entry->as_in.param[2]); + return kasnprintf(buf, "%20llu %10d 0x%08x 0x%08x 0x%08x 0x%08x\n", + entry->cpu_clk, entry->cpu_id, entry->fc.in.cmd, + entry->fc.in.param[0], entry->fc.in.param[1], + entry->fc.in.param[2]); } /* @@ -611,7 +355,7 @@ int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf) { int i, ret = 0; - ret = kasnprintf(buf, "%20s %10s %-10s %-10s %-10s\n", + ret = kasnprintf(buf, "%10s %20s %10s %-10s %-10s %-10s\n", "CPU id", "CPU clock", "command", "param1", "param2", "param3"); if (ret < 0) return ret; @@ -633,38 +377,3 @@ int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf) return ret; } - -#ifdef TBASE_CORE_SWITCHER -int mc_active_core(void) -{ - return active_cpu; -} - -int mc_switch_core(int cpu) -{ - s32 ret = 0; - union mc_fc_swich_core fc_switch_core; - - if (cpu >= nr_cpu_ids) - return -EINVAL; - - if (!cpu_online(cpu)) - return 1; - - memset(&fc_switch_core, 0, sizeof(fc_switch_core)); - fc_switch_core.as_in.cmd = MC_FC_SWAP_CPU; - if (cpu < COUNT_OF_CPUS) - fc_switch_core.as_in.core_id = cpu; - else - fc_switch_core.as_in.core_id = 0; - - mc_dev_devel("<- cmd=0x%08x, core_id=0x%08x", - fc_switch_core.as_in.cmd, fc_switch_core.as_in.core_id); - mc_dev_devel("<- cpu=0x%08x, active_cpu=0x%08x", - cpu, active_cpu); - mc_fastcall(&fc_switch_core.as_generic); - ret = convert_fc_ret(fc_switch_core.as_out.ret); - mc_dev_devel("exit with %d/0x%08X", ret, ret); - return ret; -} -#endif diff --git a/drivers/gud/MobiCoreDriver/fastcall.h b/drivers/gud/MobiCoreDriver/fastcall.h index 805ca03b47cf..97b3103d26e7 100644 --- a/drivers/gud/MobiCoreDriver/fastcall.h +++ b/drivers/gud/MobiCoreDriver/fastcall.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -15,25 +16,12 @@ #ifndef _TBASE_FASTCALL_H_ #define _TBASE_FASTCALL_H_ -/* Use the arch_extension sec pseudo op before switching to secure world */ -#if defined(__GNUC__) && \ - defined(__GNUC_MINOR__) && \ - defined(__GNUC_PATCHLEVEL__) && \ - ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)) \ - >= 40502 -#ifndef CONFIG_ARM64 -#define MC_ARCH_EXTENSION_SEC -#endif -#endif - -int mc_fc_init(uintptr_t base_pa, ptrdiff_t off, size_t q_len, size_t buf_len); -int mc_fc_info(u32 ext_info_id, u32 *state, u32 *ext_info); -int mc_fc_mem_trace(phys_addr_t buffer, u32 size); -int mc_fc_nsiq(void); -int mc_fc_yield(void); - -int mc_fastcall_init(void); -void mc_fastcall_exit(void); +int fc_init(uintptr_t base_pa, ptrdiff_t off, size_t q_len, size_t buf_len); +int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info); +int fc_trace_init(phys_addr_t buffer, u32 size); +int fc_trace_deinit(void); +int fc_nsiq(u32 session_id, u32 payload); +int fc_yield(u32 timeslice); int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf); diff --git a/drivers/gud/MobiCoreDriver/iwp.c b/drivers/gud/MobiCoreDriver/iwp.c index db9a99ad6f62..557bca5d4ebb 100644 --- a/drivers/gud/MobiCoreDriver/iwp.c +++ b/drivers/gud/MobiCoreDriver/iwp.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -27,6 +28,10 @@ #include #include #include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#endif #include "public/GP/tee_client_api.h" /* GP error codes/origins FIXME move */ #include "public/mc_user.h" @@ -38,11 +43,11 @@ #include "mci/mcitime.h" /* struct mcp_time */ #include "mci/mciiwp.h" -#include "platform.h" /* IRQ number */ #include "main.h" -#include "fastcall.h" -#include "logging.h" +#include "admin.h" /* tee_object* for 'blob' */ +#include "mmu.h" /* MMU for 'blob' */ #include "nq.h" +#include "xen_fe.h" #include "iwp.h" #define IWP_RETRIES 5 @@ -57,10 +62,11 @@ struct iws { struct list_head list; - u32 offset; + u64 slot; }; static struct { + bool iwp_dead; struct interworld_session *iws; /* Interworld lists lock */ struct mutex iws_list_lock; @@ -71,6 +77,28 @@ static struct { /* Sessions */ struct mutex sessions_lock; struct list_head sessions; + /* TEE bad state detection */ + struct notifier_block tee_stop_notifier; + /* Log of last commands */ +#define LAST_CMDS_SIZE 256 + struct mutex last_cmds_mutex; /* Log protection */ + struct command_info { + u64 cpu_clk; /* Kernel time */ + pid_t pid; /* Caller PID */ + u32 id; /* IWP command ID */ + u32 session_id; + char uuid_str[34]; + enum state { + UNUSED, /* Unused slot */ + PENDING, /* Previous command in progress */ + SENT, /* Waiting for response */ + COMPLETE, /* Got result */ + FAILED, /* Something went wrong */ + } state; /* Command processing state */ + struct gp_return result; /* Command result */ + int errno; /* Return code */ + } last_cmds[LAST_CMDS_SIZE]; + int last_cmds_index; } l_ctx; static void iwp_notif_handler(u32 id, u32 payload) @@ -79,8 +107,7 @@ static void iwp_notif_handler(u32 id, u32 payload) mutex_lock(&l_ctx.sessions_lock); list_for_each_entry(candidate, &l_ctx.sessions, list) { - mc_dev_devel("candidate->slot [%08x]", - candidate->slot); + mc_dev_devel("candidate->slot [%08llx]", candidate->slot); /* If id is SID_CANCEL_OPERATION, there is pseudo session */ if (candidate->slot == payload && (id != SID_CANCEL_OPERATION || candidate->sid == id)) { @@ -91,13 +118,14 @@ static void iwp_notif_handler(u32 id, u32 payload) mutex_unlock(&l_ctx.sessions_lock); if (!iwp_session) { - mc_dev_err("IWP no session found for id=0x%x slot=0x%x", + mc_dev_err(-ENXIO, "IWP no session found for id=0x%x slot=0x%x", id, payload); return; } mc_dev_devel("IWP: iwp_session [%p] id [%08x] slot [%08x]", iwp_session, id, payload); + nq_session_state_update(&iwp_session->nq_session, NQ_NOTIF_RECEIVED); complete(&iwp_session->completion); } @@ -106,80 +134,172 @@ void iwp_session_init(struct iwp_session *iwp_session, { nq_session_init(&iwp_session->nq_session, true); iwp_session->sid = SID_INVALID; - iwp_session->slot = (u32)-1; /* 0 is a valid slot */ + iwp_session->slot = INVALID_IWS_SLOT; INIT_LIST_HEAD(&iwp_session->list); mutex_init(&iwp_session->notif_wait_lock); init_completion(&iwp_session->completion); - mutex_init(&iwp_session->exit_code_lock); - iwp_session->exit_code = 0; mutex_init(&iwp_session->iws_lock); iwp_session->state = IWP_SESSION_RUNNING; if (identity) iwp_session->client_identity = *identity; } -static u32 iws_slot_get(void) +static u64 iws_slot_get(void) { struct iws *iws; - u32 ret = INVALID_IWS_SLOT; + u64 slot = INVALID_IWS_SLOT; + + if (is_xen_domu()) + return (uintptr_t)kzalloc(sizeof(*iws), GFP_KERNEL); mutex_lock(&l_ctx.iws_list_lock); if (!list_empty(&l_ctx.free_iws)) { iws = list_first_entry(&l_ctx.free_iws, struct iws, list); - ret = iws->offset; + slot = iws->slot; list_move(&iws->list, &l_ctx.allocd_iws); atomic_inc(&g_ctx.c_slots); + mc_dev_devel("got slot %llu", slot); } mutex_unlock(&l_ctx.iws_list_lock); - return ret; + return slot; } /* Passing INVALID_IWS_SLOT is supported */ -static void iws_slot_put(u32 offset) +static void iws_slot_put(u64 slot) { struct iws *iws; + bool found = false; + + if (is_xen_domu()) { + kfree((void *)(uintptr_t)slot); + return; + } mutex_lock(&l_ctx.iws_list_lock); list_for_each_entry(iws, &l_ctx.allocd_iws, list) { - if (offset == iws->offset) { + if (slot == iws->slot) { list_move(&iws->list, &l_ctx.free_iws); atomic_dec(&g_ctx.c_slots); + found = true; + mc_dev_devel("put slot %llu", slot); break; } } mutex_unlock(&l_ctx.iws_list_lock); + + if (!found) + mc_dev_err(-EINVAL, "slot %llu not found", slot); } -static inline struct interworld_session *slot_to_iws(u32 slot) +static inline struct interworld_session *slot_to_iws(u64 slot) { - return (struct interworld_session *)((uintptr_t)l_ctx.iws + slot); + if (is_xen_domu()) + return (struct interworld_session *)(uintptr_t)slot; + + return (struct interworld_session *)((uintptr_t)l_ctx.iws + (u32)slot); } /* * IWP command functions */ -static int iwp_cmd(struct iwp_session *iwp_session, u32 id) +static int iwp_cmd(struct iwp_session *iwp_session, u32 id, + struct teec_uuid *uuid, bool killable) { - struct completion *completion; + struct command_info *cmd_info; int ret; + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + memset(cmd_info, 0, sizeof(*cmd_info)); + cmd_info->cpu_clk = local_clock(); + cmd_info->pid = current->pid; + cmd_info->id = id; + if (id == SID_OPEN_SESSION || id == SID_OPEN_TA) { + /* Keep UUID because it's an 'open session' cmd */ + const char *cuuid = (const char *)uuid; + size_t i; + + cmd_info->uuid_str[0] = ' '; + for (i = 0; i < sizeof(*uuid); i++) { + snprintf(&cmd_info->uuid_str[1 + i * 2], 3, "%02x", + cuuid[i]); + } + } else if (id == SID_CANCEL_OPERATION) { + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + + if (iws) + cmd_info->session_id = iws->session_handle; + else + cmd_info->session_id = 0; + } else { + cmd_info->session_id = iwp_session->sid; + } + + cmd_info->state = PENDING; + iwp_set_ret(0, &cmd_info->result); + if (++l_ctx.last_cmds_index >= LAST_CMDS_SIZE) + l_ctx.last_cmds_index = 0; + mutex_unlock(&l_ctx.last_cmds_mutex); + + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + mc_dev_devel("psid [%08x], sid [%08x]", id, iwp_session->sid); ret = nq_session_notify(&iwp_session->nq_session, id, iwp_session->slot); if (ret) { - mc_dev_err("sid [%08x]: sending failed, ret = %d", - iwp_session->sid, ret); + mc_dev_err(ret, "sid [%08x]: sending failed", iwp_session->sid); + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info->errno = ret; + cmd_info->state = FAILED; + mutex_unlock(&l_ctx.last_cmds_mutex); return ret; } - completion = &iwp_session->completion; + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info->state = SENT; + mutex_unlock(&l_ctx.last_cmds_mutex); /* * NB: Wait cannot be interruptible as we need an answer from SWd. It's * up to the user-space to request a cancellation (for open session and * command invocation operations.) + * + * We do provide a way out to make applications killable in some cases + * though. */ - wait_for_completion(completion); + if (killable) { + ret = wait_for_completion_killable(&iwp_session->completion); + if (ret) { + iwp_request_cancellation(iwp_session->slot); + /* Make sure the SWd did not die in the meantime */ + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + + wait_for_completion(&iwp_session->completion); + } + } else { + wait_for_completion(&iwp_session->completion); + } + + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + { + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + + cmd_info->result.origin = iws->return_origin; + cmd_info->result.value = iws->status; + if (id == SID_OPEN_SESSION || id == SID_OPEN_TA) + cmd_info->session_id = iws->session_handle; + } + cmd_info->state = COMPLETE; + mutex_unlock(&l_ctx.last_cmds_mutex); + nq_session_state_update(&iwp_session->nq_session, NQ_NOTIF_CONSUMED); return 0; } @@ -224,6 +344,10 @@ int iwp_set_ret(int ret, struct gp_return *gp_ret) case -ENOMEM: gp_ret->value = TEEC_ERROR_OUT_OF_MEMORY; break; + case -EHOSTUNREACH: + /* Tee crashed */ + gp_ret->value = TEEC_ERROR_TARGET_DEAD; + break; case -ENXIO: /* Session not found or not running */ gp_ret->value = TEEC_ERROR_BAD_STATE; @@ -234,12 +358,17 @@ int iwp_set_ret(int ret, struct gp_return *gp_ret) return -ECHILD; } -int iwp_register_shared_mem(struct mcp_buffer_map *map, +int iwp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, struct gp_return *gp_ret) { - int ret = 0; + int ret; - ret = mcp_map(SID_MEMORY_REFERENCE, map); +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_register_shared_mem(mmu, sva, gp_ret); +#endif + + ret = mcp_map(SID_MEMORY_REFERENCE, mmu, sva); /* iwp_set_ret would override the origin if called after */ ret = iwp_set_ret(ret, gp_ret); if (ret) @@ -250,6 +379,11 @@ int iwp_register_shared_mem(struct mcp_buffer_map *map, int iwp_release_shared_mem(struct mcp_buffer_map *map) { +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_release_shared_mem(map); +#endif + return mcp_unmap(SID_MEMORY_REFERENCE, map); } @@ -281,9 +415,19 @@ static int iwp_operation_to_iws(struct gp_operation *operation, case TEEC_MEMREF_TEMP_OUTPUT: case TEEC_MEMREF_TEMP_INOUT: if (operation->params[i].tmpref.buffer) { + struct gp_temp_memref *tmpref; + + tmpref = &operation->params[i].tmpref; /* Prepare buffer to map */ - bufs[i].va = operation->params[i].tmpref.buffer; - bufs[i].len = operation->params[i].tmpref.size; + bufs[i].va = tmpref->buffer; + if (tmpref->size > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, + "buffer size %llu too big", + tmpref->size); + return -EINVAL; + } + + bufs[i].len = tmpref->size; if (param_type == TEEC_MEMREF_TEMP_INPUT) bufs[i].flags = MC_IO_MAP_INPUT; else if (param_type == TEEC_MEMREF_TEMP_OUTPUT) @@ -329,7 +473,7 @@ static int iwp_operation_to_iws(struct gp_operation *operation, static inline void iwp_iws_set_tmpref(struct interworld_session *iws, int i, const struct mcp_buffer_map *map) { - iws->params[i].tmpref.physical_address = map->phys_addr; + iws->params[i].tmpref.physical_address = map->addr; iws->params[i].tmpref.size = map->length; iws->params[i].tmpref.offset = map->offset; iws->params[i].tmpref.wsm_type = map->type; @@ -349,7 +493,7 @@ static inline void iwp_iws_set_refs(struct interworld_session *iws, for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) if (maps[i].sva) iwp_iws_set_memref(iws, i, maps[i].sva); - else if (maps[i].map.phys_addr) + else if (maps[i].map.addr) iwp_iws_set_tmpref(iws, i, &maps[i].map); } @@ -410,34 +554,130 @@ static inline void mcuuid_to_tee_uuid(const struct mc_uuid_t *in, memcpy(out->clock_seq_and_node, in->value + 8, 8); } +static const char *origin_to_string(u32 origin) +{ + switch (origin) { + case TEEC_ORIGIN_API: + return "API"; + case TEEC_ORIGIN_COMMS: + return "COMMS"; + case TEEC_ORIGIN_TEE: + return "TEE"; + case TEEC_ORIGIN_TRUSTED_APP: + return "TRUSTED_APP"; + } + return "UNKNOWN"; +} + +static const char *value_to_string(u32 value) +{ + switch (value) { + case TEEC_SUCCESS: + return "SUCCESS"; + case TEEC_ERROR_GENERIC: + return "GENERIC"; + case TEEC_ERROR_ACCESS_DENIED: + return "ACCESS_DENIED"; + case TEEC_ERROR_CANCEL: + return "CANCEL"; + case TEEC_ERROR_ACCESS_CONFLICT: + return "ACCESS_CONFLICT"; + case TEEC_ERROR_EXCESS_DATA: + return "EXCESS_DATA"; + case TEEC_ERROR_BAD_FORMAT: + return "BAD_FORMAT"; + case TEEC_ERROR_BAD_PARAMETERS: + return "BAD_PARAMETERS"; + case TEEC_ERROR_BAD_STATE: + return "BAD_STATE"; + case TEEC_ERROR_ITEM_NOT_FOUND: + return "ITEM_NOT_FOUND"; + case TEEC_ERROR_NOT_IMPLEMENTED: + return "NOT_IMPLEMENTED"; + case TEEC_ERROR_NOT_SUPPORTED: + return "NOT_SUPPORTED"; + case TEEC_ERROR_NO_DATA: + return "NO_DATA"; + case TEEC_ERROR_OUT_OF_MEMORY: + return "OUT_OF_MEMORY"; + case TEEC_ERROR_BUSY: + return "BUSY"; + case TEEC_ERROR_COMMUNICATION: + return "COMMUNICATION"; + case TEEC_ERROR_SECURITY: + return "SECURITY"; + case TEEC_ERROR_SHORT_BUFFER: + return "SHORT_BUFFER"; + case TEEC_ERROR_TARGET_DEAD: + return "TARGET_DEAD"; + case TEEC_ERROR_STORAGE_NO_SPACE: + return "STORAGE_NO_SPACE"; + } + return NULL; +} + +static const char *cmd_to_string(u32 id) +{ + switch (id) { + case SID_OPEN_SESSION: + return "open session"; + case SID_INVOKE_COMMAND: + return "invoke command"; + case SID_CLOSE_SESSION: + return "close session"; + case SID_CANCEL_OPERATION: + return "cancel operation"; + case SID_MEMORY_REFERENCE: + return "memory reference"; + case SID_OPEN_TA: + return "open TA"; + case SID_REQ_TA: + return "request TA"; + } + return "unknown"; +} + +static const char *state_to_string(enum iwp_session_state state) +{ + switch (state) { + case IWP_SESSION_RUNNING: + return "running"; + case IWP_SESSION_CLOSE_REQUESTED: + return "close requested"; + case IWP_SESSION_CLOSED: + return "closed"; + } + return "error"; +} + int iwp_open_session_prepare( struct iwp_session *iwp_session, - const struct tee_object *obj, struct gp_operation *operation, struct mc_ioctl_buffer *bufs, struct gp_shared_memory **parents, struct gp_return *gp_ret) { struct interworld_session *iws; - union mclf_header *header; - u32 slot, temp_slot; - int ret; + u64 slot, op_slot; + int ret = 0; /* Get session final slot */ slot = iws_slot_get(); - mc_dev_devel("slot [%08x]", slot); + mc_dev_devel("slot [%08llx]", slot); if (slot == INVALID_IWS_SLOT) { - mc_dev_err("can't get slot"); - return iwp_set_ret(-ENOMEM, gp_ret); + ret = -ENOMEM; + mc_dev_err(ret, "can't get slot"); + return iwp_set_ret(ret, gp_ret); } /* Get session temporary slot */ - temp_slot = iws_slot_get(); - mc_dev_devel("temp_slot [%08x]", temp_slot); - if (temp_slot == INVALID_IWS_SLOT) { - mc_dev_err("can't get temp_slot"); + op_slot = iws_slot_get(); + mc_dev_devel("op_slot [%08llx]", op_slot); + if (op_slot == INVALID_IWS_SLOT) { + ret = -ENOMEM; + mc_dev_err(ret, "can't get op_slot"); iws_slot_put(slot); - return iwp_set_ret(-ENOMEM, gp_ret); + return iwp_set_ret(ret, gp_ret); } mutex_lock(&iwp_session->iws_lock); @@ -446,30 +686,25 @@ int iwp_open_session_prepare( iwp_session->slot = slot; iws = slot_to_iws(slot); memset(iws, 0, sizeof(*iws)); - iws->command_id = temp_slot; /* Prepare temporary session */ - iws = slot_to_iws(temp_slot); + iwp_session->op_slot = op_slot; + iws = slot_to_iws(op_slot); memset(iws, 0, sizeof(*iws)); - header = (union mclf_header *)(obj->data + obj->header_length); - mcuuid_to_tee_uuid(&header->mclf_header_v2.uuid, &iws->target_uuid); - iws->login = iwp_session->client_identity.login_type; - mc_dev_devel("iws->login [%08x]", iws->login); - memcpy(&iws->client_uuid, iwp_session->client_identity.login_data, - sizeof(iws->client_uuid)); - ret = iwp_operation_to_iws(operation, iws, bufs, parents); - if (ret) - iwp_open_session_abort(iwp_session); + + if (operation) { + ret = iwp_operation_to_iws(operation, iws, bufs, parents); + if (ret) + iwp_open_session_abort(iwp_session); + } return iwp_set_ret(ret, gp_ret); } void iwp_open_session_abort(struct iwp_session *iwp_session) { - struct interworld_session *iws = slot_to_iws(iwp_session->slot); - - iws_slot_put(iws->command_id); iws_slot_put(iwp_session->slot); + iws_slot_put(iwp_session->op_slot); mutex_unlock(&iwp_session->iws_lock); } @@ -478,30 +713,92 @@ void iwp_open_session_abort(struct iwp_session *iwp_session) */ int iwp_open_session( struct iwp_session *iwp_session, + const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct mcp_buffer_map *ta_map, const struct iwp_buffer_map *maps, + struct interworld_session *iws_in, + struct tee_mmu **mmus, struct gp_return *gp_ret) { struct interworld_session *iws = slot_to_iws(iwp_session->slot); - /* command_id is zero'd by the SWd */ - u32 temp_slot = iws->command_id; - struct interworld_session *temp_iws = slot_to_iws(temp_slot); + struct interworld_session *op_iws = slot_to_iws(iwp_session->op_slot); + struct tee_object *obj = NULL; + struct tee_mmu *obj_mmu = NULL; + struct mcp_buffer_map obj_map; int ret; - /* Put ingoing operation in temporary IWS */ - iwp_iws_set_refs(temp_iws, maps); + /* Operation is NULL when called from Xen BE */ + if (operation) { + /* Login info */ + op_iws->login = iwp_session->client_identity.login_type; + mc_dev_devel("iws->login [%08x]", op_iws->login); + memcpy(&op_iws->client_uuid, + iwp_session->client_identity.login_data, + sizeof(op_iws->client_uuid)); + + /* Put ingoing operation in temporary IWS */ + iwp_iws_set_refs(op_iws, maps); + } else { + struct mcp_buffer_map map; + int i; + + *op_iws = *iws_in; + + /* Insert correct mapping in operation */ + for (i = 0; i < 4; i++) { + if (!mmus[i]) + continue; + + tee_mmu_buffer(mmus[i], &map); + iwp_iws_set_tmpref(op_iws, i, &map); + } + } + + /* For the SWd to find the TA slot from the main one */ + iws->command_id = (u32)iwp_session->op_slot; /* TA blob handling */ - iws->param_types = TEEC_MEMREF_TEMP_INPUT; - iws->session_handle = 0; - iwp_iws_set_tmpref(iws, 0, ta_map); + if (!is_xen_domu()) { + union mclf_header *header; - mc_dev_devel("wsm_type [%04x], offset [%04x]", ta_map->type, - ta_map->offset); - mc_dev_devel("size [%08x], physical_address [%08x %08x]", - ta_map->length, (u32)(ta_map->phys_addr >> 32), - (u32)(ta_map->phys_addr & 0xFFFFFFFF)); + obj = tee_object_get(uuid, true); + if (IS_ERR(obj)) { + /* Tell SWd to load TA from SFS as not in registry */ + if (PTR_ERR(obj) == -ENOENT) + obj = tee_object_select(uuid); + + if (IS_ERR(obj)) + return PTR_ERR(obj); + } + + /* Convert UUID */ + header = (union mclf_header *)(&obj->data[obj->header_length]); + mcuuid_to_tee_uuid(&header->mclf_header_v2.uuid, + &op_iws->target_uuid); + + /* Create mapping for blob (alloc'd by driver => task = NULL) */ + { + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)obj->data, + .len = obj->length, + .flags = MC_IO_MAP_INPUT, + }; + + obj_mmu = tee_mmu_create(NULL, &buf); + if (IS_ERR(obj_mmu)) { + ret = PTR_ERR(obj_mmu); + goto err_mmu; + } + + iws->param_types = TEEC_MEMREF_TEMP_INPUT; + tee_mmu_buffer(obj_mmu, &obj_map); + iwp_iws_set_tmpref(iws, 0, &obj_map); + mc_dev_devel("wsm_type [%04x], offset [%04x]", + obj_map.type, obj_map.offset); + mc_dev_devel("size [%08x], physical_address [%08llx]", + obj_map.length, obj_map.addr); + } + } /* Add to local list of sessions so we can receive the notification */ mutex_lock(&l_ctx.sessions_lock); @@ -509,9 +806,17 @@ int iwp_open_session( mutex_unlock(&l_ctx.sessions_lock); /* Send IWP open command */ - ret = iwp_cmd(iwp_session, SID_OPEN_TA); +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + ret = xen_gp_open_session(iwp_session, uuid, maps, iws, op_iws, + gp_ret); + else +#endif + ret = iwp_cmd(iwp_session, SID_OPEN_TA, &op_iws->target_uuid, + true); + /* Temporary slot is not needed any more */ - iws_slot_put(temp_slot); + iws_slot_put(iwp_session->op_slot); /* Treat remote errors as errors, just use a specific errno */ if (!ret && iws->status != TEEC_SUCCESS) { gp_ret->origin = iws->return_origin; @@ -523,17 +828,33 @@ int iwp_open_session( /* set unique identifier for list search */ iwp_session->sid = iws->session_handle; /* Get outgoing operation from main IWS */ - iwp_iws_to_operation(iws, operation); + if (operation) + iwp_iws_to_operation(iws, operation); + else + *iws_in = *iws; + } else { /* Remove from list of sessions */ mutex_lock(&l_ctx.sessions_lock); list_del(&iwp_session->list); mutex_unlock(&l_ctx.sessions_lock); iws_slot_put(iwp_session->slot); - mc_dev_devel("failed with ret [%08x]", ret); + mc_dev_devel("failed: %s from %s, ret %d", + value_to_string(gp_ret->value), + origin_to_string(gp_ret->origin), ret); } mutex_unlock(&iwp_session->iws_lock); + + /* Blob not needed as re-mapped by the SWd */ + if (obj_mmu) + tee_mmu_put(obj_mmu); + +err_mmu: + /* Delete secure object */ + if (obj) + tee_object_free(obj); + return iwp_set_ret(ret, gp_ret); } @@ -559,19 +880,24 @@ static void iwp_session_release( int iwp_close_session( struct iwp_session *iwp_session) { - int ret; + int ret = 0; - /* state is either IWP_SESSION_RUNNING or IWP_SESSION_CLOSING_GP */ - mutex_lock(&iwp_session->iws_lock); - if (iwp_session->state == IWP_SESSION_RUNNING) + if (is_xen_domu()) { +#ifdef TRUSTONIC_XEN_DOMU + ret = xen_gp_close_session(iwp_session); +#endif + } else { + mutex_lock(&iwp_session->iws_lock); iwp_session->state = IWP_SESSION_CLOSE_REQUESTED; - /* Send IWP open command */ - ret = iwp_cmd(iwp_session, SID_CLOSE_SESSION); - mutex_unlock(&iwp_session->iws_lock); + /* Send IWP open command */ + ret = iwp_cmd(iwp_session, SID_CLOSE_SESSION, NULL, false); + mutex_unlock(&iwp_session->iws_lock); + } + iwp_session_release(iwp_session); - mc_dev_devel("close session %x ret %d state %d", iwp_session->sid, - ret, iwp_session->state); + mc_dev_devel("close session %x ret %d state %s", iwp_session->sid, + ret, state_to_string(iwp_session->state)); return ret; } @@ -584,7 +910,7 @@ int iwp_invoke_command_prepare( struct gp_return *gp_ret) { struct interworld_session *iws; - int ret; + int ret = 0; if (iwp_session->state != IWP_SESSION_RUNNING) return iwp_set_ret(-EBADFD, gp_ret); @@ -593,10 +919,12 @@ int iwp_invoke_command_prepare( iws = slot_to_iws(iwp_session->slot); memset(iws, 0, sizeof(*iws)); iws->session_handle = iwp_session->sid; - iws->command_id = command_id; - ret = iwp_operation_to_iws(operation, iws, bufs, parents); - if (ret) - iwp_invoke_command_abort(iwp_session); + if (operation) { + iws->command_id = command_id; + ret = iwp_operation_to_iws(operation, iws, bufs, parents); + if (ret) + iwp_invoke_command_abort(iwp_session); + } return iwp_set_ret(ret, gp_ret); } @@ -611,19 +939,49 @@ int iwp_invoke_command( struct iwp_session *iwp_session, struct gp_operation *operation, const struct iwp_buffer_map *maps, + struct interworld_session *iws_in, + struct tee_mmu **mmus, struct gp_return *gp_ret) { struct interworld_session *iws = slot_to_iws(iwp_session->slot); int ret = 0; - /* Update IWS with operation maps */ - iwp_iws_set_refs(iws, maps); - ret = iwp_cmd(iwp_session, SID_INVOKE_COMMAND); + /* Operation is NULL when called from Xen BE */ + if (operation) { + /* Update IWS with operation maps */ + iwp_iws_set_refs(iws, maps); + } else { + struct mcp_buffer_map map; + int i; + + *iws = *iws_in; + + /* Insert correct mapping in operation */ + for (i = 0; i < 4; i++) { + if (!mmus[i]) + continue; + + tee_mmu_buffer(mmus[i], &map); + iwp_iws_set_tmpref(iws, i, &map); + } + } + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + ret = xen_gp_invoke_command(iwp_session, maps, iws, gp_ret); + else +#endif + ret = iwp_cmd(iwp_session, SID_INVOKE_COMMAND, NULL, true); + /* Treat remote errors as errors, just use a specific errno */ if (!ret && iws->status != TEEC_SUCCESS) ret = -ECHILD; - iwp_iws_to_operation(iws, operation); + if (operation) + iwp_iws_to_operation(iws, operation); + else + *iws_in = *iws; + if (ret && (ret != -ECHILD)) { ret = iwp_set_ret(ret, gp_ret); mc_dev_devel("failed with ret [%08x]", ret); @@ -631,17 +989,24 @@ int iwp_invoke_command( gp_ret->origin = iws->return_origin; gp_ret->value = iws->status; } + mutex_unlock(&iwp_session->iws_lock); return ret; } int iwp_request_cancellation( - u32 slot) + u64 slot) { /* Pseudo IWP session for cancellation */ struct iwp_session iwp_session; int ret; +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_request_cancellation( + (uintptr_t)slot_to_iws(slot)); +#endif + iwp_session_init(&iwp_session, NULL); /* sid is local. Set is to SID_CANCEL_OPERATION to make things clear */ iwp_session.sid = SID_CANCEL_OPERATION; @@ -649,19 +1014,163 @@ int iwp_request_cancellation( mutex_lock(&l_ctx.sessions_lock); list_add_tail(&iwp_session.list, &l_ctx.sessions); mutex_unlock(&l_ctx.sessions_lock); - ret = iwp_cmd(&iwp_session, SID_CANCEL_OPERATION); + ret = iwp_cmd(&iwp_session, SID_CANCEL_OPERATION, NULL, false); mutex_lock(&l_ctx.sessions_lock); list_del(&iwp_session.list); mutex_unlock(&l_ctx.sessions_lock); return ret; } +static int debug_sessions(struct kasnprintf_buf *buf) +{ + struct iwp_session *session; + int ret; + + /* Header */ + ret = kasnprintf(buf, "%20s %4s %-15s %-11s %7s\n", + "CPU clock", "ID", "state", "notif state", "slot"); + if (ret < 0) + return ret; + + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(session, &l_ctx.sessions, list) { + const char *state_str; + u64 cpu_clk; + + state_str = nq_session_state(&session->nq_session, &cpu_clk); + ret = kasnprintf(buf, "%20llu %4x %-15s %-11s %7llu\n", cpu_clk, + session->sid == SID_INVALID ? 0 : session->sid, + state_to_string(session->state), state_str, + session->slot); + if (ret < 0) + break; + } + mutex_unlock(&l_ctx.sessions_lock); + return ret; +} + +static ssize_t debug_sessions_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + debug_sessions); +} + +static const struct file_operations debug_sessions_ops = { + .read = debug_sessions_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline int show_log_entry(struct kasnprintf_buf *buf, + struct command_info *cmd_info) +{ + const char *state_str = "unknown"; + const char *value_str = value_to_string(cmd_info->result.value); + char value[16]; + + switch (cmd_info->state) { + case UNUSED: + state_str = "unused"; + break; + case PENDING: + state_str = "pending"; + break; + case SENT: + state_str = "sent"; + break; + case COMPLETE: + state_str = "complete"; + break; + case FAILED: + state_str = "failed"; + break; + } + + if (!value_str) { + snprintf(value, sizeof(value), "%08x", cmd_info->result.value); + value_str = value; + } + + return kasnprintf(buf, "%20llu %5d %-16s %5x %-8s %5d %-11s %-17s%s\n", + cmd_info->cpu_clk, cmd_info->pid, + cmd_to_string(cmd_info->id), cmd_info->session_id, + state_str, cmd_info->errno, + origin_to_string(cmd_info->result.origin), value_str, + cmd_info->uuid_str); +} + +static int debug_last_cmds(struct kasnprintf_buf *buf) +{ + struct command_info *cmd_info; + int i, ret = 0; + + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + ret = kasnprintf(buf, "%20s %5s %-16s %5s %-8s %5s %-11s %-17s%s\n", + "CPU clock", "PID", "command", "S-ID", + "state", "errno", "origin", "value", "UUID"); + if (ret < 0) + goto out; + + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + if (cmd_info->state != UNUSED) + /* Buffer has wrapped around, dump end (oldest records) */ + for (i = l_ctx.last_cmds_index; i < LAST_CMDS_SIZE; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + + /* Dump first records */ + cmd_info = &l_ctx.last_cmds[0]; + for (i = 0; i < l_ctx.last_cmds_index; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + +out: + mutex_unlock(&l_ctx.last_cmds_mutex); + return ret; +} + +static ssize_t debug_last_cmds_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, debug_last_cmds); +} + +static const struct file_operations debug_last_cmds_ops = { + .read = debug_last_cmds_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline void mark_iwp_dead(void) +{ + struct iwp_session *session; + + l_ctx.iwp_dead = true; + /* Signal all potential waiters that SWd is going away */ + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(session, &l_ctx.sessions, list) + complete(&session->completion); + mutex_unlock(&l_ctx.sessions_lock); +} + +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mark_iwp_dead(); + return 0; +} + int iwp_init(void) { int i; -#ifdef TA2TA_READY - u32 first_iws = INVALID_IWS_SLOT; -#endif /*TA2TA_READY*/ l_ctx.iws = nq_get_iwp_buffer(); INIT_LIST_HEAD(&l_ctx.free_iws); @@ -671,43 +1180,36 @@ int iwp_init(void) if (!l_ctx.iws_list_pool) return -ENOMEM; -#ifndef TA2TA_READY for (i = 0; i < MAX_IW_SESSION; i++) { - l_ctx.iws_list_pool[i].offset = + l_ctx.iws_list_pool[i].slot = i * sizeof(struct interworld_session); list_add(&l_ctx.iws_list_pool[i].list, &l_ctx.free_iws); } -#else - for (i = MAX_IW_SESSION - 1; i >= 0; i--) { - l_ctx.iws_list_pool[i].offset = - i * sizeof(struct interworld_session); - list_add(&l_ctx.iws_list_pool[i].list, &l_ctx.free_iws); - } -#endif /*TA2TA_READY*/ mutex_init(&l_ctx.iws_list_lock); INIT_LIST_HEAD(&l_ctx.sessions); mutex_init(&l_ctx.sessions_lock); - -#ifdef TA2TA_READY - /* allocate special session for request from SWd */ - first_iws = iws_slot_get(); - if (first_iws != 0) { - mc_dev_err("first_iws [%08x]", first_iws); - return -ERANGE; - } -#endif /*TA2TA_READY*/ - nq_register_notif_handler(iwp_notif_handler, true); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); + /* Debugfs */ + mutex_init(&l_ctx.last_cmds_mutex); return 0; } void iwp_exit(void) { + mark_iwp_dead(); + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); } int iwp_start(void) { + /* Create debugfs sessions and last commands entries */ + debugfs_create_file("iwp_sessions", 0400, g_ctx.debug_dir, NULL, + &debug_sessions_ops); + debugfs_create_file("last_iwp_commands", 0400, g_ctx.debug_dir, NULL, + &debug_last_cmds_ops); return 0; } diff --git a/drivers/gud/MobiCoreDriver/iwp.h b/drivers/gud/MobiCoreDriver/iwp.h index bb5d92ca76b6..52296af271ce 100644 --- a/drivers/gud/MobiCoreDriver/iwp.h +++ b/drivers/gud/MobiCoreDriver/iwp.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -17,38 +18,31 @@ #include "mci/mcloadformat.h" /* struct identity */ +#include "nq.h" #include "mcp.h" /* mcp_buffer_map FIXME move to nq? */ struct iwp_session { - /* Notification queue session (MUST BE FIRST) */ + /* Notification queue session */ struct nq_session nq_session; /* Session ID */ u32 sid; /* IWS slot */ - u32 slot; + u64 slot; + /* IWS other slot needed at open */ + u64 op_slot; /* Sessions list (protected by iwp sessions_lock) */ struct list_head list; /* Notification waiter lock */ struct mutex notif_wait_lock; /* Only one at a time */ /* Notification received */ struct completion completion; - /* Notification lock */ - struct mutex exit_code_lock; - /* Last notification */ - s32 exit_code; /* Interworld struct lock */ struct mutex iws_lock; /* Session state (protected by iwp sessions_lock) */ enum iwp_session_state { IWP_SESSION_RUNNING, - IWP_SESSION_CLOSE_FAILED, IWP_SESSION_CLOSE_REQUESTED, - IWP_SESSION_CLOSE_NOTIFIED, - IWP_SESSION_CLOSING, IWP_SESSION_CLOSED, - IWP_SESSION_DYING, - IWP_SESSION_PROXY_CLOSING, - IWP_SESSION_DESTROYED, } state; /* GP TAs have login information */ struct identity client_identity; @@ -69,7 +63,7 @@ static inline u32 iwp_session_id(struct iwp_session *session) return session->sid; } -static inline u32 iwp_session_slot(struct iwp_session *session) +static inline u64 iwp_session_slot(struct iwp_session *session) { return session->slot; } @@ -79,13 +73,13 @@ int iwp_set_ret(int ret, struct gp_return *gp_ret); /* Commands */ int iwp_register_shared_mem( - struct mcp_buffer_map *map, + struct tee_mmu *mmu, + u32 *sva, struct gp_return *gp_ret); int iwp_release_shared_mem( struct mcp_buffer_map *map); int iwp_open_session_prepare( struct iwp_session *session, - const struct tee_object *obj, struct gp_operation *operation, struct mc_ioctl_buffer *bufs, struct gp_shared_memory **parents, @@ -94,9 +88,11 @@ void iwp_open_session_abort( struct iwp_session *iwp_session); int iwp_open_session( struct iwp_session *iwp_session, + const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct mcp_buffer_map *ta_map, const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct tee_mmu **mmus, struct gp_return *gp_ret); int iwp_close_session( struct iwp_session *iwp_session); @@ -113,9 +109,11 @@ int iwp_invoke_command( struct iwp_session *iwp_session, struct gp_operation *operation, const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct tee_mmu **mmus, struct gp_return *gp_ret); int iwp_request_cancellation( - u32 slot); + u64 slot); /* Initialisation/cleanup */ int iwp_init(void); diff --git a/drivers/gud/MobiCoreDriver/logging.c b/drivers/gud/MobiCoreDriver/logging.c index 80c35fd3d0f8..2ef1d2e61db0 100644 --- a/drivers/gud/MobiCoreDriver/logging.c +++ b/drivers/gud/MobiCoreDriver/logging.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +13,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -20,9 +21,7 @@ #include #include "main.h" -#include "fastcall.h" #include "logging.h" -#include "platform.h" /* Supported log buffer version */ #define MC_LOG_VERSION 2 @@ -66,12 +65,13 @@ struct mc_trace_buf { }; static struct logging_ctx { - struct work_struct work; + struct kthread_work work; + struct kthread_worker worker; + struct task_struct *thread; union { struct mc_trace_buf *trace_buf; /* Circular log buffer */ unsigned long trace_page; }; - bool buffer_is_shared; /* Log buffer cannot be freed */ u32 tail; /* MobiCore log read position */ int thread_err; u16 prev_source; /* Previous Log source */ @@ -92,20 +92,11 @@ static inline void log_eol(u16 source, u32 cpuid) if (log_ctx.prev_source) /* TEE user-space */ -#ifdef TBASE_CORE_SWITCHER dev_info(g_ctx.mcd, "%03x(%u)|%s\n", log_ctx.prev_source, cpuid, log_ctx.line); -#else - dev_info(g_ctx.mcd, "%03x|%s\n", log_ctx.prev_source, - log_ctx.line); -#endif else /* TEE kernel */ -#ifdef TBASE_CORE_SWITCHER dev_info(g_ctx.mcd, "mtk(%u)|%s\n", cpuid, log_ctx.line); -#else - dev_info(g_ctx.mcd, "mtk|%s\n", log_ctx.line); -#endif log_ctx.line[0] = '\0'; log_ctx.line_len = 0; } @@ -180,14 +171,14 @@ static inline int log_msg(void *data) return sizeof(*msg); } -static void log_worker(struct work_struct *work) +static void logging_worker(struct kthread_work *work) { static DEFINE_MUTEX(local_mutex); mutex_lock(&local_mutex); while (log_ctx.trace_buf->head != log_ctx.tail) { if (log_ctx.trace_buf->version != MC_LOG_VERSION) { - mc_dev_err("Bad log data v%d (exp. v%d), stop", + mc_dev_err(-EINVAL, "Bad log data v%d (exp. v%d), stop", log_ctx.trace_buf->version, MC_LOG_VERSION); log_ctx.dead = true; break; @@ -207,43 +198,22 @@ static void log_worker(struct work_struct *work) * This should be called from the places where calls into MobiCore have * generated some logs(eg, yield, SIQ...) */ -void mc_logging_run(void) +void logging_run(void) { if (log_ctx.enabled && !log_ctx.dead && log_ctx.trace_buf->head != log_ctx.tail) - schedule_work(&log_ctx.work); -} - -int mc_logging_start(void) -{ - int ret = mc_fc_mem_trace(virt_to_phys((void *)(log_ctx.trace_page)), - BIT(LOG_BUF_ORDER) * PAGE_SIZE); - - if (ret) { - mc_dev_err("shared traces setup failed"); - return ret; - } - - log_ctx.buffer_is_shared = true; - mc_dev_devel("fc_log version %u", log_ctx.trace_buf->version); - mc_logging_run(); - return 0; -} - -void mc_logging_stop(void) -{ - if (!mc_fc_mem_trace(0, 0)) - log_ctx.buffer_is_shared = false; - - mc_logging_run(); - flush_work(&log_ctx.work); +#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE + queue_kthread_work(&log_ctx.worker, &log_ctx.work); +#else + kthread_queue_work(&log_ctx.worker, &log_ctx.work); +#endif } /* * Setup MobiCore kernel log. It assumes it's running on CORE 0! - * The fastcall will complain is that is not the case! + * The fastcall will complain if that is not the case! */ -int mc_logging_init(void) +int logging_init(phys_addr_t *buffer, u32 *size) { /* * We are going to map this buffer into virtual address space in SWd. @@ -254,21 +224,38 @@ int mc_logging_init(void) if (!log_ctx.trace_page) return -ENOMEM; - INIT_WORK(&log_ctx.work, log_worker); + *buffer = virt_to_phys((void *)(log_ctx.trace_page)); + *size = BIT(LOG_BUF_ORDER) * PAGE_SIZE; + + /* Logging thread */ +#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE + init_kthread_work(&log_ctx.work, logging_worker); + init_kthread_worker(&log_ctx.worker); +#else + kthread_init_work(&log_ctx.work, logging_worker); + kthread_init_worker(&log_ctx.worker); +#endif + log_ctx.thread = kthread_create(kthread_worker_fn, &log_ctx.worker, + "tee_log"); + if (IS_ERR(log_ctx.thread)) + return PTR_ERR(log_ctx.thread); + + wake_up_process(log_ctx.thread); + + /* Debugfs switch */ log_ctx.enabled = true; debugfs_create_bool("swd_debug", 0600, g_ctx.debug_dir, &log_ctx.enabled); return 0; } -void mc_logging_exit(void) +void logging_exit(bool buffer_busy) { /* - * This is not racey as the only caller for mc_logging_run is the + * This is not racey as the only caller for logging_run is the * scheduler which gets stopped before us, and long before we exit. */ - if (!log_ctx.buffer_is_shared) + kthread_stop(log_ctx.thread); + if (!buffer_busy) free_pages(log_ctx.trace_page, LOG_BUF_ORDER); - else - mc_dev_err("log buffer unregister not supported"); } diff --git a/drivers/gud/MobiCoreDriver/logging.h b/drivers/gud/MobiCoreDriver/logging.h index 5fdc03826992..3791e2e8abb0 100644 --- a/drivers/gud/MobiCoreDriver/logging.h +++ b/drivers/gud/MobiCoreDriver/logging.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -11,13 +12,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ + #ifndef _MC_LOGGING_H_ #define _MC_LOGGING_H_ -void mc_logging_run(void); -int mc_logging_init(void); -void mc_logging_exit(void); -int mc_logging_start(void); -void mc_logging_stop(void); +void logging_run(void); +int logging_init(phys_addr_t *buffer, u32 *size); +void logging_exit(bool buffer_busy); #endif /* _MC_LOGGING_H_ */ diff --git a/drivers/gud/MobiCoreDriver/main.c b/drivers/gud/MobiCoreDriver/main.c index 72239d5ac926..ee725bd1aa5c 100644 --- a/drivers/gud/MobiCoreDriver/main.c +++ b/drivers/gud/MobiCoreDriver/main.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -17,29 +18,31 @@ #include #include #include +#include #include #include #include "public/mc_user.h" -#include "public/mc_admin.h" /* MC_ADMIN_DEVNODE */ +#include "public/mc_admin.h" /* MC_ADMIN_DEVNODE */ -#include "platform.h" /* MC_PM_RUNTIME */ +#include "platform.h" /* MC_PM_RUNTIME */ #include "main.h" -#include "fastcall.h" #include "arm.h" -#include "mmu.h" -#include "scheduler.h" -#include "pm.h" -#include "logging.h" #include "admin.h" #include "user.h" -#include "mcp.h" #include "iwp.h" +#include "mcp.h" #include "nq.h" #include "client.h" - +#include "xen_be.h" +#include "xen_fe.h" #include "build_tag.h" +/* Default entry for our driver in device tree */ +#ifndef MC_DEVICE_PROPNAME +#define MC_DEVICE_PROPNAME "arm,mcd" +#endif + /* Define a MobiCore device structure for use with dev_debug() etc */ static struct device_driver driver = { .name = "Trustonic" @@ -54,7 +57,9 @@ struct mc_device_ctx g_ctx = { }; static struct { - /* TEE start return code */ + /* Device tree compatibility */ + bool use_platform_driver; + /* TEE start return code mutex */ struct mutex start_mutex; /* TEE start return code */ int start_ret; @@ -179,7 +184,7 @@ static ssize_t debug_structs_read(struct file *file, char __user *user_buf, clients_debug_structs); } -static const struct file_operations mc_debug_structs_ops = { +static const struct file_operations debug_structs_ops = { .read = debug_structs_read, .llseek = default_llseek, .open = debug_generic_open, @@ -203,7 +208,9 @@ static ssize_t debug_struct_counters_read(struct file *file, "swsms: %d\n" "mmus: %d\n" "maps: %d\n" - "slots: %d\n", + "slots: %d\n" + "xen maps: %d\n" + "xen fes: %d\n", atomic_read(&g_ctx.c_clients), atomic_read(&g_ctx.c_cbufs), atomic_read(&g_ctx.c_cwsms), @@ -211,7 +218,9 @@ static ssize_t debug_struct_counters_read(struct file *file, atomic_read(&g_ctx.c_wsms), atomic_read(&g_ctx.c_mmus), atomic_read(&g_ctx.c_maps), - atomic_read(&g_ctx.c_slots)); + atomic_read(&g_ctx.c_slots), + atomic_read(&g_ctx.c_xen_maps), + atomic_read(&g_ctx.c_xen_fes)); mutex_unlock(&main_ctx.struct_counters_buf_mutex); if (ret > 0) main_ctx.struct_counters_buf_len = ret; @@ -222,7 +231,7 @@ static ssize_t debug_struct_counters_read(struct file *file, main_ctx.struct_counters_buf_len); } -static const struct file_operations mc_debug_struct_counters_ops = { +static const struct file_operations debug_struct_counters_ops = { .read = debug_struct_counters_read, .llseek = default_llseek, }; @@ -237,7 +246,7 @@ static inline int device_user_init(void) mc_user_init(&main_ctx.user_cdev); ret = cdev_add(&main_ctx.user_cdev, main_ctx.user_dev, 1); if (ret) { - mc_dev_err("user cdev_add failed"); + mc_dev_err(ret, "user cdev_add failed"); return ret; } @@ -245,14 +254,15 @@ static inline int device_user_init(void) dev = device_create(main_ctx.class, NULL, main_ctx.user_dev, NULL, MC_USER_DEVNODE); if (IS_ERR(dev)) { + ret = PTR_ERR(dev); cdev_del(&main_ctx.user_cdev); - mc_dev_err("user device_create failed"); - return PTR_ERR(dev); + mc_dev_err(ret, "user device_create failed"); + return ret; } /* Create debugfs structs entry */ debugfs_create_file("structs", 0400, g_ctx.debug_dir, NULL, - &mc_debug_structs_ops); + &debug_structs_ops); return 0; } @@ -285,87 +295,40 @@ static int suspend_notifier(struct notifier_block *nb, unsigned long event, main_ctx.did_hibernate = false; switch (event) { case PM_SUSPEND_PREPARE: - return mc_scheduler_suspend(); + return nq_suspend(); case PM_POST_SUSPEND: - return mc_scheduler_resume(); + return nq_resume(); +#ifdef TRUSTONIC_HIBERNATION_SUPPORT case PM_HIBERNATION_PREPARE: /* Try to stop the TEE nicely (ignore failure) */ - mc_scheduler_suspend(); - /* Make sure the TEE cannot run anymore */ - mc_scheduler_stop(); - /* Flush log buffer */ - mc_logging_run(); + nq_stop(); break; case PM_POST_HIBERNATION: if (main_ctx.did_hibernate) { /* Really did hibernate */ - clients_kill_sessions(); + client_cleanup(); main_ctx.start_ret = TEE_START_NOT_TRIGGERED; return mobicore_start(); } /* Did not hibernate, just restart the TEE */ - ret = mc_scheduler_start(); - if (!ret) - ret = mc_scheduler_resume(); + ret = nq_start(); +#endif } return ret; } #endif /* MC_PM_RUNTIME */ -static int mobicore_start(void) +static inline int check_version(void) { struct mc_version_info version_info; -#ifdef CONFIG_TRUSTONIC_TEE_LPAE - bool dynamic_lpae = false; -#endif int ret; - mutex_lock(&main_ctx.start_mutex); - if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED) - goto got_ret; - - ret = mc_logging_start(); - if (ret) { - mc_dev_err("Log start failed"); - goto err_log; - } - - ret = nq_start(); - if (ret) { - mc_dev_err("NQ start failed"); - goto err_nq; - } - - ret = mcp_start(); - if (ret) { - mc_dev_err("MCP start failed"); - goto err_mcp; - } - - ret = iwp_start(); - if (ret) { - mc_dev_err("IWP start failed"); - goto err_iwp; - } - - ret = mc_scheduler_start(); - if (ret) { - mc_dev_err("Scheduler start failed"); - goto err_sched; - } - - ret = mc_pm_start(); - if (ret) { - mc_dev_err("Power Management start failed"); - goto err_pm; - } - /* Must be called before creating the user device node to avoid race */ ret = mcp_get_version(&version_info); if (ret) - goto err_mcp_cmd; + return ret; /* CMP version is meaningless in this case and is thus not printed */ mc_dev_info("\n" @@ -388,62 +351,69 @@ static int mobicore_start(void) version_info.version_dr_api, version_info.version_nwd); - if (MC_VERSION_MAJOR(version_info.version_mci) > 1) { - mc_dev_err("MCI too recent for this driver"); - goto err_version; - } - - if ((MC_VERSION_MAJOR(version_info.version_mci) == 0) && - (MC_VERSION_MINOR(version_info.version_mci) < 6)) { - mc_dev_err("MCI too old for this driver"); - goto err_version; - } - /* Determine which features are supported */ - switch (version_info.version_mci) { - case MC_VERSION(1, 6): /* 400 */ - g_ctx.f_monotonic_time = true; - /* Fall through */ - case MC_VERSION(1, 5): /* 400 */ - g_ctx.f_iwp = true; - /* Fall through */ - case MC_VERSION(1, 4): /* 310 */ -#ifdef CONFIG_TRUSTONIC_TEE_LPAE - dynamic_lpae = true; -#endif - /* Fall through */ - case MC_VERSION(1, 3): - g_ctx.f_time = true; - /* Fall through */ - case MC_VERSION(1, 2): - g_ctx.f_client_login = true; - /* Fall through */ - case MC_VERSION(1, 1): - g_ctx.f_multimap = true; - /* Fall through */ - case MC_VERSION(1, 0): /* 302 */ - g_ctx.f_mem_ext = true; - g_ctx.f_ta_auth = true; - /* Fall through */ - case MC_VERSION(0, 7): - g_ctx.f_timeout = true; - /* Fall through */ - case MC_VERSION(0, 6): /* 301 */ - break; + if (version_info.version_mci != MC_VERSION(1, 7)) { + ret = -EHOSTDOWN; + mc_dev_err(ret, "TEE incompatible with this driver"); + return ret; } -#ifdef CONFIG_TRUSTONIC_TEE_LPAE - if (!dynamic_lpae) - g_ctx.f_lpae = true; -#endif - mc_dev_info("SWd uses %sLPAE MMU table format", - g_ctx.f_lpae ? "" : "non-"); + return 0; +} + +static int mobicore_start_domu(void) +{ + mutex_lock(&main_ctx.start_mutex); + if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED) + goto end; + + /* Must be called before creating the user device node to avoid race */ + main_ctx.start_ret = check_version(); + if (main_ctx.start_ret) + goto end; + + main_ctx.start_ret = device_user_init(); +end: + mutex_unlock(&main_ctx.start_mutex); + return main_ctx.start_ret; +} + +static int mobicore_start(void) +{ + int ret; + + mutex_lock(&main_ctx.start_mutex); + if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED) + goto got_ret; + + ret = nq_start(); + if (ret) { + mc_dev_err(ret, "NQ start failed"); + goto err_nq; + } + + ret = mcp_start(); + if (ret) { + mc_dev_err(ret, "MCP start failed"); + goto err_mcp; + } + + ret = iwp_start(); + if (ret) { + mc_dev_err(ret, "IWP start failed"); + goto err_iwp; + } + + /* Must be called before creating the user device node to avoid race */ + ret = check_version(); + if (ret) + goto err_version; #ifdef MC_PM_RUNTIME main_ctx.reboot_notifier.notifier_call = reboot_notifier; ret = register_reboot_notifier(&main_ctx.reboot_notifier); if (ret) { - mc_dev_err("reboot notifier register failed"); + mc_dev_err(ret, "reboot notifier registration failed"); goto err_pm_notif; } @@ -451,38 +421,40 @@ static int mobicore_start(void) ret = register_pm_notifier(&main_ctx.pm_notifier); if (ret) { unregister_reboot_notifier(&main_ctx.reboot_notifier); - mc_dev_err("PM notifier register failed"); + mc_dev_err(ret, "PM notifier register failed"); goto err_pm_notif; } #endif + if (is_xen_dom0()) { + ret = xen_be_init(); + if (ret) + goto err_xen_be; + } + ret = device_user_init(); if (ret) - goto err_create_dev_user; + goto err_device_user; main_ctx.start_ret = 0; goto got_ret; -err_create_dev_user: +err_device_user: + if (is_xen_dom0()) + xen_be_exit(); +err_xen_be: #ifdef MC_PM_RUNTIME unregister_reboot_notifier(&main_ctx.reboot_notifier); unregister_pm_notifier(&main_ctx.pm_notifier); err_pm_notif: #endif err_version: -err_mcp_cmd: - mc_pm_stop(); -err_pm: - mc_scheduler_stop(); -err_sched: iwp_stop(); err_iwp: mcp_stop(); err_mcp: nq_stop(); err_nq: - mc_logging_stop(); -err_log: main_ctx.start_ret = ret; got_ret: mutex_unlock(&main_ctx.start_mutex); @@ -492,16 +464,18 @@ got_ret: static void mobicore_stop(void) { device_user_exit(); + if (is_xen_dom0()) + xen_be_exit(); + + if (!is_xen_domu()) { #ifdef MC_PM_RUNTIME - unregister_reboot_notifier(&main_ctx.reboot_notifier); - unregister_pm_notifier(&main_ctx.pm_notifier); + unregister_reboot_notifier(&main_ctx.reboot_notifier); + unregister_pm_notifier(&main_ctx.pm_notifier); #endif - mc_pm_stop(); - mc_scheduler_stop(); - mc_logging_stop(); - iwp_stop(); - mcp_stop(); - nq_stop(); + iwp_stop(); + mcp_stop(); + nq_stop(); + } } int mc_wait_tee_start(void) @@ -520,52 +494,38 @@ int mc_wait_tee_start(void) return ret; } -static ssize_t debug_sessions_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static inline int device_common_init(void) { - return debug_generic_read(file, user_buf, count, ppos, - mcp_debug_sessions); -} - -static const struct file_operations mc_debug_sessions_ops = { - .read = debug_sessions_read, - .llseek = default_llseek, - .open = debug_generic_open, - .release = debug_generic_release, -}; - -static ssize_t debug_mcpcmds_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - return debug_generic_read(file, user_buf, count, ppos, - mcp_debug_mcpcmds); -} - -static const struct file_operations mc_debug_mcpcmds_ops = { - .read = debug_mcpcmds_read, - .llseek = default_llseek, - .open = debug_generic_open, - .release = debug_generic_release, -}; - -static inline int device_admin_init(void) -{ - struct device *dev; - int ret = 0; + int ret; ret = alloc_chrdev_region(&main_ctx.device, 0, 2, "trustonic_tee"); if (ret) { - mc_dev_err("alloc_chrdev_region failed"); + mc_dev_err(ret, "alloc_chrdev_region failed"); return ret; } main_ctx.class = class_create(THIS_MODULE, "trustonic_tee"); if (IS_ERR(main_ctx.class)) { - mc_dev_err("class_create failed"); ret = PTR_ERR(main_ctx.class); - goto err_class; + mc_dev_err(ret, "class_create failed"); + unregister_chrdev_region(main_ctx.device, 2); + return ret; } + return 0; +} + +static inline void device_common_exit(void) +{ + class_destroy(main_ctx.class); + unregister_chrdev_region(main_ctx.device, 2); +} + +static inline int device_admin_init(void) +{ + struct device *dev; + int ret = 0; + /* Create the ADMIN node */ ret = mc_admin_init(&main_ctx.admin_cdev, mobicore_start, mobicore_stop); @@ -574,7 +534,7 @@ static inline int device_admin_init(void) ret = cdev_add(&main_ctx.admin_cdev, main_ctx.device, 1); if (ret) { - mc_dev_err("admin cdev_add failed"); + mc_dev_err(ret, "admin cdev_add failed"); goto err_cdev; } @@ -582,16 +542,11 @@ static inline int device_admin_init(void) dev = device_create(main_ctx.class, NULL, main_ctx.device, NULL, MC_ADMIN_DEVNODE); if (IS_ERR(dev)) { - mc_dev_err("admin device_create failed"); ret = PTR_ERR(dev); + mc_dev_err(ret, "admin device_create failed"); goto err_device; } - /* Create debugfs sessions and MCP commands entries */ - debugfs_create_file("sessions", 0400, g_ctx.debug_dir, NULL, - &mc_debug_sessions_ops); - debugfs_create_file("last_mcp_commands", 0400, g_ctx.debug_dir, NULL, - &mc_debug_mcpcmds_ops); return 0; err_device: @@ -599,9 +554,6 @@ err_device: err_cdev: mc_admin_exit(); err_init: - class_destroy(main_ctx.class); -err_class: - unregister_chrdev_region(main_ctx.device, 2); return ret; } @@ -610,8 +562,6 @@ static inline void device_admin_exit(void) device_destroy(main_ctx.class, main_ctx.device); cdev_del(&main_ctx.admin_cdev); mc_admin_exit(); - class_destroy(main_ctx.class); - unregister_chrdev_region(main_ctx.device, 2); } /* @@ -621,7 +571,7 @@ static inline void device_admin_exit(void) */ static int mobicore_probe(struct platform_device *pdev) { - int err = 0; + int ret = 0; if (pdev) g_ctx.mcd->of_node = pdev->dev.of_node; @@ -630,15 +580,17 @@ static int mobicore_probe(struct platform_device *pdev) mc_dev_info("MobiCore %s", MOBICORE_COMPONENT_BUILD_TAG); #endif /* Hardware does not support ARM TrustZone -> Cannot continue! */ - if (!has_security_extensions()) { - mc_dev_err("Hardware doesn't support ARM TrustZone!"); - return -ENODEV; + if (!is_xen_domu() && !has_security_extensions()) { + ret = -ENODEV; + mc_dev_err(ret, "Hardware doesn't support ARM TrustZone!"); + return ret; } /* Running in secure mode -> Cannot load the driver! */ if (is_secure_mode()) { - mc_dev_err("Running in secure MODE!"); - return -ENODEV; + ret = -ENODEV; + mc_dev_err(ret, "Running in secure MODE!"); + return ret; } /* Make sure we can create debugfs entries */ @@ -653,89 +605,75 @@ static int mobicore_probe(struct platform_device *pdev) atomic_set(&g_ctx.c_mmus, 0); atomic_set(&g_ctx.c_maps, 0); atomic_set(&g_ctx.c_slots, 0); + atomic_set(&g_ctx.c_xen_maps, 0); + atomic_set(&g_ctx.c_xen_fes, 0); main_ctx.start_ret = TEE_START_NOT_TRIGGERED; mutex_init(&main_ctx.start_mutex); mutex_init(&main_ctx.struct_counters_buf_mutex); - /* Create debugfs info entry */ + /* Create debugfs info entries */ debugfs_create_file("structs_counters", 0400, g_ctx.debug_dir, NULL, - &mc_debug_struct_counters_ops); + &debug_struct_counters_ops); /* Initialize common API layer */ client_init(); /* Initialize plenty of nice features */ - err = mc_fastcall_init(); - if (err) { - mc_dev_err("Fastcall support init failed!"); - goto err_fastcall; - } - - err = nq_init(); - if (err) { - mc_dev_err("NQ init failed!"); + ret = nq_init(); + if (ret) { + mc_dev_err(ret, "NQ init failed"); goto fail_nq_init; } - err = mcp_init(); - if (err) { - mc_dev_err("MCP init failed!"); + ret = mcp_init(); + if (ret) { + mc_dev_err(ret, "MCP init failed"); goto err_mcp; } - err = iwp_init(); - if (err) { - mc_dev_err("IWP init failed!"); + ret = iwp_init(); + if (ret) { + mc_dev_err(ret, "IWP init failed"); goto err_iwp; } - err = mc_logging_init(); - if (err) { - mc_dev_err("Log init failed!"); - goto err_log; - } + ret = device_common_init(); + if (ret) + goto err_common; - err = mc_scheduler_init(); - if (err) { - mc_dev_err("Scheduler init failed!"); - goto err_sched; - } - - /* - * Create admin dev so that daemon can already communicate with - * the driver - */ - err = device_admin_init(); - if (err) - goto err_admin; + if (!is_xen_domu()) { + /* Admin dev is for the daemon to communicate with the driver */ + ret = device_admin_init(); + if (ret) + goto err_admin; #ifndef MC_DELAYED_TEE_START - err = mobicore_start(); + ret = mobicore_start(); #endif - if (err) - goto err_start; + if (ret) + goto err_start; + } return 0; err_start: device_admin_exit(); err_admin: - mc_scheduler_exit(); -err_sched: - mc_logging_exit(); -err_log: + device_common_exit(); +err_common: iwp_exit(); err_iwp: mcp_exit(); err_mcp: nq_exit(); fail_nq_init: - mc_fastcall_exit(); -err_fastcall: debugfs_remove_recursive(g_ctx.debug_dir); - return err; + return ret; } -#ifdef MC_DEVICE_PROPNAME +static int mobicore_probe_not_of(void) +{ + return mobicore_probe(NULL); +} static const struct of_device_id of_match_table[] = { { .compatible = MC_DEVICE_PROPNAME }, @@ -751,8 +689,6 @@ static struct platform_driver mc_plat_driver = { } }; -#endif /* MC_DEVICE_PROPNAME */ - static int __init mobicore_init(void) { dev_set_name(g_ctx.mcd, "TEE"); @@ -763,25 +699,34 @@ static int __init mobicore_init(void) mc_dev_info("MobiCore mcDrvModuleApi version is %d.%d", MCDRVMODULEAPI_VERSION_MAJOR, MCDRVMODULEAPI_VERSION_MINOR); -#ifdef MC_DEVICE_PROPNAME - return platform_driver_register(&mc_plat_driver); -#else - return mobicore_probe(NULL); -#endif + + /* In a Xen DomU, just register the front-end */ + if (is_xen_domu()) + return xen_fe_init(mobicore_probe_not_of, mobicore_start_domu); + + main_ctx.use_platform_driver = + of_find_compatible_node(NULL, NULL, MC_DEVICE_PROPNAME); + if (main_ctx.use_platform_driver) + return platform_driver_register(&mc_plat_driver); + + return mobicore_probe_not_of(); } static void __exit mobicore_exit(void) { -#ifdef MC_DEVICE_PROPNAME - platform_driver_unregister(&mc_plat_driver); -#endif - device_admin_exit(); - mc_scheduler_exit(); - mc_logging_exit(); + if (is_xen_domu()) + xen_fe_exit(); + + if (main_ctx.use_platform_driver) + platform_driver_unregister(&mc_plat_driver); + + if (!is_xen_domu()) + device_admin_exit(); + + device_common_exit(); iwp_exit(); mcp_exit(); nq_exit(); - mc_fastcall_exit(); debugfs_remove_recursive(g_ctx.debug_dir); } diff --git a/drivers/gud/MobiCoreDriver/main.h b/drivers/gud/MobiCoreDriver/main.h index 6717fc5743a9..8c8de978ffb7 100644 --- a/drivers/gud/MobiCoreDriver/main.h +++ b/drivers/gud/MobiCoreDriver/main.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -20,14 +21,16 @@ #include /* struct inode and struct file */ #include #include +#include #define MC_VERSION(major, minor) \ ((((major) & 0x0000ffff) << 16) | ((minor) & 0x0000ffff)) #define MC_VERSION_MAJOR(x) ((x) >> 16) #define MC_VERSION_MINOR(x) ((x) & 0xffff) -#define mc_dev_err(fmt, ...) \ - dev_err(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define mc_dev_err(__ret__, fmt, ...) \ + dev_err(g_ctx.mcd, "ERROR %d %s: " fmt "\n", \ + __ret__, __func__, ##__VA_ARGS__) #define mc_dev_info(fmt, ...) \ dev_info(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__) @@ -49,26 +52,6 @@ struct mc_device_ctx { /* debugfs root */ struct dentry *debug_dir; - /* Features */ - /* - SWd uses LPAE MMU table format */ - bool f_lpae; - /* - SWd can set a time out to get scheduled at a future time */ - bool f_timeout; - /* - SWd supports memory extension which allows for bigger TAs */ - bool f_mem_ext; - /* - SWd supports TA authorisation */ - bool f_ta_auth; - /* - SWd can map several buffers at once */ - bool f_multimap; - /* - SWd supports GP client authentication */ - bool f_client_login; - /* - SWd needs time updates */ - bool f_time; - /* - SWd supports inter-world protocol */ - bool f_iwp; - /* - SWd needs both wall and monotonic times */ - bool f_monotonic_time; - /* Debug counters */ atomic_t c_clients; atomic_t c_cbufs; @@ -78,6 +61,8 @@ struct mc_device_ctx { atomic_t c_mmus; atomic_t c_maps; atomic_t c_slots; + atomic_t c_xen_maps; + atomic_t c_xen_fes; }; extern struct mc_device_ctx g_ctx; @@ -109,4 +94,30 @@ static inline unsigned int kref_read(struct kref *kref) } #endif +/* Xen support */ + +#ifdef CONFIG_XEN +#if KERNEL_VERSION(4, 4, 0) <= LINUX_VERSION_CODE +#define TRUSTONIC_XEN_DOMU +#endif +#endif + +static inline bool is_xen_dom0(void) +{ +#if KERNEL_VERSION(3, 18, 0) <= LINUX_VERSION_CODE + return xen_domain() && xen_initial_domain(); +#else + return false; +#endif +} + +static inline bool is_xen_domu(void) +{ +#ifdef TRUSTONIC_XEN_DOMU + return xen_domain() && !xen_initial_domain(); +#else + return false; +#endif +} + #endif /* _MC_MAIN_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mci/gptci.h b/drivers/gud/MobiCoreDriver/mci/gptci.h index 5374d5f9ddea..a51edd4f98bb 100644 --- a/drivers/gud/MobiCoreDriver/mci/gptci.h +++ b/drivers/gud/MobiCoreDriver/mci/gptci.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/mci/mcifc.h b/drivers/gud/MobiCoreDriver/mci/mcifc.h index d3674bcde788..41fd8c582997 100644 --- a/drivers/gud/MobiCoreDriver/mci/mcifc.h +++ b/drivers/gud/MobiCoreDriver/mci/mcifc.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2014 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -26,23 +27,23 @@ #if (defined(CONFIG_ARM64) && !defined(MC_ARMV7_FC)) || (defined(MC_AARCH32_FC)) -#define FASTCALL_OWNER_TZOS_32 (0xBF000000) /**Trusted OS Fastcalls SMC32 */ - +#define FASTCALL_OWNER_TZOS (0x3F000000) +#define FASTCALL_ATOMIC_MASK BIT(31) +/**Trusted OS Fastcalls SMC32 */ +#define MC_FC_STD32_BASE \ + ((u32)(FASTCALL_OWNER_TZOS | FASTCALL_ATOMIC_MASK)) /* SMC32 Trusted OS owned Fastcalls */ -#define MC_FC_STD32_BASE ((u32)FASTCALL_OWNER_TZOS_32) #define MC_FC_STD32(x) ((u32)(MC_FC_STD32_BASE + (x))) #define MC_FC_INIT MC_FC_STD32(1) /**< Initializing FastCall. */ #define MC_FC_INFO MC_FC_STD32(2) /**< Info FastCall. */ #define MC_FC_MEM_TRACE MC_FC_STD32(10) /**< Enable SWd tracing via memory */ -#define MC_FC_SWAP_CPU MC_FC_STD32(54) /**< Change new active Core */ #else #define MC_FC_INIT ((u32)(-1)) /**< Initializing FastCall. */ #define MC_FC_INFO ((u32)(-2)) /**< Info FastCall. */ #define MC_FC_MEM_TRACE ((u32)(-31)) /**< Enable SWd tracing via memory */ -#define MC_FC_SWAP_CPU ((u32)(0x84000005)) /**< Change new active Core */ #endif @@ -127,6 +128,10 @@ #define MC_EXT_INFO_ID_MC_EXC_UUID1 24 #define MC_EXT_INFO_ID_MC_EXC_UUID2 25 #define MC_EXT_INFO_ID_MC_EXC_UUID3 26 +/**< MobiCore exception handler last crashing task offset */ +#define MC_EXT_INFO_ID_TASK_OFFSET 27 +/**< MobiCore exception handler last crashing task's mclib offset */ +#define MC_EXT_INFO_ID_MCLIB_OFFSET 28 /** @} */ @@ -139,6 +144,8 @@ #define MC_FC_RET_ERR_INVALID 1 /**< MobiCore has already been initialized. */ #define MC_FC_RET_ERR_ALREADY_INITIALIZED 5 +/**< Call is not allowed. */ +#define TEE_FC_RET_ERR_NOABILITY 6 /** @} */ /** @name Init FastCall flags diff --git a/drivers/gud/MobiCoreDriver/mci/mciiwp.h b/drivers/gud/MobiCoreDriver/mci/mciiwp.h index 2f7bd2a4d943..4c1ac2ff673a 100644 --- a/drivers/gud/MobiCoreDriver/mci/mciiwp.h +++ b/drivers/gud/MobiCoreDriver/mci/mciiwp.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2016 TRUSTONIC LIMITED + * Copyright (c) 2016-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -17,9 +18,6 @@ #include "public/GP/tee_client_types.h" /* teec_uuid FIXME it's all mixed up! */ -/** Max number of interworld session allocated in MCI buffer */ -#define MAX_IW_SESSION 256 - /** Session ID for notifications for the Dragon CA-to-TA communication protocol * * Session ID are distinct from any valid MCP session identifier @@ -74,7 +72,7 @@ struct interworld_parameter_memref { * Inside the shared pages, the buffer starts at address 'offset' * and ends after 'size' bytes. * - * - wsm_type parameter may be WSM_CONTIGUOUS, WSM_L1 or WSM_L2. + * - wsm_type parameter may be WSM_CONTIGUOUS or WSM_L1. * - offset must be less than the page size (4096). * - size must be less than 0xfffff000. */ diff --git a/drivers/gud/MobiCoreDriver/mci/mcimcp.h b/drivers/gud/MobiCoreDriver/mci/mcimcp.h index 058f6bfb6ee5..a441826d2aad 100644 --- a/drivers/gud/MobiCoreDriver/mci/mcimcp.h +++ b/drivers/gud/MobiCoreDriver/mci/mcimcp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -124,6 +125,8 @@ enum cmd_id { MC_MCP_CMD_LOAD_TOKEN = 0x0B, /** Check that TA can be loaded */ MC_MCP_CMD_CHECK_LOAD_TA = 0x0C, + /** Load a decryption key */ + MC_MCP_CMD_LOAD_SYSENC_KEY_SO = 0x0D, }; /* @@ -131,8 +134,9 @@ enum cmd_id { */ #define WSM_TYPE_MASK 0xFF #define WSM_INVALID 0 /** Invalid memory type */ -#define WSM_L2 2 /** Buffer mapping uses L2/L3 table */ #define WSM_L1 3 /** Buffer mapping uses fake L1 table */ +/**< Bitflag indicating that the buffer should be uncached */ +#define WSM_UNCACHED 0x100 /* * Magic number used to identify if Open Command supports GP client @@ -290,7 +294,7 @@ struct rsp_open { struct cmd_check_load { struct cmd_header cmd_header; /** Command header */ struct mc_uuid_t uuid; /** Service UUID */ - u8 unused[4]; /** Padding to be 64-bit aligned */ + u8 unused[4]; /** Padding to be 64-bit aligned */ u64 adr_load_data; /** Physical address of the data */ u32 wsm_data_type; /** Type of MMU */ u32 ofs_load_data; /** Offset to the data */ @@ -330,7 +334,7 @@ struct rsp_close { * Map a portion of memory to a session. * The MAP command provides a block of memory to the context of a service. * The memory then becomes world-shared memory (WSM). - * The only allowed memory type here is WSM_L2. + * The only allowed memory type here is WSM_L1. */ /** Map Command */ @@ -397,6 +401,25 @@ struct rsp_load_token { struct rsp_header rsp_header; /** Response header */ }; +/** @defgroup MCPLOADKEYSO + * Load a key SO from the normal world and share it with the TEE + * If something fails, the device attestation functionality will be disabled + */ + +/** Load key SO */ +struct cmd_load_key_so { + struct cmd_header cmd_header; /** Command header */ + u32 wsm_data_type; /** Type of MMU */ + u64 adr_load_data; /** Physical address of the MMU */ + u64 ofs_load_data; /** Offset to the data */ + u64 len_load_data; /** Length of the data */ +}; + +/** Load key SO Command Response */ +struct rsp_load_key_so { + struct rsp_header rsp_header; /** Response header */ +}; + /** Structure of the MCP buffer */ union mcp_message { struct init_values init_values; /** Initialisation values */ @@ -420,17 +443,18 @@ union mcp_message { struct rsp_load_token rsp_load_token; struct cmd_check_load cmd_check_load; /** TA load check */ struct rsp_check_load rsp_check_load; + struct cmd_load_key_so cmd_load_key_so;/** Load key SO */ + struct rsp_load_key_so rsp_load_key_so; }; -/** Minimum MCP buffer length (in bytes) */ -#define MIN_MCP_LEN sizeof(mcp_message_t) - #define MC_FLAG_NO_SLEEP_REQ 0 #define MC_FLAG_REQ_TO_SLEEP 1 #define MC_STATE_NORMAL_EXECUTION 0 #define MC_STATE_READY_TO_SLEEP 1 +#define MC_STATE_FLAG_TEE_HALT_MASK BIT(0) + struct sleep_mode { u16 sleep_req; /** Ask SWd to get ready to sleep */ u16 ready_to_sleep; /** SWd is now ready to sleep */ @@ -438,13 +462,15 @@ struct sleep_mode { /** MobiCore status flags */ struct mcp_flags { - /** If not MC_FLAG_SCHEDULE_IDLE, MobiCore needsscheduling */ + /** If not MC_FLAG_SCHEDULE_IDLE, MobiCore needs scheduling */ u32 schedule; struct sleep_mode sleep_mode; /** Secure-world sleep timeout in milliseconds */ s32 timeout_ms; - /** Reserved for future use: Must not be interpreted */ - u32 RFU3; + /** TEE flags */ + u8 tee_flags; + /** Reserved for future use */ + u8 RFU_padding[3]; }; /** MobiCore is idle. No scheduling required */ diff --git a/drivers/gud/MobiCoreDriver/mci/mcinq.h b/drivers/gud/MobiCoreDriver/mci/mcinq.h index 98a51681839b..ba44aca22597 100644 --- a/drivers/gud/MobiCoreDriver/mci/mcinq.h +++ b/drivers/gud/MobiCoreDriver/mci/mcinq.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/mci/mcitime.h b/drivers/gud/MobiCoreDriver/mci/mcitime.h index c666c1a680a6..e1982e3f03d8 100644 --- a/drivers/gud/MobiCoreDriver/mci/mcitime.h +++ b/drivers/gud/MobiCoreDriver/mci/mcitime.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2015 TRUSTONIC LIMITED + * Copyright (c) 2015-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/mci/mcloadformat.h b/drivers/gud/MobiCoreDriver/mci/mcloadformat.h index 1ebea5f17ef9..cf957761b8f7 100644 --- a/drivers/gud/MobiCoreDriver/mci/mcloadformat.h +++ b/drivers/gud/MobiCoreDriver/mci/mcloadformat.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -18,6 +19,14 @@ #define MC_TLBLOBLEN_MAGIC 0x7672746C /* Magic for SWd: vrtl */ #define MAX_SO_CONT_SIZE 512 /* Max size for a container */ +/** MCLF magic */ +/**< "MCLF" in big endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_BE \ + ((uint32_t)('M' | ('C' << 8) | ('L' << 16) | ('F' << 24))) +/**< "MCLF" in little endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_LE \ + ((uint32_t)(('M' << 24) | ('C' << 16) | ('L' << 8) | 'F')) + /** MCLF flags */ /**< Loaded service cannot be unloaded from MobiCore. */ #define MC_SERVICE_HEADER_FLAGS_PERMANENT BIT(0) diff --git a/drivers/gud/MobiCoreDriver/mcp.c b/drivers/gud/MobiCoreDriver/mcp.c index ca541acf9488..0e83b6c375ff 100644 --- a/drivers/gud/MobiCoreDriver/mcp.c +++ b/drivers/gud/MobiCoreDriver/mcp.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * Copyright (c) 2013-2020 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -41,10 +42,11 @@ #include "mci/mcitime.h" /* struct mcp_time */ #include "mci/mciiwp.h" -#include "platform.h" /* IRQ number */ #include "main.h" -#include "fastcall.h" -#include "logging.h" +#include "admin.h" /* tee_object* for 'blob' */ +#include "mmu.h" /* MMU for 'blob' */ +#include "nq.h" +#include "xen_fe.h" #include "mcp.h" /* respond timeout for MCP notification, in secs */ @@ -53,7 +55,8 @@ #define MCP_NF_QUEUE_SZ 8 static struct { - struct mutex queue_lock; /* Lock for MCP messages */ + union mcp_message *buffer; /* MCP communication buffer */ + struct mutex buffer_mutex; /* Lock for the buffer above */ struct completion complete; bool mcp_dead; struct mcp_session mcp_session; /* Pseudo session for MCP */ @@ -63,12 +66,13 @@ static struct { /* Sessions */ struct mutex sessions_lock; struct list_head sessions; - /* Wait timeout */ - u32 timeout; - /* Log of last MCP commands */ -#define MCP_LOG_SIZE 256 - struct mutex last_mcp_cmds_mutex; /* Log protection */ - struct mcp_command_info { + /* TEE bad state detection */ + struct notifier_block tee_stop_notifier; + u32 timeout_period; + /* Log of last commands */ +#define LAST_CMDS_SIZE 1024 + struct mutex last_cmds_mutex; /* Log protection */ + struct command_info { u64 cpu_clk; /* Kernel time */ pid_t pid; /* Caller PID */ enum cmd_id id; /* MCP command ID */ @@ -81,13 +85,13 @@ static struct { COMPLETE, /* Got result */ FAILED, /* Something went wrong */ } state; /* Command processing state */ - enum mcp_result result; /* Command result */ int errno; /* Return code */ - } last_mcp_cmds[MCP_LOG_SIZE]; - int last_mcp_cmds_index; + enum mcp_result result; /* Command result */ + } last_cmds[LAST_CMDS_SIZE]; + int last_cmds_index; } l_ctx; -static const char *mcp_cmd_to_string(enum cmd_id id) +static const char *cmd_to_string(enum cmd_id id) { switch (id) { case MC_MCP_CMD_ID_INVALID: @@ -112,10 +116,25 @@ static const char *mcp_cmd_to_string(enum cmd_id id) return "load token"; case MC_MCP_CMD_CHECK_LOAD_TA: return "check load TA"; + case MC_MCP_CMD_LOAD_SYSENC_KEY_SO: + return "load Key SO"; } return "unknown"; } +static const char *state_to_string(enum mcp_session_state state) +{ + switch (state) { + case MCP_SESSION_RUNNING: + return "running"; + case MCP_SESSION_CLOSE_FAILED: + return "close failed"; + case MCP_SESSION_CLOSED: + return "closed"; + } + return "error"; +} + static inline void mark_mcp_dead(void) { struct mcp_session *session; @@ -127,6 +146,13 @@ static inline void mark_mcp_dead(void) complete(&session->completion); } +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mark_mcp_dead(); + return 0; +} + void mcp_session_init(struct mcp_session *session) { nq_session_init(&session->nq_session, false); @@ -137,6 +163,7 @@ void mcp_session_init(struct mcp_session *session) mutex_init(&session->exit_code_lock); session->exit_code = 0; session->state = MCP_SESSION_RUNNING; + session->notif_count = 0; } static inline bool mcp_session_isrunning(struct mcp_session *session) @@ -153,14 +180,22 @@ static inline bool mcp_session_isrunning(struct mcp_session *session) * session remains valid thanks to the upper layers reference counters, but the * SWd session may have died, in which case we are informed. */ -int mcp_session_waitnotif(struct mcp_session *session, s32 timeout, - bool silent_expiry) +int mcp_wait(struct mcp_session *session, s32 timeout, int silent_expiry) { + s32 err; int ret = 0; mutex_lock(&session->notif_wait_lock); +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) { + ret = xen_mc_wait(session, timeout, silent_expiry); + mutex_unlock(&session->notif_wait_lock); + return ret; + } +#endif + if (l_ctx.mcp_dead) { - ret = -ENOTCONN; + ret = -EHOSTUNREACH; goto end; } @@ -169,7 +204,8 @@ int mcp_session_waitnotif(struct mcp_session *session, s32 timeout, goto end; } - if (mcp_session_exitcode(session)) { + mcp_get_err(session, &err); + if (err) { ret = -ECOMM; goto end; } @@ -195,11 +231,12 @@ int mcp_session_waitnotif(struct mcp_session *session, s32 timeout, } if (l_ctx.mcp_dead) { - ret = -ENOTCONN; + ret = -EHOSTUNREACH; goto end; } - if (mcp_session_exitcode(session)) { + mcp_get_err(session, &err); + if (err) { ret = -ECOMM; goto end; } @@ -230,53 +267,46 @@ end: return ret; } -s32 mcp_session_exitcode(struct mcp_session *session) +int mcp_get_err(struct mcp_session *session, s32 *err) { - s32 exit_code; +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_get_err(session, err); +#endif mutex_lock(&session->exit_code_lock); - exit_code = session->exit_code; + *err = session->exit_code; mutex_unlock(&session->exit_code_lock); - if (exit_code) - mc_dev_info("session %x ec %d", session->sid, exit_code); + if (*err) + mc_dev_info("session %x ec %d", session->sid, *err); - return exit_code; + return 0; } static inline int wait_mcp_notification(void) { - unsigned long timeout = msecs_to_jiffies(l_ctx.timeout * 1000); - int try; + unsigned long timeout = msecs_to_jiffies(l_ctx.timeout_period * 1000); + int try, ret = -ETIME; /* - * Total timeout is l_ctx.timeout * MCP_RETRIES, but we check for + * Total timeout is l_ctx.timeout_period * MCP_RETRIES, but we check for * a crash to try and terminate before then if things go wrong. */ for (try = 1; try <= MCP_RETRIES; try++) { - u32 status; - int ret; - /* * Wait non-interruptible to keep MCP synchronised even if * caller is interrupted by signal. */ - ret = wait_for_completion_timeout(&l_ctx.complete, timeout); - if (ret > 0) + if (wait_for_completion_timeout(&l_ctx.complete, timeout) > 0) return 0; - mc_dev_err("No answer after %ds", l_ctx.timeout * try); - - /* If SWd halted, exit now */ - if (!mc_fc_info(MC_EXT_INFO_ID_MCI_VERSION, &status, NULL) && - status == MC_STATUS_HALT) - break; + mc_dev_err(ret, "no answer after %ds", + l_ctx.timeout_period * try); } - /* TEE halted or dead: dump status and SMC log */ - mark_mcp_dead(); - nq_dump_status(); - - return -ETIME; + mc_dev_err(ret, "timed out waiting for MCP notification"); + nq_signal_tee_hung(); + return ret; } static int mcp_cmd(union mcp_message *cmd, @@ -285,21 +315,20 @@ static int mcp_cmd(union mcp_message *cmd, u32 *out_session_id, struct mc_uuid_t *uuid) { - int err = 0, ret = -ENOTCONN; - union mcp_message *msg = nq_get_mcp_message(); + int err = 0, ret = -EHOSTUNREACH; + union mcp_message *msg; enum cmd_id cmd_id = cmd->cmd_header.cmd_id; - struct mcp_command_info *cmd_info; + struct command_info *cmd_info; /* Initialize MCP log */ - mutex_lock(&l_ctx.last_mcp_cmds_mutex); - cmd_info = &l_ctx.last_mcp_cmds[l_ctx.last_mcp_cmds_index]; + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; cmd_info->cpu_clk = local_clock(); cmd_info->pid = current->pid; - cmd_info->cpu_clk = local_clock(); cmd_info->id = cmd_id; cmd_info->session_id = in_session_id; if (uuid) { - /* display UUID because it's an openSession cmd */ + /* Keep UUID because it's an 'open session' cmd */ size_t i; cmd_info->uuid_str[0] = ' '; @@ -312,37 +341,37 @@ static int mcp_cmd(union mcp_message *cmd, } cmd_info->state = PENDING; - cmd_info->result = MC_MCP_RET_OK; cmd_info->errno = 0; - if (++l_ctx.last_mcp_cmds_index >= MCP_LOG_SIZE) - l_ctx.last_mcp_cmds_index = 0; - mutex_unlock(&l_ctx.last_mcp_cmds_mutex); + cmd_info->result = MC_MCP_RET_OK; + if (++l_ctx.last_cmds_index >= LAST_CMDS_SIZE) + l_ctx.last_cmds_index = 0; + mutex_unlock(&l_ctx.last_cmds_mutex); - mutex_lock(&l_ctx.queue_lock); + mutex_lock(&l_ctx.buffer_mutex); + msg = l_ctx.buffer; if (l_ctx.mcp_dead) goto out; /* Copy message to MCP buffer */ memcpy(msg, cmd, sizeof(*msg)); - /* Poke TEE */ - ret = mcp_notify(&l_ctx.mcp_session); - if (ret) - goto out; + /* Send MCP notification, with cmd_id as payload for debug purpose */ + nq_session_notify(&l_ctx.mcp_session.nq_session, l_ctx.mcp_session.sid, + cmd_id); /* Update MCP log */ - mutex_lock(&l_ctx.last_mcp_cmds_mutex); + mutex_lock(&l_ctx.last_cmds_mutex); cmd_info->state = SENT; - mutex_unlock(&l_ctx.last_mcp_cmds_mutex); + mutex_unlock(&l_ctx.last_cmds_mutex); ret = wait_mcp_notification(); if (ret) goto out; /* Check response ID */ if (msg->rsp_header.rsp_id != (cmd_id | FLAG_RESPONSE)) { - mc_dev_err("MCP command got invalid response (0x%X)", - msg->rsp_header.rsp_id); ret = -EBADE; + mc_dev_err(ret, "MCP command got invalid response (0x%X)", + msg->rsp_header.rsp_id); goto out; } @@ -390,68 +419,83 @@ static int mcp_cmd(union mcp_message *cmd, out: /* Update MCP log */ - mutex_lock(&l_ctx.last_mcp_cmds_mutex); + mutex_lock(&l_ctx.last_cmds_mutex); if (ret) { cmd_info->state = FAILED; cmd_info->errno = -ret; } else { cmd_info->state = COMPLETE; - cmd_info->result = msg->rsp_header.result; cmd_info->errno = -err; + cmd_info->result = msg->rsp_header.result; /* For open session: get SID */ if (!err && out_session_id) cmd_info->session_id = *out_session_id; } - mutex_unlock(&l_ctx.last_mcp_cmds_mutex); - mutex_unlock(&l_ctx.queue_lock); + mutex_unlock(&l_ctx.last_cmds_mutex); + mutex_unlock(&l_ctx.buffer_mutex); if (ret) { - mc_dev_err("%s: sending failed, ret = %d", - mcp_cmd_to_string(cmd_id), ret); + mc_dev_err(ret, "%s: sending failed", cmd_to_string(cmd_id)); return ret; } if (err) { if (cmd_id == MC_MCP_CMD_CLOSE_SESSION && err == -EAGAIN) mc_dev_devel("%s: try again", - mcp_cmd_to_string(cmd_id)); + cmd_to_string(cmd_id)); else - mc_dev_err("%s: res %d/ret %d", - mcp_cmd_to_string(cmd_id), - msg->rsp_header.result, err); + mc_dev_err(err, "%s: res %d", cmd_to_string(cmd_id), + msg->rsp_header.result); return err; } return 0; } +static inline int __mcp_get_version(struct mc_version_info *version_info) +{ + union mcp_message cmd; + u32 version; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_get_version(version_info); +#endif + + version = MC_VERSION(MCDRVMODULEAPI_VERSION_MAJOR, + MCDRVMODULEAPI_VERSION_MINOR); + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_GET_MOBICORE_VERSION; + ret = mcp_cmd(&cmd, 0, NULL, NULL); + if (ret) + return ret; + + memcpy(version_info, &cmd.rsp_get_version.version_info, + sizeof(*version_info)); + /* + * The CMP version is meaningless in this case, and is replaced + * by the driver's own version. + */ + version_info->version_nwd = version; + return 0; +} + int mcp_get_version(struct mc_version_info *version_info) { static struct mc_version_info static_version_info; /* If cache empty, get version from the SWd and cache it */ if (!static_version_info.version_nwd) { - u32 version = MC_VERSION(MCDRVMODULEAPI_VERSION_MAJOR, - MCDRVMODULEAPI_VERSION_MINOR); - union mcp_message cmd; - int ret; + int ret = __mcp_get_version(&static_version_info); - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd_header.cmd_id = MC_MCP_CMD_GET_MOBICORE_VERSION; - ret = mcp_cmd(&cmd, 0, NULL, NULL); if (ret) return ret; - - memcpy(&static_version_info, &cmd.rsp_get_version.version_info, - sizeof(static_version_info)); - /* - * The CMP version is meaningless in this case, and is replaced - * by the driver's own version. - */ - static_version_info.version_nwd = version; } /* Copy cached version */ memcpy(version_info, &static_version_info, sizeof(*version_info)); + nq_set_version_ptr(static_version_info.product_id); return 0; } @@ -462,7 +506,7 @@ int mcp_load_token(uintptr_t data, const struct mcp_buffer_map *map) memset(&cmd, 0, sizeof(cmd)); cmd.cmd_header.cmd_id = MC_MCP_CMD_LOAD_TOKEN; cmd.cmd_load_token.wsm_data_type = map->type; - cmd.cmd_load_token.adr_load_data = map->phys_addr; + cmd.cmd_load_token.adr_load_data = map->addr; cmd.cmd_load_token.ofs_load_data = map->offset; cmd.cmd_load_token.len_load_data = map->length; return mcp_cmd(&cmd, 0, NULL, NULL); @@ -478,7 +522,7 @@ int mcp_load_check(const struct tee_object *obj, cmd.cmd_header.cmd_id = MC_MCP_CMD_CHECK_LOAD_TA; /* Data */ cmd.cmd_check_load.wsm_data_type = map->type; - cmd.cmd_check_load.adr_load_data = map->phys_addr; + cmd.cmd_check_load.adr_load_data = map->addr; cmd.cmd_check_load.ofs_load_data = map->offset; cmd.cmd_check_load.len_load_data = map->length; /* Header */ @@ -487,44 +531,115 @@ int mcp_load_check(const struct tee_object *obj, return mcp_cmd(&cmd, 0, NULL, &cmd.cmd_check_load.uuid); } -int mcp_open_session(struct mcp_session *session, - const struct tee_object *obj, - const struct mcp_buffer_map *map, - const struct mcp_buffer_map *tci_map) +int mcp_load_key_so(uintptr_t data, const struct mcp_buffer_map *map) +{ + union mcp_message cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_LOAD_SYSENC_KEY_SO; + cmd.cmd_load_key_so.wsm_data_type = map->type; + cmd.cmd_load_key_so.adr_load_data = map->addr; + cmd.cmd_load_key_so.ofs_load_data = map->offset; + cmd.cmd_load_key_so.len_load_data = map->length; + return mcp_cmd(&cmd, 0, NULL, NULL); +} + +int mcp_open_session(struct mcp_session *session, struct mcp_open_info *info, + bool *tci_in_use) { static DEFINE_MUTEX(local_mutex); + struct tee_object *obj; const union mclf_header *header; + struct tee_mmu *obj_mmu; + struct mcp_buffer_map obj_map; union mcp_message cmd; int ret; +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) { + ret = xen_mc_open_session(session, info); + if (ret) + return ret; + + /* Add to list of sessions */ + mutex_lock(&l_ctx.sessions_lock); + list_add_tail(&session->list, &l_ctx.sessions); + mutex_unlock(&l_ctx.sessions_lock); + return 0; + } +#endif + + /* Create 'blob' */ + if (info->type == TEE_MC_UUID) { + /* Get TA from registry */ + obj = tee_object_get(info->uuid, false); + /* Tell SWd to load TA from SFS as not in registry */ + if (IS_ERR(obj) && (PTR_ERR(obj) == -ENOENT)) + obj = tee_object_select(info->uuid); + } else if (info->type == TEE_MC_DRIVER_UUID) { + /* Load driver using only uuid */ + obj = tee_object_select(info->uuid); + *tci_in_use = false; + } else if (info->user) { + /* Create secure object from user-space trustlet binary */ + obj = tee_object_read(info->spid, info->va, info->len); + } else { + /* Create secure object from kernel-space trustlet binary */ + obj = tee_object_copy(info->va, info->len); + } + + if (IS_ERR(obj)) + return PTR_ERR(obj); + + /* Header */ + header = (const union mclf_header *)(&obj->data[obj->header_length]); + if (info->type == TEE_MC_DRIVER && + (header->mclf_header_v2.flags & + MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE)) + *tci_in_use = false; + + /* Create mapping for blob (allocated by driver, so task = NULL) */ + { + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)obj->data, + .len = obj->length, + .flags = MC_IO_MAP_INPUT, + }; + + obj_mmu = tee_mmu_create(NULL, &buf); + if (IS_ERR(obj_mmu)) { + ret = PTR_ERR(obj_mmu); + goto err_mmu; + } + + tee_mmu_buffer(obj_mmu, &obj_map); + } + memset(&cmd, 0, sizeof(cmd)); cmd.cmd_header.cmd_id = MC_MCP_CMD_OPEN_SESSION; /* Data */ - cmd.cmd_open.wsm_data_type = map->type; - cmd.cmd_open.adr_load_data = map->phys_addr; - cmd.cmd_open.ofs_load_data = map->offset; - cmd.cmd_open.len_load_data = map->length; + cmd.cmd_open.uuid = header->mclf_header_v2.uuid; + cmd.cmd_open.wsm_data_type = obj_map.type; + cmd.cmd_open.adr_load_data = obj_map.addr; + cmd.cmd_open.ofs_load_data = obj_map.offset; + cmd.cmd_open.len_load_data = obj_map.length; /* Buffer */ - if (tci_map) { - cmd.cmd_open.wsmtype_tci = tci_map->type; - cmd.cmd_open.adr_tci_buffer = tci_map->phys_addr; - cmd.cmd_open.ofs_tci_buffer = tci_map->offset; - cmd.cmd_open.len_tci_buffer = tci_map->length; + if (*tci_in_use) { + struct mcp_buffer_map map; + + tee_mmu_buffer(info->tci_mmu, &map); + cmd.cmd_open.wsmtype_tci = map.type; + cmd.cmd_open.adr_tci_buffer = map.addr; + cmd.cmd_open.ofs_tci_buffer = map.offset; + cmd.cmd_open.len_tci_buffer = map.length; } else { cmd.cmd_open.wsmtype_tci = WSM_INVALID; } - /* Header */ - header = (union mclf_header *)(obj->data + obj->header_length); - cmd.cmd_open.uuid = header->mclf_header_v2.uuid; - cmd.cmd_open.is_gpta = nq_session_is_gp(&session->nq_session); + /* Reset unexpected notification */ mutex_lock(&local_mutex); l_ctx.unexp_notif.session_id = SID_MCP; /* Cannot be */ - if (!g_ctx.f_client_login) - memcpy(&cmd.cmd_open.tl_header, header, - sizeof(cmd.cmd_open.tl_header)); - else - cmd.cmd_open.cmd_open_data.mclf_magic = MC_GP_CLIENT_AUTH_MAGIC; + cmd.cmd_open.cmd_open_data.mclf_magic = MC_GP_CLIENT_AUTH_MAGIC; /* Send MCP open command */ ret = mcp_cmd(&cmd, 0, &cmd.rsp_open.session_id, &cmd.cmd_open.uuid); @@ -553,6 +668,14 @@ int mcp_open_session(struct mcp_session *session, } mutex_unlock(&local_mutex); + + /* Blob for UUID/TA not needed as re-mapped by the SWd */ + tee_mmu_put(obj_mmu); + +err_mmu: + /* Delete secure object */ + tee_object_free(obj); + return ret; } @@ -567,15 +690,23 @@ int mcp_open_session(struct mcp_session *session, int mcp_close_session(struct mcp_session *session) { union mcp_message cmd; - int ret; + /* ret's value is always set, but some compilers complain */ + int ret = -ENXIO; + + if (is_xen_domu()) { +#ifdef TRUSTONIC_XEN_DOMU + ret = xen_mc_close_session(session); +#endif + } else { + /* Signal a potential waiter that SWd session is going away */ + complete(&session->completion); + /* Send MCP command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_CLOSE_SESSION; + cmd.cmd_close.session_id = session->sid; + ret = mcp_cmd(&cmd, cmd.cmd_close.session_id, NULL, NULL); + } - /* Signal a potential waiter that SWd session is going away */ - complete(&session->completion); - /* Send MCP command */ - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd_header.cmd_id = MC_MCP_CMD_CLOSE_SESSION; - cmd.cmd_close.session_id = session->sid; - ret = mcp_cmd(&cmd, cmd.cmd_close.session_id, NULL, NULL); mutex_lock(&l_ctx.sessions_lock); if (!ret) { session->state = MCP_SESSION_CLOSED; @@ -586,38 +717,46 @@ int mcp_close_session(struct mcp_session *session) session->state = MCP_SESSION_CLOSE_FAILED; } mutex_unlock(&l_ctx.sessions_lock); - mc_dev_devel("close session %x ret %d state %d", - session->sid, ret, session->state); + mc_dev_devel("close session %x ret %d state %s", + session->sid, ret, state_to_string(session->state)); return ret; } /* - * Session is to be removed from NWd records as SWd is dead + * Session is to be removed from NWd records as SWd has been wiped clean */ -void mcp_kill_session(struct mcp_session *session) +void mcp_cleanup_session(struct mcp_session *session) { mutex_lock(&l_ctx.sessions_lock); + session->state = MCP_SESSION_CLOSED; list_del(&session->list); nq_session_exit(&session->nq_session); mutex_unlock(&l_ctx.sessions_lock); } -int mcp_map(u32 session_id, struct mcp_buffer_map *map) +int mcp_map(u32 session_id, struct tee_mmu *mmu, u32 *sva) { + struct mcp_buffer_map map; union mcp_message cmd; int ret; +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_map(session_id, mmu, sva); +#endif + memset(&cmd, 0, sizeof(cmd)); cmd.cmd_header.cmd_id = MC_MCP_CMD_MAP; cmd.cmd_map.session_id = session_id; - cmd.cmd_map.wsm_type = map->type; - cmd.cmd_map.adr_buffer = map->phys_addr; - cmd.cmd_map.ofs_buffer = map->offset; - cmd.cmd_map.len_buffer = map->length; - cmd.cmd_map.flags = map->flags; + tee_mmu_buffer(mmu, &map); + cmd.cmd_map.wsm_type = map.type; + cmd.cmd_map.adr_buffer = map.addr; + cmd.cmd_map.ofs_buffer = map.offset; + cmd.cmd_map.len_buffer = map.length; + cmd.cmd_map.flags = map.flags; ret = mcp_cmd(&cmd, session_id, NULL, NULL); if (!ret) { - map->secure_va = cmd.rsp_map.secure_va; + *sva = cmd.rsp_map.secure_va; atomic_inc(&g_ctx.c_maps); } @@ -629,6 +768,11 @@ int mcp_unmap(u32 session_id, const struct mcp_buffer_map *map) union mcp_message cmd; int ret; +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_unmap(session_id, map); +#endif + memset(&cmd, 0, sizeof(cmd)); cmd.cmd_header.cmd_id = MC_MCP_CMD_UNMAP; cmd.cmd_unmap.session_id = session_id; @@ -653,12 +797,22 @@ static int mcp_close(void) int mcp_notify(struct mcp_session *session) { + if (l_ctx.mcp_dead) + return -EHOSTUNREACH; + if (session->sid == SID_MCP) mc_dev_devel("notify MCP"); else mc_dev_devel("notify session %x", session->sid); - return nq_session_notify(&session->nq_session, session->sid, 0); +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_notify(session); +#endif + + /* Put notif_count as payload for debug purpose */ + return nq_session_notify(&session->nq_session, session->sid, + ++session->notif_count); } static inline void session_notif_handler(struct mcp_session *session, u32 id, @@ -672,17 +826,7 @@ static inline void session_notif_handler(struct mcp_session *session, u32 id, if (payload) { /* Update exit code, or not */ mutex_lock(&session->exit_code_lock); - /* - * In GP, the only way to recover the sessions exit code - * is to call TEEC_InvokeCommand which will notify. But - * notifying a dead session would change the exit code - * to ERR_SID_NOT_ACTIVE, hence the check below. - */ - if (!nq_session_is_gp(&session->nq_session) || - !session->exit_code || - payload != ERR_SID_NOT_ACTIVE) - session->exit_code = payload; - + session->exit_code = payload; mutex_unlock(&session->exit_code_lock); } @@ -727,78 +871,28 @@ static void mcp_notif_handler(u32 id, u32 payload) } } -int mcp_start(void) -{ - return 0; -} - -void mcp_stop(void) -{ - mcp_close(); -} - -int mcp_init(void) -{ - mutex_init(&l_ctx.queue_lock); - init_completion(&l_ctx.complete); - /* Setup notification queue mutex */ - mcp_session_init(&l_ctx.mcp_session); - l_ctx.mcp_session.sid = SID_MCP; - mutex_init(&l_ctx.unexp_notif_mutex); - INIT_LIST_HEAD(&l_ctx.sessions); - mutex_init(&l_ctx.sessions_lock); - mutex_init(&l_ctx.last_mcp_cmds_mutex); - - l_ctx.timeout = MCP_TIMEOUT; - debugfs_create_u32("mcp_timeout", 0600, g_ctx.debug_dir, - &l_ctx.timeout); - - nq_register_notif_handler(mcp_notif_handler, false); - - return 0; -} - -void mcp_exit(void) -{ - mark_mcp_dead(); -} - -static const char *state_to_string(enum mcp_session_state state) -{ - switch (state) { - case MCP_SESSION_RUNNING: - return "running"; - case MCP_SESSION_CLOSE_FAILED: - return "close failed"; - case MCP_SESSION_CLOSED: - return "closed"; - } - return "error"; -} - -int mcp_debug_sessions(struct kasnprintf_buf *buf) +static int debug_sessions(struct kasnprintf_buf *buf) { struct mcp_session *session; int ret; /* Header */ - ret = kasnprintf(buf, "%20s %4s %4s %4s %-15s %-11s\n", - "CPU clock", "ID", "type", "ec", "state", - "notif state"); + ret = kasnprintf(buf, "%20s %4s %-15s %-11s %4s\n", + "CPU clock", "ID", "state", "notif state", "ec"); if (ret < 0) return ret; mutex_lock(&l_ctx.sessions_lock); list_for_each_entry(session, &l_ctx.sessions, list) { - s32 exit_code = mcp_session_exitcode(session); - struct nq_session *nq_session = &session->nq_session; + const char *state_str; + u64 cpu_clk; + s32 err; - ret = kasnprintf(buf, "%20llu %4x %-4s %4d %-15s %-11s\n", - nq_session_notif_cpu_clk(nq_session), - session->sid, - nq_session_is_gp(nq_session) ? "GP" : "MC", - exit_code, state_to_string(session->state), - nq_session_state_string(nq_session)); + state_str = nq_session_state(&session->nq_session, &cpu_clk); + mcp_get_err(session, &err); + ret = kasnprintf(buf, "%20llu %4x %-15s %-11s %4d\n", cpu_clk, + session->sid, state_to_string(session->state), + state_str, err); if (ret < 0) break; } @@ -806,8 +900,22 @@ int mcp_debug_sessions(struct kasnprintf_buf *buf) return ret; } -static inline int show_mcp_log_entry(struct kasnprintf_buf *buf, - struct mcp_command_info *cmd_info) +static ssize_t debug_sessions_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + debug_sessions); +} + +static const struct file_operations debug_sessions_ops = { + .read = debug_sessions_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline int show_log_entry(struct kasnprintf_buf *buf, + struct command_info *cmd_info) { const char *state_str = "unknown"; @@ -829,44 +937,102 @@ static inline int show_mcp_log_entry(struct kasnprintf_buf *buf, break; } - return kasnprintf(buf, "%20llu %5d %-13s %5x %-8s %6d %5d%s\n", + return kasnprintf(buf, "%20llu %5d %-16s %5x %-8s %5d %6d%s\n", cmd_info->cpu_clk, cmd_info->pid, - mcp_cmd_to_string(cmd_info->id), cmd_info->session_id, - state_str, cmd_info->result, cmd_info->errno, + cmd_to_string(cmd_info->id), cmd_info->session_id, + state_str, cmd_info->errno, cmd_info->result, cmd_info->uuid_str); } -int mcp_debug_mcpcmds(struct kasnprintf_buf *buf) +static int debug_last_cmds(struct kasnprintf_buf *buf) { - struct mcp_command_info *cmd_info; + struct command_info *cmd_info; int i, ret = 0; /* Initialize MCP log */ - mutex_lock(&l_ctx.last_mcp_cmds_mutex); - ret = kasnprintf(buf, "%20s %5s %-13s %5s %-8s %6s %5s %s\n", + mutex_lock(&l_ctx.last_cmds_mutex); + ret = kasnprintf(buf, "%20s %5s %-16s %5s %-8s %5s %6s %s\n", "CPU clock", "PID", "command", "S-ID", - "state", "result", "errno", "UUID"); + "state", "errno", "result", "UUID"); if (ret < 0) goto out; - cmd_info = &l_ctx.last_mcp_cmds[l_ctx.last_mcp_cmds_index]; + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; if (cmd_info->state != UNUSED) /* Buffer has wrapped around, dump end (oldest records) */ - for (i = l_ctx.last_mcp_cmds_index; i < MCP_LOG_SIZE; i++) { - ret = show_mcp_log_entry(buf, cmd_info++); + for (i = l_ctx.last_cmds_index; i < LAST_CMDS_SIZE; i++) { + ret = show_log_entry(buf, cmd_info++); if (ret < 0) goto out; } /* Dump first records */ - cmd_info = &l_ctx.last_mcp_cmds[0]; - for (i = 0; i < l_ctx.last_mcp_cmds_index; i++) { - ret = show_mcp_log_entry(buf, cmd_info++); + cmd_info = &l_ctx.last_cmds[0]; + for (i = 0; i < l_ctx.last_cmds_index; i++) { + ret = show_log_entry(buf, cmd_info++); if (ret < 0) goto out; } out: - mutex_unlock(&l_ctx.last_mcp_cmds_mutex); + mutex_unlock(&l_ctx.last_cmds_mutex); return ret; } + +static ssize_t debug_last_cmds_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, debug_last_cmds); +} + +static const struct file_operations debug_last_cmds_ops = { + .read = debug_last_cmds_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +int mcp_init(void) +{ + l_ctx.buffer = nq_get_mcp_buffer(); + mutex_init(&l_ctx.buffer_mutex); + init_completion(&l_ctx.complete); + /* Setup notification queue mutex */ + mcp_session_init(&l_ctx.mcp_session); + l_ctx.mcp_session.sid = SID_MCP; + mutex_init(&l_ctx.unexp_notif_mutex); + INIT_LIST_HEAD(&l_ctx.sessions); + mutex_init(&l_ctx.sessions_lock); + mutex_init(&l_ctx.last_cmds_mutex); + + l_ctx.timeout_period = MCP_TIMEOUT; + + nq_register_notif_handler(mcp_notif_handler, false); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); + + return 0; +} + +void mcp_exit(void) +{ + mark_mcp_dead(); + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); +} + +int mcp_start(void) +{ + /* Create debugfs sessions and last commands entries */ + debugfs_create_file("sessions", 0400, g_ctx.debug_dir, NULL, + &debug_sessions_ops); + debugfs_create_file("last_mcp_commands", 0400, g_ctx.debug_dir, NULL, + &debug_last_cmds_ops); + debugfs_create_u32("mcp_timeout", 0600, g_ctx.debug_dir, + &l_ctx.timeout_period); + return 0; +} + +void mcp_stop(void) +{ + mcp_close(); +} diff --git a/drivers/gud/MobiCoreDriver/mcp.h b/drivers/gud/MobiCoreDriver/mcp.h index 42053430541a..766d8516b29e 100644 --- a/drivers/gud/MobiCoreDriver/mcp.h +++ b/drivers/gud/MobiCoreDriver/mcp.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -18,6 +19,29 @@ #include "mci/mcloadformat.h" /* struct identity */ #include "nq.h" +struct tee_mmu; + +/* Structure to hold the TA/driver information at open */ +struct mcp_open_info { + enum { + TEE_MC_UUID, + TEE_MC_TA, + TEE_MC_DRIVER, + TEE_MC_DRIVER_UUID, + } type; + /* TA/driver */ + const struct mc_uuid_t *uuid; + u32 spid; + uintptr_t va; + size_t len; + /* TCI */ + uintptr_t tci_va; + size_t tci_len; + struct tee_mmu *tci_mmu; + /* Origin */ + bool user; +}; + /* Structure to hold the TA/driver descriptor to pass to MCP */ struct tee_object { u32 length; /* Total length */ @@ -27,16 +51,18 @@ struct tee_object { /* Structure to hold all mapped buffer data to pass to MCP */ struct mcp_buffer_map { - u64 phys_addr; /** Page-aligned physical address */ - u32 secure_va; /** SWd virtual address */ - u32 offset; /** Data offset inside the first page */ - u32 length; /** Length of the data */ - u32 type; /** Type of MMU */ - u32 flags; /** Flags (typically read/write) */ + u64 addr; /** Page-aligned PA, or VA */ + unsigned long nr_pages; /** Total number of pages mapped */ + u32 secure_va; /** SWd virtual address */ + u32 offset; /** Data offset inside the first page */ + u32 length; /** Length of the data */ + u32 type; /** Type of MMU */ + u32 flags; /** Flags (typically read/write) */ + struct tee_mmu *mmu; /** MMU from which the map was made */ }; struct mcp_session { - /* Notification queue session (MUST BE FIRST) */ + /* Notification queue session */ struct nq_session nq_session; /* Session ID */ u32 sid; @@ -56,28 +82,28 @@ struct mcp_session { MCP_SESSION_CLOSE_FAILED, MCP_SESSION_CLOSED, } state; + /* Notification counter */ + u32 notif_count; }; /* Init for the mcp_session structure */ void mcp_session_init(struct mcp_session *session); -int mcp_session_waitnotif(struct mcp_session *session, s32 timeout, - bool silent_expiry); -s32 mcp_session_exitcode(struct mcp_session *mcp_session); /* Commands */ int mcp_get_version(struct mc_version_info *version_info); int mcp_load_token(uintptr_t data, const struct mcp_buffer_map *buffer_map); int mcp_load_check(const struct tee_object *obj, const struct mcp_buffer_map *buffer_map); -int mcp_open_session(struct mcp_session *session, - const struct tee_object *obj, - const struct mcp_buffer_map *map, - const struct mcp_buffer_map *tci_map); +int mcp_load_key_so(uintptr_t data, const struct mcp_buffer_map *buffer_map); +int mcp_open_session(struct mcp_session *session, struct mcp_open_info *info, + bool *tci_in_use); int mcp_close_session(struct mcp_session *session); -void mcp_kill_session(struct mcp_session *session); -int mcp_map(u32 session_id, struct mcp_buffer_map *map); +void mcp_cleanup_session(struct mcp_session *session); +int mcp_map(u32 session_id, struct tee_mmu *mmu, u32 *sva); int mcp_unmap(u32 session_id, const struct mcp_buffer_map *map); int mcp_notify(struct mcp_session *mcp_session); +int mcp_wait(struct mcp_session *session, s32 timeout, int silent_expiry); +int mcp_get_err(struct mcp_session *session, s32 *err); /* Initialisation/cleanup */ int mcp_init(void); @@ -85,8 +111,4 @@ void mcp_exit(void); int mcp_start(void); void mcp_stop(void); -/* Debug */ -int mcp_debug_sessions(struct kasnprintf_buf *buf); -int mcp_debug_mcpcmds(struct kasnprintf_buf *buf); - #endif /* _MC_MCP_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mmu.c b/drivers/gud/MobiCoreDriver/mmu.c index 303cfded1f52..f86d29034463 100644 --- a/drivers/gud/MobiCoreDriver/mmu.c +++ b/drivers/gud/MobiCoreDriver/mmu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -24,15 +25,22 @@ #include #include +#ifdef CONFIG_XEN +/* To get the MFN */ +#include +#include +#endif + #include "public/mc_user.h" #include "mci/mcimcp.h" -#include "platform.h" /* CONFIG_TRUSTONIC_TEE_LPAE */ #include "main.h" #include "mcp.h" /* mcp_buffer_map */ #include "mmu.h" +#define PHYS_48BIT_MASK (BIT(48) - 1) + /* Common */ #define MMU_BUFFERABLE BIT(2) /* AttrIndx[0] */ #define MMU_CACHEABLE BIT(3) /* AttrIndx[1] */ @@ -57,26 +65,28 @@ #define MMU_EXT_SHARED_32 BIT(10) /* ARMv6 and higher */ /* ION */ -#define MMU_ION_BUF BIT(24) /* Trustonic Specific flag to detect ION mem */ +/* Trustonic Specific flag to detect ION mem */ +#define MMU_ION_BUF BIT(24) /* - * MobiCore specific page tables for world shared memory. - * Linux uses shadow page tables, see arch/arm/include/asm/pgtable-2level. - * MobiCore uses the default ARM format. - * - * Number of page table entries in one L2 MMU table. This is ARM specific, an - * MMU table covers 1 MiB by using 256 entries referring to 4KiB pages each. + * Specific case for kernel 4.4.168 that does not have the same + * get_user_pages() implementation */ -#define L2_ENTRIES_MAX 256 +#if KERNEL_VERSION(4, 4, 167) < LINUX_VERSION_CODE && \ + KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; -/* - * Small buffers (below 1MiB) are mapped using the legacy L2 table, but bigger - * buffers now use a fake L1 table that holds 64-bit pointers to L2 tables. As - * this must be exactly one page, we can hold up to 512 entries. - */ -#define L1_ENTRIES_MAX 512 + if (write) + gup_flags |= FOLL_WRITE; -#if KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE + return get_user_pages(NULL, mm, start, nr_pages, gup_flags, pages, + NULL); +} +#elif KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE static inline long gup_local(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) @@ -88,39 +98,40 @@ static inline long gup_local(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) { - unsigned int flags = 0; + unsigned int gup_flags = 0; if (write) - flags |= FOLL_WRITE; + gup_flags |= FOLL_WRITE; + /* gup_flags |= FOLL_CMA; */ - return get_user_pages_remote(NULL, mm, start, nr_pages, write, 0, pages, - NULL); + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + 0, pages, NULL); } #elif KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE static inline long gup_local(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) { - unsigned int flags = 0; + unsigned int gup_flags = 0; if (write) - flags |= FOLL_WRITE; + gup_flags |= FOLL_WRITE; - return get_user_pages_remote(NULL, mm, start, nr_pages, flags, pages, - NULL); + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + pages, NULL); } #else static inline long gup_local(struct mm_struct *mm, uintptr_t start, unsigned long nr_pages, int write, struct page **pages) { - unsigned int flags = 0; + unsigned int gup_flags = 0; if (write) - flags |= FOLL_WRITE; + gup_flags |= FOLL_WRITE; - return get_user_pages_remote(NULL, mm, start, nr_pages, flags, pages, - NULL, NULL); + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + pages, NULL, NULL); } #endif @@ -142,21 +153,17 @@ static inline long gup_local_repeat(struct mm_struct *mm, uintptr_t start, } /* - * Fake L1 MMU table. + * A table that could be either a pmd or pte */ -union l1_table { - u64 *pages_phys; /* Array of physical page addresses */ - unsigned long page; -}; - -/* - * L2 MMU table, which is more a L3 table in the LPAE case. - */ -union l2_table { - union { /* Array of PTEs */ - u32 *ptes_32; - u64 *ptes_64; - }; +union mmu_table { + u64 *entries; /* Array of PTEs */ + /* Array of pages */ + struct page **pages; + /* Array of VAs */ + uintptr_t *vas; + /* Address of table */ + void *addr; + /* Page for table */ unsigned long page; }; @@ -168,91 +175,32 @@ union l2_table { * the MMU table and a handle for this table is returned to the user. */ struct tee_mmu { - union l2_table l2_tables[L1_ENTRIES_MAX]; /* L2 tables */ - size_t l2_tables_nr; /* Actual number of L2 tables */ - union l1_table l1_table; /* Fake L1 table */ - union l2_table l1_l2_table; /* L2 table for the L1 table */ - u32 offset; - u32 length; - bool user; /* Pages are from user space */ - int pages_created; /* Leak check */ - int pages_locked; /* Leak check */ + struct kref kref; + /* Array of pages that hold buffer ptes*/ + union mmu_table pte_tables[PMD_ENTRIES_MAX]; + /* Actual number of ptes tables */ + size_t nr_pmd_entries; + /* Contains phys @ of ptes tables */ + union mmu_table pmd_table; + struct tee_deleter *deleter; /* Xen map to free */ + unsigned long nr_pages; + int pages_created; /* Leak check */ + int pages_locked; /* Leak check */ + u32 offset; + u32 length; + u32 flags; + /* Pages are from user space */ + bool user; + bool use_pages_and_vas; /* ION case only */ - struct dma_buf *dma_buf; - struct dma_buf_attachment *attach; - struct sg_table *sgt; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; }; -/* - * Linux uses different mappings for SMP systems(the sharing flag is set for the - * pte. In order not to confuse things too much in Mobicore make sure the shared - * buffers have the same flags. This should also be done in SWD side. - */ - -static u64 pte_flags_64 = MMU_BUFFERABLE | MMU_CACHEABLE | MMU_EXT_NG | -#ifdef CONFIG_SMP - MMU_EXT_SHARED_64 | -#endif /* CONFIG_SMP */ - MMU_EXT_XN | MMU_EXT_AF | MMU_AP_RW_ALL | - MMU_NS | MMU_TYPE_PAGE; - -static u64 pte_flags_64_dma = MMU_EXT_NG | -#ifdef CONFIG_SMP - MMU_EXT_SHARED_64 | -#endif /* CONFIG_SMP */ - MMU_EXT_XN | MMU_EXT_AF | MMU_AP_RW_ALL | - MMU_NS | MMU_TYPE_PAGE; - -static u32 pte_flags_32 = MMU_BUFFERABLE | MMU_CACHEABLE | MMU_EXT_NG | -#ifdef CONFIG_SMP - MMU_EXT_SHARED_32 | MMU_EXT_TEX(1) | -#endif /* CONFIG_SMP */ - MMU_EXT_AP1 | MMU_EXT_AP0 | - MMU_TYPE_SMALL | MMU_TYPE_EXT; - -static u32 pte_flags_32_dma = MMU_EXT_NG | -#ifdef CONFIG_SMP - MMU_EXT_SHARED_32 | MMU_EXT_TEX(1) | -#endif /* CONFIG_SMP */ - MMU_EXT_AP1 | MMU_EXT_AP0 | - MMU_TYPE_SMALL | MMU_TYPE_EXT; - -static inline u32 get_pte_flags_32(bool is_writable) +static void tee_mmu_delete(struct tee_mmu *mmu) { - return is_writable ? pte_flags_32 : pte_flags_32 | MMU_EXT_AP2; -} - -static inline u32 get_pte_flags_32_dma(bool is_writable) -{ - return is_writable ? pte_flags_32_dma : pte_flags_32_dma | MMU_EXT_AP2; -} - -static inline u64 get_pte_flags_64(bool is_writable) -{ - return is_writable ? pte_flags_64 : pte_flags_64 | MMU_AP2_RO; -} - -static inline u64 get_pte_flags_64_dma(bool is_writable) -{ - return is_writable ? pte_flags_64_dma : pte_flags_64_dma | MMU_AP2_RO; -} - -static uintptr_t mmu_table_pointer(const struct tee_mmu *mmu) -{ - if (mmu->l1_table.page) { - return g_ctx.f_lpae ? - (uintptr_t)mmu->l1_l2_table.ptes_64 : - (uintptr_t)mmu->l1_l2_table.ptes_32; - } else { - return g_ctx.f_lpae ? - (uintptr_t)mmu->l2_tables[0].ptes_64 : - (uintptr_t)mmu->l2_tables[0].ptes_32; - } -} - -static void mmu_release(struct tee_mmu *mmu) -{ - size_t t; + unsigned long chunk, nr_pages_left = mmu->nr_pages; #ifdef CONFIG_DMA_SHARED_BUFFER if (mmu->dma_buf) { @@ -264,73 +212,129 @@ static void mmu_release(struct tee_mmu *mmu) #endif /* Release all locked user space pages */ - for (t = 0; t < mmu->l2_tables_nr; t++) { - union l2_table *l2_table = &mmu->l2_tables[t]; + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + union mmu_table *pte_table = &mmu->pte_tables[chunk]; + unsigned long nr_pages = nr_pages_left; - if (!l2_table->page) + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + nr_pages_left -= nr_pages; + + if (!pte_table->page) break; - if (mmu->user) { - u64 *pte64 = l2_table->ptes_64; - u32 *pte32 = l2_table->ptes_32; + if (mmu->user && mmu->use_pages_and_vas) { + struct page **page = pte_table->pages; + int i; + + for (i = 0; i < nr_pages; i++, page++) + put_page(*page); + + mmu->pages_locked -= nr_pages; + } else if (mmu->user) { + u64 *pte64 = pte_table->entries; pte_t pte; int i; - for (i = 0; i < L2_ENTRIES_MAX; i++) { + for (i = 0; i < nr_pages; i++) { #if (KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE) || defined(CONFIG_ARM) { - if (g_ctx.f_lpae) - pte = *pte64++; - else - pte = *pte32++; + pte = *pte64++; + /* Unused entries are 0 */ + if (!pte) + break; } - - /* Unused entries are 0 */ - if (!pte) - break; #else { - if (g_ctx.f_lpae) - pte.pte = *pte64++; - else - pte.pte = *pte32++; + pte.pte = *pte64++; + /* Unused entries are 0 */ + if (!pte.pte) + break; } - - /* Unused entries are 0 */ - if (!pte.pte) - break; #endif /* pte_page() cannot return NULL */ put_page(pte_page(pte)); - mmu->pages_locked--; } + + mmu->pages_locked -= nr_pages; } - free_page(l2_table->page); + free_page(pte_table->page); mmu->pages_created--; } - if (mmu->l1_l2_table.page) { - free_page(mmu->l1_l2_table.page); - mmu->pages_created--; - } - - if (mmu->l1_table.page) { - free_page(mmu->l1_table.page); + if (mmu->pmd_table.page) { + free_page(mmu->pmd_table.page); mmu->pages_created--; } if (mmu->pages_created || mmu->pages_locked) - mc_dev_err("leak detected: still in use %d, still locked %d", + mc_dev_err(-EUCLEAN, + "leak detected: still in use %d, still locked %d", mmu->pages_created, mmu->pages_locked); + if (mmu->deleter) + mmu->deleter->delete(mmu->deleter->object); + kfree(mmu); /* Decrement debug counter */ atomic_dec(&g_ctx.c_mmus); } +static struct tee_mmu *tee_mmu_create_common(const struct mcp_buffer_map *b_map) +{ + struct tee_mmu *mmu; + int ret = -ENOMEM; + + if (b_map->nr_pages > (PMD_ENTRIES_MAX * PTE_ENTRIES_MAX)) { + ret = -EINVAL; + mc_dev_err(ret, "data mapping exceeds %d pages: %lu", + PMD_ENTRIES_MAX * PTE_ENTRIES_MAX, b_map->nr_pages); + return ERR_PTR(ret); + } + + /* Allocate the struct */ + mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); + if (!mmu) + return ERR_PTR(-ENOMEM); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_mmus); + kref_init(&mmu->kref); + + /* The Xen front-end does not use PTEs */ + if (is_xen_domu()) + mmu->use_pages_and_vas = true; + + /* Buffer info */ + mmu->offset = b_map->offset; + mmu->length = b_map->length; + mmu->flags = b_map->flags; + + /* Pages info */ + mmu->nr_pages = b_map->nr_pages; + mmu->nr_pmd_entries = (mmu->nr_pages + PTE_ENTRIES_MAX - 1) / + PTE_ENTRIES_MAX; + mc_dev_devel("mmu->nr_pages %lu num_ptes_pages %zu", + mmu->nr_pages, mmu->nr_pmd_entries); + + /* Allocate a page for the L1 table, always used for DomU */ + mmu->pmd_table.page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pmd_table.page) + goto end; + + mmu->pages_created++; + + return mmu; + +end: + tee_mmu_delete(mmu); + return ERR_PTR(ret); +} + static bool mmu_get_dma_buffer(struct tee_mmu *mmu, int va) { #ifdef CONFIG_DMA_SHARED_BUFFER @@ -370,17 +374,20 @@ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, struct tee_mmu *mmu; const void *data = (const void *)(uintptr_t)buf->va; const void *reader = (const void *)((uintptr_t)data & PAGE_MASK); - struct page **pages; /* Same as above, conveniently typed */ + struct page **pages; /* Same as below, conveniently typed */ unsigned long pages_page = 0; /* Page to contain the page pointers */ - size_t chunk; - unsigned long total_pages_nr; - int l1_entries_max; + unsigned long chunk; + struct mcp_buffer_map b_map = { + .offset = (u32)(buf->va & ~PAGE_MASK), + .length = buf->len, + .flags = buf->flags, + }; + bool writeable = buf->flags & MC_IO_MAP_OUTPUT; int ret = 0; - int write = (buf->flags & MC_IO_MAP_OUTPUT) != 0; #ifndef CONFIG_DMA_SHARED_BUFFER if (buf->flags & MMU_ION_BUF) { - mc_dev_err("ION buffers not supported by kernel"); + mc_dev_err(-EINVAL, "ION buffers not supported by kernel"); return ERR_PTR(-EINVAL); } #endif @@ -389,51 +396,32 @@ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, if (!(buf->flags & MMU_ION_BUF) && !buf->va) return ERR_PTR(-EINVAL); + if (buf->flags & MMU_ION_BUF) + /* buf->va is not a valid address. ION buffers are aligned */ + b_map.offset = 0; + /* Allocate the struct */ - mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); - if (!mmu) - return ERR_PTR(-ENOMEM); + b_map.nr_pages = PAGE_ALIGN(b_map.offset + b_map.length) / PAGE_SIZE; + /* Allow Registered Shared mem with valid pointer and zero size. */ + if (!b_map.nr_pages) + b_map.nr_pages = 1; - /* Increment debug counter */ - atomic_inc(&g_ctx.c_mmus); + mmu = tee_mmu_create_common(&b_map); + if (IS_ERR(mmu)) + return mmu; - /* Check that we have enough space to map data */ - mmu->length = buf->len; if (buf->flags & MMU_ION_BUF) { + mc_dev_devel("Buffer is ION"); /* Buffer is ION - * va is the client's dma_buf fd, which should be converted * to a struct sg_table * directly. */ if (!mmu_get_dma_buffer(mmu, buf->va)) { - mc_dev_err("mmu_get_dma_buffer failed"); + mc_dev_err(ret, "mmu_get_dma_buffer failed"); ret = -EINVAL; goto end; } - - mmu->offset = (u32)(mmu->sgt->sgl->offset & ~PAGE_MASK); - } else { - mmu->offset = (u32)((uintptr_t)data & ~PAGE_MASK); } - - total_pages_nr = PAGE_ALIGN(mmu->offset + mmu->length) / PAGE_SIZE; - if (g_ctx.f_mem_ext) - l1_entries_max = L1_ENTRIES_MAX; - else - l1_entries_max = 1; - - if (total_pages_nr > (l1_entries_max * L2_ENTRIES_MAX)) { - mc_dev_err("data mapping exceeds %d pages", - l1_entries_max * L2_ENTRIES_MAX); - ret = -EINVAL; - goto end; - } - - /* Get number of L2 tables needed */ - mmu->l2_tables_nr = (total_pages_nr + L2_ENTRIES_MAX - 1) / - L2_ENTRIES_MAX; - mc_dev_devel("total_pages_nr %lu l2_tables_nr %zu", - total_pages_nr, mmu->l2_tables_nr); - /* Get a page to store page pointers */ pages_page = get_zeroed_page(GFP_KERNEL); if (!pages_page) { @@ -443,73 +431,37 @@ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, mmu->pages_created++; pages = (struct page **)pages_page; - - /* Allocate a page for the L1 table */ - if (mmu->l2_tables_nr > 1) { - mmu->l1_table.page = get_zeroed_page(GFP_KERNEL); - if (!mmu->l1_table.page) { - ret = -ENOMEM; - goto end; - } - mmu->pages_created++; - - mmu->l1_l2_table.page = get_zeroed_page(GFP_KERNEL); - if (!mmu->l1_l2_table.page) { - ret = -ENOMEM; - goto end; - } - mmu->pages_created++; - - /* Map it */ - if (g_ctx.f_lpae) { - u64 *pte; - - pte = &mmu->l1_l2_table.ptes_64[0]; - *pte = virt_to_phys(mmu->l1_table.pages_phys); - *pte |= get_pte_flags_64(write); - } else { - u32 *pte; - - pte = &mmu->l1_l2_table.ptes_32[0]; - *pte = virt_to_phys(mmu->l1_table.pages_phys); - *pte |= get_pte_flags_32(write); - } - } - - for (chunk = 0; chunk < mmu->l2_tables_nr; chunk++) { - unsigned long pages_nr, i; - struct page **page_ptr; + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + unsigned long nr_pages; + int i; /* Size to map for this chunk */ - if (chunk == (mmu->l2_tables_nr - 1)) - pages_nr = ((total_pages_nr - 1) % L2_ENTRIES_MAX) + 1; + if (chunk == (mmu->nr_pmd_entries - 1)) + nr_pages = ((mmu->nr_pages - 1) % PTE_ENTRIES_MAX) + 1; else - pages_nr = L2_ENTRIES_MAX; + nr_pages = PTE_ENTRIES_MAX; - /* Allocate a page for the MMU descriptor */ - mmu->l2_tables[chunk].page = get_zeroed_page(GFP_KERNEL); - if (!mmu->l2_tables[chunk].page) { + /* Allocate a page to hold ptes that describe buffer pages */ + mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pte_tables[chunk].page) { ret = -ENOMEM; goto end; } mmu->pages_created++; - /* Add page address to L1 table if needed */ - if (mmu->l1_table.page) { - void *table; - - if (g_ctx.f_lpae) - table = mmu->l2_tables[chunk].ptes_64; - else - table = mmu->l2_tables[chunk].ptes_32; - - mmu->l1_table.pages_phys[chunk] = virt_to_phys(table); - } + /* Add page address to pmd table if needed */ + if (mmu->use_pages_and_vas) + mmu->pmd_table.vas[chunk] = + mmu->pte_tables[chunk].page; + else + mmu->pmd_table.entries[chunk] = + virt_to_phys(mmu->pte_tables[chunk].addr); /* Get pages */ if (mmu->dma_buf) { /* Buffer is ION */ struct sg_mapping_iter miter; + struct page **page_ptr; page_ptr = &pages[0]; sg_miter_start(&miter, mmu->sgt->sgl, @@ -524,96 +476,79 @@ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, /* Buffer was allocated in user space */ down_read(&mm->mmap_sem); + /* + * Always try to map read/write from a Linux PoV, so + * Linux creates (page faults) the underlying pages if + * missing. + */ gup_ret = gup_local_repeat(mm, (uintptr_t)reader, - pages_nr, 1, pages); - if ((gup_ret == -EFAULT) && !write) { + nr_pages, 1, pages); + if ((gup_ret == -EFAULT) && !writeable) { + /* + * If mapping read/write fails, and the buffer + * is to be shared as input only, try to map + * again read-only. + */ gup_ret = gup_local_repeat(mm, (uintptr_t)reader, - pages_nr, 0, pages); + nr_pages, 0, pages); } up_read(&mm->mmap_sem); if (gup_ret < 0) { ret = gup_ret; - mc_dev_err("failed to get user pages @%p: %d", - reader, ret); + mc_dev_err(ret, "failed to get user pages @%p", + reader); goto end; } /* check if we could lock all pages. */ - if (gup_ret != pages_nr) { - mc_dev_err("failed to get user pages: %ld", - gup_ret); + if (gup_ret != nr_pages) { + mc_dev_err((int)gup_ret, + "failed to get user pages"); +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE release_pages(pages, gup_ret, 0); +#else + release_pages(pages, gup_ret); +#endif ret = -EINVAL; goto end; } - reader += pages_nr * PAGE_SIZE; + reader += nr_pages * PAGE_SIZE; mmu->user = true; - mmu->pages_locked += pages_nr; + mmu->pages_locked += nr_pages; } else if (is_vmalloc_addr(data)) { /* Buffer vmalloc'ed in kernel space */ - page_ptr = &pages[0]; - for (i = 0; i < pages_nr; i++) { + for (i = 0; i < nr_pages; i++) { struct page *page = vmalloc_to_page(reader); if (!page) { - mc_dev_err("failed to map address"); ret = -EINVAL; + mc_dev_err(ret, + "failed to map address"); goto end; } - *page_ptr++ = page; + pages[i] = page; reader += PAGE_SIZE; } } else { /* Buffer kmalloc'ed in kernel space */ struct page *page = virt_to_page(reader); - reader += pages_nr * PAGE_SIZE; - page_ptr = &pages[0]; - for (i = 0; i < pages_nr; i++) - *page_ptr++ = page++; + reader += nr_pages * PAGE_SIZE; + for (i = 0; i < nr_pages; i++) + pages[i] = page++; } - /* Create MMU Table entries */ - page_ptr = &pages[0]; - - /* - * Create MMU table entry, see ARM MMU docu for details about - * flags stored in the lowest 12 bits. As a side reference, the - * Article "ARM's multiply-mapped memory mess" found in the - * collection at http://lwn.net/Articles/409032/ is also worth - * reading. - */ - if (g_ctx.f_lpae) { - u64 *pte = &mmu->l2_tables[chunk].ptes_64[0]; - - for (i = 0; i < pages_nr; i++, page_ptr++, pte++) { - *pte = page_to_phys(*page_ptr); - if (mmu->dma_buf) - *pte |= get_pte_flags_64_dma(write); - else - *pte |= get_pte_flags_64(write); - } + /* Create Table of physical addresses*/ + if (mmu->use_pages_and_vas) { + memcpy(mmu->pte_tables[chunk].pages, pages, + nr_pages * sizeof(*pages)); } else { - u32 *pte = &mmu->l2_tables[chunk].ptes_32[0]; - - for (i = 0; i < pages_nr; i++, page_ptr++, pte++) { - unsigned long phys = page_to_phys(*page_ptr); -#if defined CONFIG_ARM64 - if (phys & 0xffffffff00000000UL) { - mc_dev_err("64-bit pointer: 0x%16lx", - phys); - ret = -EFAULT; - goto end; - } -#endif - *pte = (u32)phys; - if (mmu->dma_buf) - *pte |= get_pte_flags_32_dma(write); - else - *pte |= get_pte_flags_32(write); + for (i = 0; i < nr_pages; i++) { + mmu->pte_tables[chunk].entries[i] = + page_to_phys(pages[i]); } } } @@ -625,67 +560,126 @@ end: } if (ret) { - mmu_release(mmu); + tee_mmu_delete(mmu); return ERR_PTR(ret); } - mc_dev_devel("created mmu %p: %s va %llx len %u off %u L%d table %lx", - mmu, mmu->user ? "user" : "kernel", buf->va, mmu->length, - mmu->offset, mmu->l1_table.page ? 1 : 2, - mmu_table_pointer(mmu)); + mc_dev_devel( + "created mmu %p: %s va %llx len %u off %u flg %x pmd table %lx", + mmu, mmu->user ? "user" : "kernel", buf->va, mmu->length, + mmu->offset, mmu->flags, mmu->pmd_table.page); return mmu; } -void tee_mmu_delete(struct tee_mmu *mmu) +struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages, + const struct mcp_buffer_map *b_map) { - if (!mmu) - return; + int ret = -EINVAL; +#ifdef CONFIG_XEN + struct tee_mmu *mmu; + unsigned long chunk, nr_pages_left; - mc_dev_devel("free mmu %p: %s len %u off %u L%d table %lx", + /* Allocate the struct */ + mmu = tee_mmu_create_common(b_map); + if (IS_ERR(mmu)) + return mmu; + + nr_pages_left = mmu->nr_pages; + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + unsigned long nr_pages = nr_pages_left; + u64 *pte; + int i; + + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + nr_pages_left -= nr_pages; + + /* Allocate a page to hold ptes that describe buffer pages */ + mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pte_tables[chunk].page) { + ret = -ENOMEM; + goto err; + } + mmu->pages_created++; + + /* Add page address to pmd table if needed */ + mmu->pmd_table.entries[chunk] = + virt_to_phys(mmu->pte_tables[chunk].addr); + + /* Convert to PTEs */ + pte = &mmu->pte_tables[chunk].entries[0]; + + for (i = 0; i < nr_pages; i++, pages++, pte++) { + unsigned long phys; + unsigned long pfn; + + phys = page_to_phys(*pages); +#if defined CONFIG_ARM64 + phys &= PHYS_48BIT_MASK; +#endif + pfn = PFN_DOWN(phys); + *pte = __pfn_to_mfn(pfn) << PAGE_SHIFT; + } + } + + mmu->deleter = deleter; + mc_dev_devel("wrapped mmu %p: len %u off %u flg %x pmd table %lx", + mmu, mmu->length, mmu->offset, mmu->flags, + mmu->pmd_table.page); + return mmu; + +err: + tee_mmu_delete(mmu); +#endif + return ERR_PTR(ret); +} + +void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter) +{ + mmu->deleter = deleter; +} + +static void tee_mmu_release(struct kref *kref) +{ + struct tee_mmu *mmu = container_of(kref, struct tee_mmu, kref); + + mc_dev_devel("free mmu %p: %s len %u off %u pmd table %lx", mmu, mmu->user ? "user" : "kernel", mmu->length, - mmu->offset, mmu->l1_table.page ? 1 : 2, - mmu_table_pointer(mmu)); - mmu_release(mmu); + mmu->offset, mmu->pmd_table.page); + tee_mmu_delete(mmu); } -bool client_mmu_matches(const struct tee_mmu *left, - const struct tee_mmu *right) +void tee_mmu_get(struct tee_mmu *mmu) { - const void *left_page = left->l2_tables[0].ptes_32; - const void *right_page = right->l2_tables[0].ptes_32; - bool ret; - - /* L1 not supported */ - if (left->l1_table.page || right->l1_table.page) - return false; - - /* Only need to compare contents of L2 page */ - ret = !memcmp(left_page, right_page, PAGE_SIZE); - mc_dev_devel("MMU tables virt %p and %p %smatch", left, right, - ret ? "" : "do not "); - return ret; + kref_get(&mmu->kref); } -void tee_mmu_buffer(const struct tee_mmu *mmu, struct mcp_buffer_map *map) +void tee_mmu_put(struct tee_mmu *mmu) { - uintptr_t table = mmu_table_pointer(mmu); + kref_put(&mmu->kref, tee_mmu_release); +} + +void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map) +{ + if (mmu->use_pages_and_vas) + map->addr = mmu->pmd_table.page; + else + map->addr = virt_to_phys(mmu->pmd_table.addr); - map->phys_addr = virt_to_phys((void *)table); map->secure_va = 0; map->offset = mmu->offset; map->length = mmu->length; - map->flags = MC_IO_MAP_INPUT | MC_IO_MAP_OUTPUT; - if (mmu->l1_table.page) - map->type = WSM_L1; - else - map->type = WSM_L2; + map->nr_pages = mmu->nr_pages; + map->flags = mmu->flags; + map->type = WSM_L1; + map->mmu = mmu; } int tee_mmu_debug_structs(struct kasnprintf_buf *buf, const struct tee_mmu *mmu) { return kasnprintf(buf, - "\t\t\tmmu %pK: %s len %u off %u table %pK type L%d\n" - , mmu, mmu->user ? "user" : "kernel", mmu->length, - mmu->offset, (void *)mmu_table_pointer(mmu), - mmu->l1_table.page ? 1 : 2); + "\t\t\tmmu %pK: %s len %u off %u table %pK\n", + mmu, mmu->user ? "user" : "kernel", mmu->length, + mmu->offset, (void *)mmu->pmd_table.page); } diff --git a/drivers/gud/MobiCoreDriver/mmu.h b/drivers/gud/MobiCoreDriver/mmu.h index 62cef294d0b3..3a6185411f18 100644 --- a/drivers/gud/MobiCoreDriver/mmu.h +++ b/drivers/gud/MobiCoreDriver/mmu.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -15,9 +16,29 @@ #ifndef _TBASE_MEM_H_ #define _TBASE_MEM_H_ +/* + * This represents the maximum number of entries in a Page Table Entries + * array which maps one 4KiB page. Each entry is 64 bits long physical + * address with some possible flags. With 512 entries it is possible + * to map 2MiB memory block. + */ +#define PTE_ENTRIES_MAX 512 + +/* + * This represents the maximum number of entries in a Page Middle Directory + * which maps one 4KiB page. Each entry is a 64 bits physical address that + * points to a PTE. With 512 entries t is possible to map 1GB memory block. + */ +#define PMD_ENTRIES_MAX 512 + struct tee_mmu; struct mcp_buffer_map; +struct tee_deleter { + void *object; + void (*delete)(void *object); +}; + /* * Allocate MMU table and map buffer into it. * That is, create respective table entries. @@ -26,20 +47,31 @@ struct tee_mmu *tee_mmu_create(struct mm_struct *mm, const struct mc_ioctl_buffer *buf); /* - * Delete a used MMU table. + * Allocate MMU table and map pages into it. + * This is for Xen Dom0 to re-create a buffer with existing pages. */ -void tee_mmu_delete(struct tee_mmu *mmu); +struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages, + const struct mcp_buffer_map *buf); /* - * Compare physical addresses from two MMU tables. + * Give the MMU an object to release when released */ -bool client_mmu_matches(const struct tee_mmu *left, - const struct tee_mmu *right); +void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter); + +/* + * Gets a reference on a MMU table. + */ +void tee_mmu_get(struct tee_mmu *mmu); + +/* + * Puts a reference on a MMU table. + */ +void tee_mmu_put(struct tee_mmu *mmu); /* * Fill in buffer info for MMU table. */ -void tee_mmu_buffer(const struct tee_mmu *mmu, struct mcp_buffer_map *map); +void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map); /* * Add info to debug buffer. diff --git a/drivers/gud/MobiCoreDriver/nq.c b/drivers/gud/MobiCoreDriver/nq.c index 122f678a05ea..9c118d61d8c5 100644 --- a/drivers/gud/MobiCoreDriver/nq.c +++ b/drivers/gud/MobiCoreDriver/nq.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -19,23 +20,32 @@ #include #include #include +#include #include +#include #if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE #include /* local_clock */ #endif +#include "platform.h" /* CPU-related information */ + #include "public/mc_user.h" #include "mci/mcifc.h" #include "mci/mciiwp.h" #include "mci/mcimcp.h" #include "mci/mcinq.h" -#include "mci/mcitime.h" /* struct mcp_time */ +#include "mci/mcitime.h" /* struct mcp_time */ + #include "main.h" +#include "clock.h" #include "fastcall.h" +#include "logging.h" #include "nq.h" #define NQ_NUM_ELEMS 64 +#define SCHEDULING_FREQ 5 /**< N-SIQ every n-th time */ +#define DEFAULT_TIMEOUT_MS 20000 /* We do nothing on timeout anyway */ static struct { struct mutex buffer_mutex; /* Lock on SWd communication buffer */ @@ -43,16 +53,15 @@ static struct { struct interworld_session *iwp_buffer; struct task_struct *irq_bh_thread; struct completion irq_bh_complete; - bool irq_bh_active; + bool irq_bh_thread_run; int irq; - int (*scheduler_cb)(enum nq_scheduler_commands); - void (*crash_handler_cb)(void); + struct blocking_notifier_head tee_stop_notifiers; void (*mcp_notif_handler)(u32 id, u32 payload); void (*iwp_notif_handler)(u32 id, u32 payload); /* MobiCore MCI information */ unsigned int order; union { - void *base; + void *mci; struct { struct notification_queue *tx; struct notification_queue *rx; @@ -66,9 +75,35 @@ static struct { struct mutex notifications_mutex; struct list_head notifications; /* Dump buffer */ + char *tee_version; struct kasnprintf_buf dump; /* Time */ struct mcp_time *time; + + /* Scheduler */ + struct task_struct *tee_scheduler_thread; + bool tee_scheduler_run; + bool tee_hung; + int boot_ret; + struct completion boot_complete; /* Signal end of boot */ + struct completion idle_complete; /* Unblock scheduler thread */ + struct completion sleep_complete; /* Wait for sleep status */ + struct mutex sleep_mutex; /* Protect sleep request */ + struct mutex request_mutex; /* Protect all below */ + /* The order of this enum matters */ + enum sched_command { + NONE, /* No specific request */ + YIELD, /* Run the SWd */ + NSIQ, /* Schedule the SWd */ + SUSPEND, /* Suspend the SWd */ + RESUME, /* Resume the SWd */ + } request; + bool suspended; + + /* Logging */ + phys_addr_t log_buffer; + u32 log_buffer_size; + bool log_buffer_busy; } l_ctx; static inline bool is_iwp_id(u32 id) @@ -76,6 +111,15 @@ static inline bool is_iwp_id(u32 id) return (id & SID_IWP_NOTIFICATION) != 0; } +static inline void session_state_update_internal(struct nq_session *session, + enum nq_notif_state state) +{ + mutex_lock(&session->mutex); + session->state = state; + session->cpu_clk = local_clock(); + mutex_unlock(&session->mutex); +} + /* * Notification Queue overflow management: * - once the SWd NQ is full, sessions get added to the overflow queue: @@ -112,7 +156,17 @@ static inline void notif_queue_push(u32 session_id, u32 payload) rmb(); } -static inline bool nq_notifications_flush_nolock(void) +static void retrieve_last_session_payload(u32 *session_id, u32 *payload) +{ + struct notification_queue_header *hdr = &l_ctx.nq.tx->hdr; + u32 i = (hdr->write_cnt - 1) % hdr->queue_size; + + *session_id = l_ctx.nq.tx->notification[i].session_id; + *payload = l_ctx.nq.tx->notification[i].payload; +} + +/* Must be called with l_ctx.notifications_mutex taken */ +static inline bool nq_notifications_flush(void) { bool flushed = false; @@ -123,7 +177,7 @@ static inline bool nq_notifications_flush_nolock(void) struct nq_session, list); mc_dev_devel("pop %x", session->id); notif_queue_push(session->id, session->payload); - nq_session_state_update(session, NQ_NOTIF_SENT); + session_state_update_internal(session, NQ_NOTIF_SENT); list_del_init(&session->list); flushed = true; } @@ -131,91 +185,31 @@ static inline bool nq_notifications_flush_nolock(void) return flushed; } -bool nq_notifications_flush(void) +static int nq_scheduler_command(enum sched_command command) { - bool flushed = false; + if (IS_ERR_OR_NULL(l_ctx.tee_scheduler_thread)) + return -EFAULT; - mutex_lock(&l_ctx.notifications_mutex); - flushed = nq_notifications_flush_nolock(); - mutex_unlock(&l_ctx.notifications_mutex); - return flushed; -} - -int nq_session_notify(struct nq_session *session, u32 id, u32 payload) -{ - int ret = 0; - - if (!l_ctx.scheduler_cb) - return -EAGAIN; - - mutex_lock(&l_ctx.notifications_mutex); - session->id = id; - session->payload = payload; - if (!list_empty(&l_ctx.notifications) || notif_queue_full()) { - if (!list_empty(&session->list)) { - if (payload != session->payload) { - mc_dev_err("skip %x payload change %x -> %x", - session->id, session->payload, - payload); - } else { - mc_dev_devel("skip %x payload %x", - session->id, payload); - } - ret = -EAGAIN; - } else { - mc_dev_devel("push %x payload %x", session->id, - payload); - /* session->payload = payload; */ - list_add_tail(&session->list, &l_ctx.notifications); - nq_session_state_update(session, NQ_NOTIF_QUEUED); - } - - nq_notifications_flush_nolock(); - - if (l_ctx.scheduler_cb(MC_NQ_YIELD)) - ret = -EPROTO; - } else { - mc_dev_devel("send %x payload %x", session->id, payload); - notif_queue_push(session->id, payload); - nq_session_state_update(session, NQ_NOTIF_SENT); - if (l_ctx.scheduler_cb(MC_NQ_NSIQ)) - ret = -EPROTO; + mutex_lock(&l_ctx.request_mutex); + if (l_ctx.request < command) { + l_ctx.request = command; + complete(&l_ctx.idle_complete); } - mutex_unlock(&l_ctx.notifications_mutex); - return ret; + mutex_unlock(&l_ctx.request_mutex); + return 0; } -void nq_update_time(void) +static inline void nq_update_time(void) { struct timespec tm; getnstimeofday(&tm); l_ctx.time->wall_clock_seconds = tm.tv_sec; l_ctx.time->wall_clock_nsec = tm.tv_nsec; - if (g_ctx.f_monotonic_time) { - getrawmonotonic(&tm); - l_ctx.time->monotonic_seconds = tm.tv_sec; - l_ctx.time->monotonic_nsec = tm.tv_nsec; - } -} - -union mcp_message *nq_get_mcp_message(void) -{ - return &l_ctx.mcp_buffer->message; -} - -struct interworld_session *nq_get_iwp_buffer(void) -{ - return l_ctx.iwp_buffer; -} - -void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp) -{ - if (iwp) - l_ctx.iwp_notif_handler = handler; - else - l_ctx.mcp_notif_handler = handler; + getrawmonotonic(&tm); + l_ctx.time->monotonic_seconds = tm.tv_sec; + l_ctx.time->monotonic_nsec = tm.tv_nsec; } static inline void nq_notif_handler(u32 id, u32 payload) @@ -231,9 +225,13 @@ static int irq_bh_worker(void *arg) { struct notification_queue *rx = l_ctx.nq.rx; - while (l_ctx.irq_bh_active) { + while (1) { wait_for_completion_killable(&l_ctx.irq_bh_complete); + /* This thread can only be stopped with nq_stop */ + if (!l_ctx.irq_bh_thread_run) + break; + /* Deal with all pending notifications in one go */ while ((rx->hdr.write_cnt - rx->hdr.read_cnt) > 0) { struct notification nf; @@ -262,8 +260,7 @@ static int irq_bh_worker(void *arg) * In this case the S-SIQ tells NWd that SWd is no longer idle * an will need scheduling again. */ - if (l_ctx.scheduler_cb) - l_ctx.scheduler_cb(MC_NQ_NSIQ); + nq_scheduler_command(NSIQ); } return 0; } @@ -286,34 +283,71 @@ void nq_session_init(struct nq_session *session, bool is_gp) session->is_gp = is_gp; } -bool nq_session_is_gp(const struct nq_session *session) -{ - return session->is_gp; -} - -u64 nq_session_notif_cpu_clk(const struct nq_session *session) -{ - return session->cpu_clk; -} - void nq_session_exit(struct nq_session *session) { mutex_lock(&l_ctx.notifications_mutex); - list_del(&session->list); + if (!list_empty(&session->list)) + list_del(&session->list); mutex_unlock(&l_ctx.notifications_mutex); } void nq_session_state_update(struct nq_session *session, enum nq_notif_state state) { - mutex_lock(&session->mutex); - session->state = state; - session->cpu_clk = local_clock(); - mutex_unlock(&session->mutex); + if (state < NQ_NOTIF_RECEIVED) + return; + + session_state_update_internal(session, state); } -const char *nq_session_state_string(struct nq_session *session) +int nq_session_notify(struct nq_session *session, u32 id, u32 payload) { + int ret = 0; + + mutex_lock(&l_ctx.notifications_mutex); + session->id = id; + session->payload = payload; + if (!list_empty(&l_ctx.notifications) || notif_queue_full()) { + if (!list_empty(&session->list)) { + ret = -EAGAIN; + if (payload != session->payload) { + mc_dev_err(ret, + "skip %x payload change %x -> %x", + session->id, session->payload, + payload); + } else { + mc_dev_devel("skip %x payload %x", + session->id, payload); + } + } else { + mc_dev_devel("push %x payload %x", session->id, + payload); + /* session->payload = payload; */ + list_add_tail(&session->list, &l_ctx.notifications); + session_state_update_internal(session, NQ_NOTIF_QUEUED); + } + + nq_notifications_flush(); + + if (nq_scheduler_command(YIELD)) + ret = -EPROTO; + } else { + mc_dev_devel("send %x payload %x", session->id, payload); + notif_queue_push(session->id, payload); + session_state_update_internal(session, NQ_NOTIF_SENT); + if (nq_scheduler_command(NSIQ)) + ret = -EPROTO; + } + + mutex_unlock(&l_ctx.notifications_mutex); + return ret; +} + +const char *nq_session_state(const struct nq_session *session, u64 *cpu_clk) +{ + if (cpu_clk) + *cpu_clk = session->cpu_clk; + switch (session->state) { case NQ_NOTIF_IDLE: return "idle"; @@ -341,7 +375,7 @@ static ssize_t debug_crashdump_read(struct file *file, char __user *user_buf, return 0; } -static const struct file_operations mc_debug_crashdump_ops = { +static const struct file_operations debug_crashdump_ops = { .read = debug_crashdump_read, .llseek = default_llseek, }; @@ -353,14 +387,14 @@ static ssize_t debug_smclog_read(struct file *file, char __user *user_buf, mc_fastcall_debug_smclog); } -static const struct file_operations mc_debug_smclog_ops = { +static const struct file_operations debug_smclog_ops = { .read = debug_smclog_read, .llseek = default_llseek, .open = debug_generic_open, .release = debug_generic_release, }; -void nq_dump_status(void) +static void nq_dump_status(void) { static const struct { unsigned int index; @@ -406,6 +440,12 @@ void nq_dump_status(void) { MC_EXT_INFO_ID_MC_EXC_IPCMSG, "mcExcep.cause"}, /**< MobiCore exception handler last IPC data */ {MC_EXT_INFO_ID_MC_EXC_IPCDATA, "mcExcep.meta"}, + /**< MobiCore last crashing task offset */ + {MC_EXT_INFO_ID_TASK_OFFSET, + "faultRec.offset.task"}, + /**< MobiCore last crashing task's mcLib offset */ + {MC_EXT_INFO_ID_MCLIB_OFFSET, + "faultRec.offset.mclib"}, }; char uuid_str[33]; @@ -415,16 +455,25 @@ void nq_dump_status(void) if (l_ctx.dump.off) ret = -EBUSY; - mc_dev_err("TEE halted. Status dump:"); + mc_dev_info("TEE HALTED"); + if (l_ctx.tee_version) { + mc_dev_info("TEE version: %s", l_ctx.tee_version); + if (ret >= 0) + ret = kasnprintf(&l_ctx.dump, "TEE version: %s\n", + l_ctx.tee_version); + } + + mc_dev_info("Status dump:"); for (i = 0; i < (size_t)ARRAY_SIZE(status_map); i++) { u32 info; - if (!mc_fc_info(status_map[i].index, NULL, &info)) { - mc_dev_err(" %-20s= 0x%08x", status_map[i].msg, info); - if (ret >= 0) - ret = kasnprintf(&l_ctx.dump, "%-20s= 0x%08x\n", - status_map[i].msg, info); - } + if (fc_info(status_map[i].index, NULL, &info)) + return; + + mc_dev_info(" %-22s= 0x%08x", status_map[i].msg, info); + if (ret >= 0) + ret = kasnprintf(&l_ctx.dump, "%-22s= 0x%08x\n", + status_map[i].msg, info); } /* construct UUID string */ @@ -432,7 +481,7 @@ void nq_dump_status(void) u32 info; size_t j; - if (mc_fc_info(MC_EXT_INFO_ID_MC_EXC_UUID + i, NULL, &info)) + if (fc_info(MC_EXT_INFO_ID_MC_EXC_UUID + i, NULL, &info)) return; for (j = 0; j < sizeof(info); j++) { @@ -441,9 +490,9 @@ void nq_dump_status(void) } } - mc_dev_err(" %-20s= 0x%s", "mcExcep.uuid", uuid_str); + mc_dev_info(" %-22s= 0x%s", "mcExcep.uuid", uuid_str); if (ret >= 0) - ret = kasnprintf(&l_ctx.dump, "%-20s= 0x%s\n", "mcExcep.uuid", + ret = kasnprintf(&l_ctx.dump, "%-22s= 0x%s\n", "mcExcep.uuid", uuid_str); if (ret < 0) { @@ -453,42 +502,29 @@ void nq_dump_status(void) } debugfs_create_file("crashdump", 0400, g_ctx.debug_dir, NULL, - &mc_debug_crashdump_ops); + &debug_crashdump_ops); debugfs_create_file("last_smc_commands", 0400, g_ctx.debug_dir, NULL, - &mc_debug_smclog_ops); - if (l_ctx.crash_handler_cb) - l_ctx.crash_handler_cb(); + &debug_smclog_ops); } -void nq_register_scheduler(int (*scheduler_cb)(enum nq_scheduler_commands)) +static void nq_handle_tee_crash(void) { - l_ctx.scheduler_cb = scheduler_cb; + /* + * Do not change the call order: the debugfs nq status file needs + * to be created before requesting the Daemon to read it. + */ + nq_dump_status(); + blocking_notifier_call_chain(&l_ctx.tee_stop_notifiers, 0, NULL); } -void nq_register_crash_handler(void (*crash_handler_cb)(void)) -{ - l_ctx.crash_handler_cb = crash_handler_cb; -} - -static inline int set_sleep_mode_rq(u16 sleep_req) +static inline void set_sleep_mode_rq(u16 sleep_req) { mutex_lock(&l_ctx.buffer_mutex); l_ctx.mcp_buffer->flags.sleep_mode.sleep_req = sleep_req; mutex_unlock(&l_ctx.buffer_mutex); - return 0; } -int nq_suspend(void) -{ - return set_sleep_mode_rq(MC_FLAG_REQ_TO_SLEEP); -} - -int nq_resume(void) -{ - return set_sleep_mode_rq(MC_FLAG_NO_SLEEP_REQ); -} - -bool nq_suspended(void) +static inline bool nq_suspended(void) { struct mcp_flags *flags = &l_ctx.mcp_buffer->flags; bool ret; @@ -506,7 +542,14 @@ bool nq_suspended(void) return ret; } -bool nq_get_idle_timeout(s32 *timeout) +/* + * Get the requested SWd sleep timeout value (ms) + * - if the timeout is -1, wait indefinitely + * - if the timeout is 0, re-schedule immediately (timeouts in µs in the SWd) + * - otherwise sleep for the required time + * returns true if sleep is required, false otherwise + */ +static inline bool nq_get_idle_timeout(s32 *timeout) { u32 schedule; bool ret; @@ -514,11 +557,7 @@ bool nq_get_idle_timeout(s32 *timeout) mutex_lock(&l_ctx.buffer_mutex); schedule = l_ctx.mcp_buffer->flags.schedule; if (schedule == MC_FLAG_SCHEDULE_IDLE) { - if (g_ctx.f_timeout) - *timeout = l_ctx.mcp_buffer->flags.timeout_ms; - else - *timeout = -1; - + *timeout = l_ctx.mcp_buffer->flags.timeout_ms; ret = true; } else { ret = false; @@ -528,48 +567,109 @@ bool nq_get_idle_timeout(s32 *timeout) return ret; } -void nq_reset_idle_timeout(void) +union mcp_message *nq_get_mcp_buffer(void) { - mutex_lock(&l_ctx.buffer_mutex); - l_ctx.mcp_buffer->flags.timeout_ms = -1; - mutex_unlock(&l_ctx.buffer_mutex); + return &l_ctx.mcp_buffer->message; } -int nq_start(void) +struct interworld_session *nq_get_iwp_buffer(void) +{ + return l_ctx.iwp_buffer; +} + +void nq_set_version_ptr(char *version) +{ + l_ctx.tee_version = version; +} + +void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp) +{ + if (iwp) + l_ctx.iwp_notif_handler = handler; + else + l_ctx.mcp_notif_handler = handler; +} + +int nq_register_tee_stop_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&l_ctx.tee_stop_notifiers, nb); +} + +int nq_unregister_tee_stop_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&l_ctx.tee_stop_notifiers, + nb); +} + +ssize_t nq_get_stop_message(char __user *buffer, size_t size) +{ + size_t max_len = l_ctx.dump.size - l_ctx.dump.off; + char *buf = l_ctx.dump.buf; + int ret; + + if (!l_ctx.dump.off || !max_len) + return 0; + + if (size > max_len) + size = max_len; + + ret = copy_to_user(buffer, buf, size); + if (ret) + return -EFAULT; + + return size; +} + +void nq_signal_tee_hung(void) +{ + mc_dev_devel("force stop the notification queue"); + /* Stop the tee_scheduler thread */ + l_ctx.tee_hung = true; + l_ctx.tee_scheduler_run = false; + complete(&l_ctx.idle_complete); + nq_scheduler_command(NONE); +} + +static int nq_scheduler_pm_command(enum sched_command command) +{ + int ret = -EPERM; + + if (IS_ERR_OR_NULL(l_ctx.tee_scheduler_thread)) + return -EFAULT; + + mutex_lock(&l_ctx.sleep_mutex); + + /* Send request */ + nq_scheduler_command(command); + + /* Wait for scheduler to reply */ + wait_for_completion(&l_ctx.sleep_complete); + mutex_lock(&l_ctx.request_mutex); + if (command == SUSPEND) { + if (l_ctx.suspended) + ret = 0; + } else { + if (!l_ctx.suspended) + ret = 0; + } + + mutex_unlock(&l_ctx.request_mutex); + mutex_unlock(&l_ctx.sleep_mutex); + return ret; +} + +static int nq_boot_tee(void) { size_t q_len = ALIGN(2 * (sizeof(struct notification_queue_header) + NQ_NUM_ELEMS * sizeof(struct notification)), 4); + struct irq_data *irq_d = irq_get_irq_data(l_ctx.irq); int ret; - struct irq_data *irq_d; - - /* Make sure we have an interrupt number before going on */ -#if defined(CONFIG_OF) - l_ctx.irq = irq_of_parse_and_map(g_ctx.mcd->of_node, 0); -#endif -#if defined(MC_INTR_SSIQ) - if (l_ctx.irq <= 0) - l_ctx.irq = MC_INTR_SSIQ; -#endif - - if (l_ctx.irq <= 0) { - mc_dev_err("No IRQ number, aborting"); - return -EINVAL; - } - - /* - * Initialize the time structure for SWd - * At this stage, we don't know if the SWd needs to get the REE time and - * we set it anyway. - */ - nq_update_time(); /* Call the INIT fastcall to setup shared buffers */ - ret = mc_fc_init( - virt_to_phys(l_ctx.base), - (uintptr_t)l_ctx.mcp_buffer - (uintptr_t)l_ctx.base, - q_len, - sizeof(*l_ctx.mcp_buffer)); - + ret = fc_init(virt_to_phys(l_ctx.mci), + (uintptr_t)l_ctx.mcp_buffer - (uintptr_t)l_ctx.mci, q_len, + sizeof(*l_ctx.mcp_buffer)); + logging_run(); if (ret) return ret; @@ -579,22 +679,22 @@ int nq_start(void) l_ctx.mcp_buffer->message.init_values.irq = MC_INTR_SSIQ_SWD; #endif l_ctx.mcp_buffer->message.init_values.flags |= MC_IV_FLAG_TIME; - irq_d = irq_get_irq_data(l_ctx.irq); if (irq_d) l_ctx.mcp_buffer->message.init_values.irq = irq_d->hwirq; l_ctx.mcp_buffer->message.init_values.time_ofs = - (u32)((uintptr_t)l_ctx.time - (uintptr_t)l_ctx.base); + (u32)((uintptr_t)l_ctx.time - (uintptr_t)l_ctx.mci); l_ctx.mcp_buffer->message.init_values.time_len = sizeof(*l_ctx.time); l_ctx.mcp_buffer->message.init_values.flags |= MC_IV_FLAG_IWP; l_ctx.mcp_buffer->message.init_values.iws_buf_ofs = - (u64)((uintptr_t)l_ctx.iwp_buffer - (uintptr_t)l_ctx.base); + (u64)((uintptr_t)l_ctx.iwp_buffer - (uintptr_t)l_ctx.mci); l_ctx.mcp_buffer->message.init_values.iws_buf_size = MAX_IW_SESSION * sizeof(struct interworld_session); /* First empty N-SIQ to setup of the MCI structure */ - ret = mc_fc_nsiq(); + ret = fc_nsiq(0, 0); + logging_run(); if (ret) return ret; @@ -604,9 +704,10 @@ int nq_start(void) */ do { u32 status = 0; - u32 timeslot; + u32 timeslice; - ret = mc_fc_info(MC_EXT_INFO_ID_MCI_VERSION, &status, NULL); + ret = fc_info(MC_EXT_INFO_ID_MCI_VERSION, &status, NULL); + logging_run(); if (ret) return ret; @@ -614,9 +715,10 @@ int nq_start(void) case MC_STATUS_NOT_INITIALIZED: /* Switch to the TEE to give it more CPU time. */ ret = EAGAIN; - for (timeslot = 0; timeslot < 10; timeslot++) { - int tmp_ret = mc_fc_yield(); + for (timeslice = 0; timeslice < 10; timeslice++) { + int tmp_ret = fc_yield(timeslice); + logging_run(); if (tmp_ret) return tmp_ret; } @@ -627,41 +729,323 @@ int nq_start(void) break; case MC_STATUS_HALT: - nq_dump_status(); - mc_dev_err("halt during init, state 0x%x", status); - return -ENODEV; + ret = -ENODEV; + nq_handle_tee_crash(); + mc_dev_err(ret, "halt during init, state 0x%x", status); + return ret; case MC_STATUS_INITIALIZED: mc_dev_devel("ready"); break; default: /* MC_STATUS_BAD_INIT or anything else */ - mc_dev_err("MCI init failed, state 0x%x", status); - return -EIO; + ret = -EIO; + mc_dev_err(ret, "MCI init failed, state 0x%x", status); + return ret; } } while (ret == EAGAIN); - /* Set up S-SIQ interrupt handler and its bottom-half */ - l_ctx.irq_bh_active = true; + return ret; +} + +static inline bool tee_sleep(s32 timeout_ms) +{ + bool infinite_timeout = timeout_ms < 0; + + /* TEE is going to sleep */ + mc_clock_disable(); + do { + s32 local_timeout_ms; + unsigned long jiffies; + + if (infinite_timeout) { + local_timeout_ms = DEFAULT_TIMEOUT_MS; + } else { + local_timeout_ms = timeout_ms; + if (local_timeout_ms > DEFAULT_TIMEOUT_MS) + local_timeout_ms = DEFAULT_TIMEOUT_MS; + } + + jiffies = msecs_to_jiffies(local_timeout_ms); + if (wait_for_completion_timeout(&l_ctx.idle_complete, jiffies)) + break; + + if (!infinite_timeout) + timeout_ms -= local_timeout_ms; + } while (timeout_ms); + + /* TEE is getting back to work */ + mc_clock_enable(); + return timeout_ms == 0; +} + +/* + * This thread, and only this thread, schedules the SWd. Hence, reading the idle + * status and its associated timeout is safe from race conditions. + */ +static int tee_scheduler(void *arg) +{ + bool swd_notify = false; + int ret = 0; + + /* Enable TEE clock */ + mc_clock_enable(); + + /* Logging */ + if (l_ctx.log_buffer_size) { + ret = fc_trace_init(l_ctx.log_buffer, l_ctx.log_buffer_size); + if (!ret) { + logging_run(); + l_ctx.log_buffer_busy = true; + mc_dev_info("registered log buffer of size %d", + l_ctx.log_buffer_size); + } else { + mc_dev_err(ret, "failed to register log buffer"); + /* Ignore error */ + ret = 0; + } + } else { + mc_dev_info("no log buffer to register"); + } + + /* Bootup */ + l_ctx.boot_ret = nq_boot_tee(); + complete(&l_ctx.boot_complete); + if (l_ctx.boot_ret) { + mc_clock_disable(); + return l_ctx.boot_ret; + } + + /* Run */ + while (1) { + s32 timeout_ms = -1; + bool pm_request = false; + u8 tee_flags; + + if (l_ctx.suspended || nq_get_idle_timeout(&timeout_ms)) { + /* If timeout is 0 we keep scheduling the SWd */ + if (!timeout_ms) + nq_scheduler_command(NSIQ); + else if (tee_sleep(timeout_ms)) + /* Timed out, force SWd schedule */ + nq_scheduler_command(NSIQ); + } + + /* + * Potential exit causes: + * 1) nq_stop is called: just stop the thread (no crash dump) + * 2) nq_signal_tee_hung: breaks the loop and handle the hang as + * a crash + * 3) The thread detects a TEE crash and breaks the loop + */ + if (!l_ctx.tee_scheduler_run) + break; + + /* Get requested command if any */ + mutex_lock(&l_ctx.request_mutex); + switch (l_ctx.request) { + case NONE: + break; + case YIELD: + swd_notify = false; + break; + case NSIQ: + swd_notify = true; + break; + case SUSPEND: + /* Force N_SIQ */ + swd_notify = true; + set_sleep_mode_rq(MC_FLAG_REQ_TO_SLEEP); + pm_request = true; + break; + case RESUME: + /* Force N_SIQ */ + swd_notify = true; + set_sleep_mode_rq(MC_FLAG_NO_SLEEP_REQ); + pm_request = true; + break; + } + + l_ctx.request = NONE; + nq_update_time(); + mutex_unlock(&l_ctx.request_mutex); + + /* Reset timeout so we don't loop if SWd halted */ + mutex_lock(&l_ctx.buffer_mutex); + l_ctx.mcp_buffer->flags.timeout_ms = -1; + mutex_unlock(&l_ctx.buffer_mutex); + + if (swd_notify) { + u32 session_id = 0; + u32 payload = 0; + + retrieve_last_session_payload(&session_id, &payload); + swd_notify = false; + + /* Call SWd scheduler */ + fc_nsiq(session_id, payload); + } else { + /* Resume SWd from where it was */ + fc_yield(0); + } + + /* Always flush log buffer after the SWd has run */ + logging_run(); + + /* Check crash */ + mutex_lock(&l_ctx.buffer_mutex); + tee_flags = l_ctx.mcp_buffer->flags.tee_flags; + mutex_unlock(&l_ctx.buffer_mutex); + if (tee_flags & MC_STATE_FLAG_TEE_HALT_MASK) { + ret = -EHOSTUNREACH; + mc_dev_err(ret, "TEE halted, exiting"); + break; + } + + /* Should have suspended by now if requested */ + mutex_lock(&l_ctx.request_mutex); + if (pm_request) { + l_ctx.suspended = nq_suspended(); + complete(&l_ctx.sleep_complete); + } + + mutex_unlock(&l_ctx.request_mutex); + + /* Flush pending notifications if possible */ + mutex_lock(&l_ctx.notifications_mutex); + if (nq_notifications_flush()) + complete(&l_ctx.idle_complete); + + mutex_unlock(&l_ctx.notifications_mutex); + } + + mc_dev_devel("loop exit, ret is %d", ret); + if (ret || l_ctx.tee_hung) { + /* There is an error, the tee must have crashed */ + nq_handle_tee_crash(); + } + + /* Logging */ + ret = fc_trace_deinit(); + if (!ret) + l_ctx.log_buffer_busy = false; + else + mc_dev_err(ret, "failed to unregister log buffer"); + + mc_clock_disable(); + return ret; +} + +int nq_suspend(void) +{ + return nq_scheduler_pm_command(SUSPEND); +} + +int nq_resume(void) +{ + return nq_scheduler_pm_command(RESUME); +} + +int nq_start(void) +{ + int ret; +#if defined(CPU_IDS) + struct cpumask new_mask; + unsigned int cpu_id[] = CPU_IDS; + int i; +#endif + /* Make sure we have the interrupt before going on */ +#if defined(CONFIG_OF) + l_ctx.irq = irq_of_parse_and_map(g_ctx.mcd->of_node, 0); +#endif +#if defined(MC_INTR_SSIQ) + if (l_ctx.irq <= 0) + l_ctx.irq = MC_INTR_SSIQ; +#endif + + if (l_ctx.irq <= 0) { + ret = -EINVAL; + mc_dev_err(ret, "No IRQ number, aborting"); + return ret; + } + + ret = request_irq(l_ctx.irq, irq_handler, IRQF_TRIGGER_RISING, + "trustonic", NULL); + if (ret) + return ret; + + /* + * Initialize the time structure for SWd + * At this stage, we don't know if the SWd needs to get the REE time and + * we set it anyway. + */ + nq_update_time(); + + /* Setup S-SIQ interrupt handler and its bottom-half */ + l_ctx.irq_bh_thread_run = true; l_ctx.irq_bh_thread = kthread_run(irq_bh_worker, NULL, "tee_irq_bh"); if (IS_ERR(l_ctx.irq_bh_thread)) { - mc_dev_err("irq_bh_worker thread creation failed"); - return PTR_ERR(l_ctx.irq_bh_thread); + ret = PTR_ERR(l_ctx.irq_bh_thread); + mc_dev_err(ret, "irq_bh_worker thread creation failed"); + return ret; } - return request_irq(l_ctx.irq, irq_handler, IRQF_TRIGGER_RISING, - "trustonic", NULL); + + /* Scheduler */ + l_ctx.tee_scheduler_run = true; + l_ctx.tee_scheduler_thread = kthread_create(tee_scheduler, NULL, + "tee_scheduler"); + if (IS_ERR(l_ctx.tee_scheduler_thread)) { + ret = PTR_ERR(l_ctx.tee_scheduler_thread); + mc_dev_err(ret, "tee_scheduler thread creation failed"); + return ret; + } +#if defined(CPU_IDS) + cpumask_clear(&new_mask); + for (i = 0; i < NB_CPU; i++) + cpumask_set_cpu(cpu_id[i], &new_mask); + set_cpus_allowed_ptr(l_ctx.tee_scheduler_thread, &new_mask); + mc_dev_info("tee_scheduler running only on %d CPU", NB_CPU); +#endif + + wake_up_process(l_ctx.tee_scheduler_thread); + + wait_for_completion(&l_ctx.boot_complete); + if (l_ctx.boot_ret) + return l_ctx.boot_ret; + + complete(&l_ctx.idle_complete); + return 0; } void nq_stop(void) { - free_irq(l_ctx.irq, NULL); - l_ctx.irq_bh_active = false; + /* Scheduler */ + l_ctx.tee_scheduler_run = false; + complete(&l_ctx.idle_complete); + kthread_stop(l_ctx.tee_scheduler_thread); + + /* NQ */ + l_ctx.irq_bh_thread_run = false; + complete(&l_ctx.irq_bh_complete); kthread_stop(l_ctx.irq_bh_thread); + free_irq(l_ctx.irq, NULL); } int nq_init(void) { - size_t q_len, buf_len; + size_t q_len, mci_len; unsigned long mci; + int ret; + + ret = mc_clock_init(); + if (ret) + goto err_clock; + + ret = logging_init(&l_ctx.log_buffer, &l_ctx.log_buffer_size); + if (ret) + goto err_logging; + + /* Setup crash handler function list */ + BLOCKING_INIT_NOTIFIER_HEAD(&l_ctx.tee_stop_notifiers); mutex_init(&l_ctx.buffer_mutex); init_completion(&l_ctx.irq_bh_complete); @@ -673,16 +1057,16 @@ int nq_init(void) q_len = ALIGN(2 * (sizeof(struct notification_queue_header) + NQ_NUM_ELEMS * sizeof(struct notification)), 4); - buf_len = q_len + + mci_len = q_len + sizeof(*l_ctx.time) + sizeof(*l_ctx.mcp_buffer) + MAX_IW_SESSION * sizeof(struct interworld_session); - l_ctx.order = get_order(buf_len); + l_ctx.order = get_order(mci_len); mci = __get_free_pages(GFP_USER | __GFP_ZERO, l_ctx.order); if (!mci) - return -ENOMEM; + goto err_mci; l_ctx.nq.tx = (struct notification_queue *)mci; l_ctx.nq.tx->hdr.queue_size = NQ_NUM_ELEMS; @@ -705,18 +1089,32 @@ int nq_init(void) */ /* mci should be already 8 bytes aligned */ l_ctx.iwp_buffer = (void *)ALIGN(mci, 8); - if ((unsigned long)l_ctx.iwp_buffer != mci) - mc_dev_err("ERROR: iws buffer is not aligned"); - mci += MAX_IW_SESSION * sizeof(struct interworld_session); l_ctx.time = (void *)ALIGN(mci, 8); + + /* Scheduler */ + init_completion(&l_ctx.boot_complete); + init_completion(&l_ctx.idle_complete); + init_completion(&l_ctx.sleep_complete); + mutex_init(&l_ctx.sleep_mutex); + mutex_init(&l_ctx.request_mutex); return 0; + +err_mci: + logging_exit(l_ctx.log_buffer_busy); +err_logging: + mc_clock_exit(); +err_clock: + return ret; } void nq_exit(void) { if (l_ctx.dump.off) kfree(l_ctx.dump.buf); - free_pages((unsigned long)l_ctx.base, l_ctx.order); + + free_pages((unsigned long)l_ctx.mci, l_ctx.order); + logging_exit(l_ctx.log_buffer_busy); + mc_clock_exit(); } diff --git a/drivers/gud/MobiCoreDriver/nq.h b/drivers/gud/MobiCoreDriver/nq.h index 115c044bd6c1..dbd4062eae61 100644 --- a/drivers/gud/MobiCoreDriver/nq.h +++ b/drivers/gud/MobiCoreDriver/nq.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -17,8 +18,21 @@ #include #include +#include -/* FIXME to be in .c file, renamed */ +/** Max number of interworld session allocated in MCI buffer */ +#define MAX_IW_SESSION 256 + +enum nq_notif_state { + NQ_NOTIF_IDLE, /* Nothing happened yet */ + NQ_NOTIF_QUEUED, /* Notification in overflow queue */ + NQ_NOTIF_SENT, /* Notification in send queue */ + NQ_NOTIF_RECEIVED, /* Notification received */ + NQ_NOTIF_CONSUMED, /* Notification reported to CA */ + NQ_NOTIF_DEAD, /* Error reported to CA */ +}; + +/* FIXME to be renamed */ struct nq_session { /* Notification id */ u32 id; @@ -29,68 +43,41 @@ struct nq_session { /* Notification debug mutex */ struct mutex mutex; /* Current notification/session state */ - enum nq_notif_state { - NQ_NOTIF_IDLE, /* Nothing happened yet */ - NQ_NOTIF_QUEUED, /* Notification in overflow queue */ - NQ_NOTIF_SENT, /* Notification in send queue */ - NQ_NOTIF_RECEIVED, /* Notification received */ - NQ_NOTIF_CONSUMED, /* Notification reported to CA */ - NQ_NOTIF_DEAD, /* Error reported to CA */ - } state; + enum nq_notif_state state; /* Time at notification state change */ u64 cpu_clk; /* This TA is of Global Platform type, set by upper layer */ - bool is_gp; + int is_gp; }; -/* Initialisation/cleanup */ -int nq_init(void); -void nq_exit(void); +/* Notification queue channel */ +void nq_session_init(struct nq_session *session, bool is_gp); +void nq_session_exit(struct nq_session *session); +void nq_session_state_update(struct nq_session *session, + enum nq_notif_state state); +int nq_session_notify(struct nq_session *session, u32 id, u32 payload); +const char *nq_session_state(const struct nq_session *session, u64 *cpu_clk); + +/* Services */ +union mcp_message *nq_get_mcp_buffer(void); +struct interworld_session *nq_get_iwp_buffer(void); +void nq_set_version_ptr(char *version); +void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp); +int nq_register_tee_stop_notifier(struct notifier_block *nb); +int nq_unregister_tee_stop_notifier(struct notifier_block *nb); +ssize_t nq_get_stop_message(char __user *buffer, size_t size); +void nq_signal_tee_hung(void); + +/* SWd suspend/resume */ +int nq_suspend(void); +int nq_resume(void); /* Start/stop TEE */ int nq_start(void); void nq_stop(void); -/* SWd suspend/resume */ -int nq_suspend(void); -int nq_resume(void); -bool nq_suspended(void); - -/* - * Get the requested SWd sleep timeout value (ms) - * - if the timeout is -1, wait indefinitely - * - if the timeout is 0, re-schedule immediately (timeouts in µs in the SWd) - * - otherwise sleep for the required time - * returns true if sleep is required, false otherwise - */ -bool nq_get_idle_timeout(s32 *timeout); -void nq_reset_idle_timeout(void); - -/* Services */ -/* Callback to scheduler registration */ -enum nq_scheduler_commands { - MC_NQ_YIELD, - MC_NQ_NSIQ, -}; - -void nq_register_scheduler(int (*scheduler_cb)(enum nq_scheduler_commands)); -void nq_register_crash_handler(void (*crashhandler_cb)(void)); -void nq_dump_status(void); -void nq_update_time(void); -/* Accessors for MCP/IWP contexts */ -union mcp_message *nq_get_mcp_message(void); -struct interworld_session *nq_get_iwp_buffer(void); -void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp); - -/* Notifications */ -void nq_session_init(struct nq_session *session, bool is_gp); -bool nq_session_is_gp(const struct nq_session *session); -u64 nq_session_notif_cpu_clk(const struct nq_session *session); -void nq_session_exit(struct nq_session *session); -void nq_session_state_update(struct nq_session *session, - enum nq_notif_state state); -const char *nq_session_state_string(struct nq_session *session); -int nq_session_notify(struct nq_session *session, u32 id, u32 payload); -bool nq_notifications_flush(void); +/* Initialisation/cleanup */ +int nq_init(void); +void nq_exit(void); #endif /* _MC_NQ_H_ */ diff --git a/drivers/gud/MobiCoreDriver/platform.h b/drivers/gud/MobiCoreDriver/platform.h index e2981b321569..baba44516adc 100644 --- a/drivers/gud/MobiCoreDriver/platform.h +++ b/drivers/gud/MobiCoreDriver/platform.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * Copyright (c) 2013-2020 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -14,23 +15,29 @@ #ifndef _MC_PLATFORM_H_ #define _MC_PLATFORM_H_ -/* MobiCore Interrupt for Qualcomm (DT IRQ has priority if present) */ -#define MC_INTR_SSIQ 280 - -/* Use SMC for fastcalls */ -#define MC_SMC_FASTCALL - -#include - -/*--------------- Implementation -------------- */ -#include -#include - +#include #include #include #include #include #include +#include + +#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE +#define USE_SHM_BRIDGE +#endif + +#include +#include +#if defined USE_SHM_BRIDGE +#include +#endif +/*--------------- Implementation -------------- */ +/* MobiCore Interrupt for Qualcomm (DT IRQ has priority if present) */ +#define MC_INTR_SSIQ 280 + +/* Use SMC for fastcalls */ +#define MC_SMC_FASTCALL #define SCM_MOBIOS_FNID(s, c) (((((s) & 0xFF) << 8) | ((c) & 0xFF)) \ | 0x33000000) @@ -48,21 +55,36 @@ static inline int smc_fastcall(void *fc_generic, size_t size) { +#if !defined(USE_SHM_BRIDGE) if (is_scm_armv8()) { +#endif struct scm_desc desc = {0}; int ret; - void *scm_buf = NULL; + static void *scm_buf; + static phys_addr_t scm_buf_pa; - scm_buf = kzalloc(PAGE_ALIGN(size), GFP_KERNEL); + if (!scm_buf) { +#if defined USE_SHM_BRIDGE + static struct qtee_shm scm_shm = {0}; + + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(size), + &scm_shm); + scm_buf = scm_shm.vaddr; + scm_buf_pa = scm_shm.paddr; +#else + scm_buf = kzalloc(PAGE_ALIGN(size), GFP_KERNEL); + scm_buf_pa = virt_to_phys(scm_buf); +#endif + } if (!scm_buf) return -ENOMEM; memcpy(scm_buf, fc_generic, size); dmac_flush_range(scm_buf, scm_buf + size); desc.arginfo = TZ_EXECUTIVE_EXT_ID_PARAM_ID; - desc.args[0] = virt_to_phys(scm_buf); + desc.args[0] = scm_buf_pa; desc.args[1] = (u32)size; - desc.args[2] = virt_to_phys(scm_buf); + desc.args[2] = scm_buf_pa; desc.args[3] = (u32)size; ret = scm_call2( @@ -72,18 +94,14 @@ static inline int smc_fastcall(void *fc_generic, size_t size) dmac_flush_range(scm_buf, scm_buf + size); memcpy(fc_generic, scm_buf, size); - kfree(scm_buf); return ret; +#if !defined(USE_SHM_BRIDGE) } return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE, fc_generic, size, fc_generic, size); -} - -/* Should be defined for all TZBSPv4 platform common flag */ -#ifndef CONFIG_TRUSTONIC_TEE_LPAE -#define CONFIG_TRUSTONIC_TEE_LPAE #endif +} /* * Do not start the TEE at driver init @@ -93,20 +111,46 @@ static inline int smc_fastcall(void *fc_generic, size_t size) /* * Perform crypto clock enable/disable * of clocks - * "bus_clk" - * "core_clk" - * "iface_clk" + * "bus_clk" + * "core_clk" + * "iface_clk" */ #define MC_CRYPTO_CLOCK_MANAGEMENT +/* + * This should be defined only on SDM845 + * #define TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE "qcom,no-clock-support" + */ #define MC_CRYPTO_CLOCK_CORESRC_PROPNAME "qcom,ce-opp-freq" + +#if defined USE_SHM_BRIDGE +#define MC_CLOCK_CORESRC_DEFAULTRATE 192000000 +#else #define MC_CLOCK_CORESRC_DEFAULTRATE 100000000 +#endif /* * Perform clock enable/disable for clock "core_clk_src" */ #define MC_DEVICE_PROPNAME "qcom,mcd" +/* + * Platform Node + */ +#define TT_CLOCK_DEVICE_NAME "qcom,qseecom" /* All TZBSPv4 targets are using AARCH32_FC flag */ #define MC_AARCH32_FC +/* This should be defined only on SM8150 + * Gold cores do not support TEE interfaces + * CPU_IDS should list only silver cores + */ +/* Enforce/restrict statically CPUs potentially running TEE + * (Customize to match platform CPU layout... 0xF0 for big cores only for ex). + * If not defined TEE dynamically using all platform CPUs (recommended) + * Warning: Both PLAT_DEFAULT_TEE_AFFINITY_MASK and + * BIG_CORE_SWITCH_AFFINITY_MASK have to be defined + */ +#define PLAT_DEFAULT_TEE_AFFINITY_MASK (0xF) +#define BIG_CORE_SWITCH_AFFINITY_MASK (0xF) + #endif /* _MC_PLATFORM_H_ */ diff --git a/drivers/gud/MobiCoreDriver/pm.c b/drivers/gud/MobiCoreDriver/pm.c deleted file mode 100644 index 9552d4f536fd..000000000000 --- a/drivers/gud/MobiCoreDriver/pm.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "platform.h" /* MC_BL_NOTIFIER */ - -#ifdef MC_BL_NOTIFIER -#include "main.h" -#include "scheduler.h" /* SWd suspend/resume commands */ -#include "pm.h" -#include - -static struct pm_context { - struct notifier_block bl_swicher_notifier; -} pm_ctx; - -static int bl_switcher_notifier_handler(struct notifier_block *this, - unsigned long event, void *ptr) -{ - unsigned int mpidr, cpu; - int ret = 0; - - asm volatile ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (mpidr)); - cpu = mpidr & 0x3; - mc_dev_devel("%s switching!!, cpu: %u, Out=%u", - event == SWITCH_ENTER ? "Before" : "After", cpu, - (mpidr >> 8) & 0xf); - - if (cpu != 0) - return 0; - - switch (event) { - case SWITCH_ENTER: - ret = mc_scheduler_suspend(); - break; - case SWITCH_EXIT: - ret = mc_scheduler_resume(); - break; - default: - mc_dev_devel("MobiCore: Unknown switch event!"); - } - - return ret; -} - -int mc_pm_start(void) -{ - pm_ctx.bl_swicher_notifier.notifier_call = bl_switcher_notifier_handler; - register_bL_swicher_notifier(&pm_ctx.bl_swicher_notifier); - return 0; -} - -void mc_pm_stop(void) -{ - unregister_bL_swicher_notifier(&pm_ctx.bl_swicher_notifier); -} - -#endif /* MC_BL_NOTIFIER */ diff --git a/drivers/gud/MobiCoreDriver/public/GP/tee_client_api.h b/drivers/gud/MobiCoreDriver/public/GP/tee_client_api.h index 14253c4ad91c..395936a0884b 100644 --- a/drivers/gud/MobiCoreDriver/public/GP/tee_client_api.h +++ b/drivers/gud/MobiCoreDriver/public/GP/tee_client_api.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/public/GP/tee_client_api_imp.h b/drivers/gud/MobiCoreDriver/public/GP/tee_client_api_imp.h index 4b3497f4568b..7ee5ef0ee4e2 100644 --- a/drivers/gud/MobiCoreDriver/public/GP/tee_client_api_imp.h +++ b/drivers/gud/MobiCoreDriver/public/GP/tee_client_api_imp.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -14,8 +15,7 @@ /* * This header file defines the implementation-dependent types, - * constants and macros for all the Trusted Foundations implementations - * of the TEE Client API + * constants and macros for all the Kinibi implementations of the TEE Client API */ #ifndef __TEE_CLIENT_API_IMP_H__ #define __TEE_CLIENT_API_IMP_H__ @@ -25,18 +25,18 @@ struct tee_client; struct teec_context_imp { - struct tee_client *client; + struct tee_client *client; }; struct teec_session_imp { u32 session_id; struct teec_context_imp context; - bool active; + int active; }; struct teec_shared_memory_imp { - struct tee_client *client; - bool implementation_allocated; + struct tee_client *client; + int implementation_allocated; }; struct teec_operation_imp { diff --git a/drivers/gud/MobiCoreDriver/public/GP/tee_client_error.h b/drivers/gud/MobiCoreDriver/public/GP/tee_client_error.h index 85be1d4b8128..33553c3e0bcc 100644 --- a/drivers/gud/MobiCoreDriver/public/GP/tee_client_error.h +++ b/drivers/gud/MobiCoreDriver/public/GP/tee_client_error.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/public/GP/tee_client_types.h b/drivers/gud/MobiCoreDriver/public/GP/tee_client_types.h index e870ee6aef48..47ea2f9b144e 100644 --- a/drivers/gud/MobiCoreDriver/public/GP/tee_client_types.h +++ b/drivers/gud/MobiCoreDriver/public/GP/tee_client_types.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/public/mc_admin.h b/drivers/gud/MobiCoreDriver/public/mc_admin.h index b0e3abb35e0a..b66fff693290 100644 --- a/drivers/gud/MobiCoreDriver/public/mc_admin.h +++ b/drivers/gud/MobiCoreDriver/public/mc_admin.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -37,11 +38,11 @@ enum { #define MC_IOC_MAGIC 'M' struct mc_admin_request { - __u32 request_id; /* Unique request identifier */ - __u32 command; /* Command to daemon */ + __u32 request_id; /* Unique request identifier */ + __u32 command; /* Command to daemon */ struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */ - __u32 is_gp; /* Whether trustlet is GP */ - __u32 spid; /* SPID of trustlet, if relevant */ + __u32 is_gp; /* Whether trustlet is GP */ + __u32 spid; /* SPID of trustlet, if relevant */ }; struct mc_admin_response { @@ -60,9 +61,10 @@ struct mc_admin_driver_info { }; struct mc_admin_load_info { - __u32 spid; /* SPID of trustlet, if relevant */ - __u64 address; /* Address of the data */ - __u32 length; /* Length of data to get */ + __u32 spid; /* SPID of trustlet, if relevant */ + __u64 address; /* Address of the data */ + __u32 length; /* Length of data to get */ + struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */ }; #define MC_ADMIN_IO_GET_DRIVER_REQUEST \ @@ -75,6 +77,8 @@ struct mc_admin_load_info { _IOW(MC_IOC_MAGIC, 3, struct mc_admin_load_info) #define MC_ADMIN_IO_LOAD_CHECK \ _IOW(MC_IOC_MAGIC, 4, struct mc_admin_load_info) +#define MC_ADMIN_IO_LOAD_KEY_SO \ + _IOW(MC_IOC_MAGIC, 5, struct mc_admin_load_info) #ifdef __cplusplus } diff --git a/drivers/gud/MobiCoreDriver/public/mc_user.h b/drivers/gud/MobiCoreDriver/public/mc_user.h index 812335e1a084..b8b32c26ec3d 100644 --- a/drivers/gud/MobiCoreDriver/public/mc_user.h +++ b/drivers/gud/MobiCoreDriver/public/mc_user.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -15,8 +16,8 @@ #ifndef _MC_USER_H_ #define _MC_USER_H_ -#define MCDRVMODULEAPI_VERSION_MAJOR 6 -#define MCDRVMODULEAPI_VERSION_MINOR 3 +#define MCDRVMODULEAPI_VERSION_MAJOR 7 +#define MCDRVMODULEAPI_VERSION_MINOR 0 #include @@ -29,22 +30,20 @@ /** Maximum length of MobiCore product ID string. */ #define MC_PRODUCT_ID_LEN 64 -/** Flags for session */ -#define MC_IO_SESSION_REMOTE_BUFFERS BIT(0) -#define MC_IO_SESSION_NO_MULTIMAP BIT(1) - /** Number of buffers that can be mapped at once */ #define MC_MAP_MAX 4 /* Max length for buffers */ -#define BUFFER_LENGTH_MAX 0x100000 +#define MC_MAX_TCI_LEN 0x100000 +#define BUFFER_LENGTH_MAX 0x40000000 + +/* Max length for objects */ +#define OBJECT_LENGTH_MAX 0x8000000 /* Flags for buffers to map (aligned on GP) */ #define MC_IO_MAP_INPUT BIT(0) #define MC_IO_MAP_OUTPUT BIT(1) #define MC_IO_MAP_INPUT_OUTPUT (MC_IO_MAP_INPUT | MC_IO_MAP_OUTPUT) -/** Extra flags for buffers to map (proprietary) */ -#define MC_IO_MAP_PERSISTENT BIT(20) /* * Universally Unique Identifier (UUID) according to ISO/IEC 11578. @@ -90,7 +89,6 @@ struct mc_ioctl_open_session { __u64 tci; /* tci buffer pointer */ __u32 tcilen; /* tci length */ struct mc_identity identity; /* GP TA identity */ - __s32 client_fd; /* client, when using proxy */ }; /* @@ -103,7 +101,6 @@ struct mc_ioctl_open_trustlet { __u32 tlen; /* binary length */ __u64 tci; /* tci buffer pointer */ __u32 tcilen; /* tci length */ - __s32 client_fd; /* client, when using proxy */ }; /* @@ -138,7 +135,6 @@ struct mc_ioctl_buffer { */ struct mc_ioctl_map { __u32 sid; /* session id */ - __s32 client_fd; /* client, when using proxy */ struct mc_ioctl_buffer buf; /* buffers info */ }; @@ -220,7 +216,6 @@ struct mc_ioctl_gp_initialize_context { struct mc_ioctl_gp_register_shared_mem { struct gp_shared_memory memref; struct gp_return ret; /* return origin/value (out) */ - __s32 client_fd; /* client, when using proxy */ }; /* @@ -239,7 +234,6 @@ struct mc_ioctl_gp_open_session { struct gp_operation operation; /* set of parameters */ struct gp_return ret; /* return origin/value (out) */ __u32 session_id; /* session id (out) */ - __s32 client_fd; /* client, when using proxy */ }; /* @@ -257,7 +251,6 @@ struct mc_ioctl_gp_invoke_command { __u32 session_id; /* session id */ __u32 command_id; /* ID of the command */ struct gp_return ret; /* return origin/value (out) */ - __s32 client_fd; /* client, when using proxy */ }; /* diff --git a/drivers/gud/MobiCoreDriver/public/mobicore_driver_api.h b/drivers/gud/MobiCoreDriver/public/mobicore_driver_api.h index 801b19bd19db..13414f69429b 100644 --- a/drivers/gud/MobiCoreDriver/public/mobicore_driver_api.h +++ b/drivers/gud/MobiCoreDriver/public/mobicore_driver_api.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/scheduler.c b/drivers/gud/MobiCoreDriver/scheduler.c deleted file mode 100644 index b6e3f65c58f1..000000000000 --- a/drivers/gud/MobiCoreDriver/scheduler.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2013-2018 TRUSTONIC LIMITED - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "public/mc_user.h" - -#include "main.h" -#include "fastcall.h" -#include "logging.h" -#include "mcp.h" -#include "nq.h" -#include "scheduler.h" - -#define SCHEDULING_FREQ 5 /**< N-SIQ every n-th time */ -#define DEFAULT_TIMEOUT_MS 60000 - -static struct sched_ctx { - struct task_struct *thread; - bool thread_run; - struct completion idle_complete; /* Unblock scheduler thread */ - struct completion sleep_complete; /* Wait for sleep status */ - struct mutex sleep_mutex; /* Protect sleep request */ - struct mutex request_mutex; /* Protect all below */ - /* The order of this enum matters */ - enum sched_command { - NONE, /* No specific request */ - YIELD, /* Run the SWd */ - NSIQ, /* Schedule the SWd */ - SUSPEND, /* Suspend the SWd */ - RESUME, /* Resume the SWd */ - } request; - bool suspended; -} sched_ctx; - -static int mc_scheduler_command(enum sched_command command) -{ - if (IS_ERR_OR_NULL(sched_ctx.thread)) - return -EFAULT; - - mutex_lock(&sched_ctx.request_mutex); - if (sched_ctx.request < command) { - sched_ctx.request = command; - complete(&sched_ctx.idle_complete); - } - - mutex_unlock(&sched_ctx.request_mutex); - return 0; -} - -static int mc_scheduler_pm_command(enum sched_command command) -{ - int ret = -EPERM; - - if (IS_ERR_OR_NULL(sched_ctx.thread)) - return -EFAULT; - - mutex_lock(&sched_ctx.sleep_mutex); - - /* Send request */ - mc_scheduler_command(command); - - /* Wait for scheduler to reply */ - wait_for_completion(&sched_ctx.sleep_complete); - mutex_lock(&sched_ctx.request_mutex); - if (command == SUSPEND) { - if (sched_ctx.suspended) - ret = 0; - } else { - if (!sched_ctx.suspended) - ret = 0; - } - - mutex_unlock(&sched_ctx.request_mutex); - - mutex_unlock(&sched_ctx.sleep_mutex); - return ret; -} - -static int mc_dev_command(enum nq_scheduler_commands command) -{ - switch (command) { - case MC_NQ_YIELD: - return mc_scheduler_command(YIELD); - case MC_NQ_NSIQ: - return mc_scheduler_command(NSIQ); - } - - return -EINVAL; -} - -int mc_scheduler_suspend(void) -{ - return mc_scheduler_pm_command(SUSPEND); -} - -int mc_scheduler_resume(void) -{ - return mc_scheduler_pm_command(RESUME); -} - -/* - * This thread, and only this thread, schedules the SWd. Hence, reading the idle - * status and its associated timeout is safe from race conditions. - */ -static int tee_scheduler(void *arg) -{ - int timeslice = 0; /* Actually scheduling period */ - int ret = 0; - - while (1) { - s32 timeout_ms = -1; - bool pm_request = false; - - if (sched_ctx.suspended || nq_get_idle_timeout(&timeout_ms)) { - /* If timeout is 0 we keep scheduling the SWd */ - if (!timeout_ms) { - mc_scheduler_command(NSIQ); - } else { - bool infinite_timeout = timeout_ms < 0; - - if (timeout_ms < 0 || - timeout_ms > DEFAULT_TIMEOUT_MS) - timeout_ms = DEFAULT_TIMEOUT_MS; - - if (!wait_for_completion_timeout( - &sched_ctx.idle_complete, - msecs_to_jiffies(timeout_ms))) { - if (infinite_timeout) - continue; - - /* Timed out, force SWd schedule */ - mc_scheduler_command(NSIQ); - } - } - } - - if (kthread_should_stop() || !sched_ctx.thread_run) - break; - - /* Get requested command if any */ - mutex_lock(&sched_ctx.request_mutex); - if (sched_ctx.request == YIELD) - /* Yield forced: increment timeslice */ - timeslice++; - else if (sched_ctx.request >= NSIQ) { - /* Force N_SIQ, also to suspend/resume SWd */ - timeslice = 0; - if (sched_ctx.request == SUSPEND) { - nq_suspend(); - pm_request = true; - } else if (sched_ctx.request == RESUME) { - nq_resume(); - pm_request = true; - } - } - - if (g_ctx.f_time) - nq_update_time(); - - sched_ctx.request = NONE; - mutex_unlock(&sched_ctx.request_mutex); - - /* Reset timeout so we don't loop if SWd halted */ - nq_reset_idle_timeout(); - if (timeslice--) { - /* Resume SWd from where it was */ - ret = mc_fc_yield(); - } else { - timeslice = SCHEDULING_FREQ; - /* Call SWd scheduler */ - ret = mc_fc_nsiq(); - } - - /* Always flush log buffer after the SWd has run */ - mc_logging_run(); - if (ret) - break; - - /* Should have suspended by now if requested */ - mutex_lock(&sched_ctx.request_mutex); - if (pm_request) { - sched_ctx.suspended = nq_suspended(); - complete(&sched_ctx.sleep_complete); - } - - mutex_unlock(&sched_ctx.request_mutex); - - /* Flush pending notifications if possible */ - if (nq_notifications_flush()) - complete(&sched_ctx.idle_complete); - } - - mc_dev_devel("exit, ret is %d", ret); - return ret; -} - -int mc_scheduler_start(void) -{ - sched_ctx.thread_run = true; - sched_ctx.thread = kthread_run(tee_scheduler, NULL, "tee_scheduler"); - if (IS_ERR(sched_ctx.thread)) { - mc_dev_err("tee_scheduler thread creation failed"); - return PTR_ERR(sched_ctx.thread); - } - - nq_register_scheduler(mc_dev_command); - complete(&sched_ctx.idle_complete); - return 0; -} - -void mc_scheduler_stop(void) -{ - nq_register_scheduler(NULL); - sched_ctx.thread_run = false; - complete(&sched_ctx.idle_complete); - kthread_stop(sched_ctx.thread); -} - -int mc_scheduler_init(void) -{ - init_completion(&sched_ctx.idle_complete); - init_completion(&sched_ctx.sleep_complete); - mutex_init(&sched_ctx.sleep_mutex); - mutex_init(&sched_ctx.request_mutex); - return 0; -} diff --git a/drivers/gud/MobiCoreDriver/scheduler.h b/drivers/gud/MobiCoreDriver/scheduler.h deleted file mode 100644 index c3c17f1c9017..000000000000 --- a/drivers/gud/MobiCoreDriver/scheduler.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __MC_SCHEDULER_H__ -#define __MC_SCHEDULER_H__ - -int mc_scheduler_init(void); -static inline void mc_scheduler_exit(void) {} -int mc_scheduler_start(void); -void mc_scheduler_stop(void); -int mc_scheduler_suspend(void); -int mc_scheduler_resume(void); - -#endif /* __MC_SCHEDULER_H__ */ diff --git a/drivers/gud/MobiCoreDriver/session.c b/drivers/gud/MobiCoreDriver/session.c index 1251edfe8407..8a7b0f27cfd7 100644 --- a/drivers/gud/MobiCoreDriver/session.c +++ b/drivers/gud/MobiCoreDriver/session.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -68,7 +69,8 @@ static inline bool gid_lt(kgid_t left, kgid_t right) } #endif #include "main.h" -#include "mmu.h" +#include "mmu.h" /* tee_mmu_buffer, tee_mmu_debug_structs */ +#include "iwp.h" #include "mcp.h" #include "client.h" /* *cbuf* */ #include "session.h" @@ -77,15 +79,19 @@ static inline bool gid_lt(kgid_t left, kgid_t right) #define SHA1_HASH_SIZE 20 static int wsm_create(struct tee_session *session, struct tee_wsm *wsm, - const struct mc_ioctl_buffer *buf, int client_fd) + const struct mc_ioctl_buffer *buf) { if (wsm->in_use) { - mc_dev_err("wsm already in use"); + mc_dev_err(-EINVAL, "wsm already in use"); return -EINVAL; } - wsm->mmu = client_mmu_create(session->client, buf, &wsm->cbuf, - client_fd); + if (buf->len > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %u too big", buf->len); + return -EINVAL; + } + + wsm->mmu = client_mmu_create(session->client, buf, &wsm->cbuf); if (IS_ERR(wsm->mmu)) return PTR_ERR(wsm->mmu); @@ -98,21 +104,46 @@ static int wsm_create(struct tee_session *session, struct tee_wsm *wsm, return 0; } +static int wsm_wrap(struct tee_session *session, struct tee_wsm *wsm, + struct tee_mmu *mmu) +{ + struct mcp_buffer_map map; + + if (wsm->in_use) { + mc_dev_err(-EINVAL, "wsm already in use"); + return -EINVAL; + } + + wsm->mmu = mmu; + tee_mmu_get(wsm->mmu); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_wsms); + tee_mmu_buffer(wsm->mmu, &map); + wsm->va = 0; + wsm->len = map.length; + wsm->flags = map.flags; + wsm->in_use = true; + return 0; +} + /* * Free a WSM object, must be called under the session's wsms_lock */ static void wsm_free(struct tee_session *session, struct tee_wsm *wsm) { if (!wsm->in_use) { - mc_dev_err("wsm not in use"); + mc_dev_err(-EINVAL, "wsm not in use"); return; } - /* Free MMU table */ - client_mmu_free(session->client, wsm->va, wsm->mmu, wsm->cbuf); - /* Delete wsm object */ - mc_dev_devel("freed wsm %p: mmu %p cbuf %p va %lx len %u sva %x", + mc_dev_devel("free wsm %p: mmu %p cbuf %p va %lx len %u sva %x", wsm, wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->sva); + /* Free MMU table */ + tee_mmu_put(wsm->mmu); + if (wsm->cbuf) + tee_cbuf_put(wsm->cbuf); + /* Decrement debug counter */ atomic_dec(&g_ctx.c_wsms); wsm->in_use = false; @@ -122,8 +153,10 @@ static void wsm_free(struct tee_session *session, struct tee_wsm *wsm) static int hash_path_and_data(struct task_struct *task, u8 *hash, const void *data, unsigned int data_len) { - struct mm_struct *mm = task->mm; + struct file *exe_file; struct crypto_shash *tfm; + struct shash_desc *desc; + size_t desc_size; char *buf; char *path; unsigned int path_len; @@ -133,13 +166,13 @@ static int hash_path_and_data(struct task_struct *task, u8 *hash, if (!buf) return -ENOMEM; - down_read(&mm->mmap_sem); - if (!mm->exe_file) { + exe_file = get_task_exe_file(task); + if (!exe_file) { ret = -ENOENT; goto end; } - path = d_path(&mm->exe_file->f_path, buf, PAGE_SIZE); + path = d_path(&exe_file->f_path, buf, PAGE_SIZE); if (IS_ERR(path)) { ret = PTR_ERR(path); goto end; @@ -159,31 +192,32 @@ static int hash_path_and_data(struct task_struct *task, u8 *hash, tfm = crypto_alloc_shash("sha1", 0, 0); if (IS_ERR(tfm)) { ret = PTR_ERR(tfm); - mc_dev_err("cannot allocate shash: %d", ret); + mc_dev_err(ret, "cannot allocate shash"); goto end; } - { - SHASH_DESC_ON_STACK(desc, tfm); - - desc->tfm = tfm; - desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; - - crypto_shash_init(desc); - crypto_shash_update(desc, (u8 *)path, path_len); - if (data) { - mc_dev_devel("hashing additional data"); - crypto_shash_update(desc, data, data_len); - } - - crypto_shash_final(desc, hash); - shash_desc_zero(desc); + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto err_desc; } - crypto_free_shash(tfm); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + crypto_shash_init(desc); + crypto_shash_update(desc, (u8 *)path, path_len); + if (data) { + mc_dev_devel("hashing additional data"); + crypto_shash_update(desc, data, data_len); + } + crypto_shash_final(desc, hash); + shash_desc_zero(desc); + kfree(desc); +err_desc: + crypto_free_shash(tfm); end: - up_read(&mm->mmap_sem); free_page((unsigned long)buf); return ret; @@ -295,10 +329,12 @@ static int check_prepare_identity(const struct mc_identity *identity, struct task_struct *task) { struct mc_identity *mcp_id = (struct mc_identity *)mcp_identity; - u8 hash[SHA1_HASH_SIZE]; + u8 hash[SHA1_HASH_SIZE] = { 0 }; bool application = false; + bool supplied_ca_identity = false; const void *data; unsigned int data_len; + static const u8 zero_buffer[sizeof(identity->login_data)] = { 0 }; /* Copy login type */ mcp_identity->login_type = identity->login_type; @@ -307,12 +343,6 @@ static int check_prepare_identity(const struct mc_identity *identity, identity->login_type == TEEC_TT_LOGIN_KERNEL) return 0; - /* Mobicore doesn't support GP client authentication. */ - if (!g_ctx.f_client_login) { - mc_dev_err("Unsupported login type %x", identity->login_type); - return -EINVAL; - } - /* Fill in uid field */ if (identity->login_type == LOGIN_USER || identity->login_type == LOGIN_USER_APPLICATION) { @@ -331,7 +361,8 @@ static int check_prepare_identity(const struct mc_identity *identity, * of its supplementary groups */ if (!has_group(cred, identity->gid)) { - mc_dev_err("group %d not allowed", identity->gid); + mc_dev_err(-EACCES, "group %d not allowed", + identity->gid); return -EACCES; } @@ -341,16 +372,21 @@ static int check_prepare_identity(const struct mc_identity *identity, switch (identity->login_type) { case LOGIN_PUBLIC: - case LOGIN_USER: case LOGIN_GROUP: break; + case LOGIN_USER: + data = NULL; + data_len = 0; + break; case LOGIN_APPLICATION: application = true; + supplied_ca_identity = true; data = NULL; data_len = 0; break; case LOGIN_USER_APPLICATION: application = true; + supplied_ca_identity = true; data = &mcp_id->uid; data_len = sizeof(mcp_id->uid); break; @@ -361,11 +397,22 @@ static int check_prepare_identity(const struct mc_identity *identity, break; default: /* Any other login_type value is invalid. */ - mc_dev_err("Invalid login type %d", identity->login_type); + mc_dev_err(-EINVAL, "Invalid login type %d", + identity->login_type); return -EINVAL; } - if (application) { + /* let the supplied login_data pass through if it is LOGIN_APPLICATION + * or LOGIN_USER_APPLICATION and not a zero-filled buffer + * That buffer is expected to contain a NWd computed hash containing the + * CA identity + */ + if (supplied_ca_identity && + memcmp(identity->login_data, zero_buffer, + sizeof(identity->login_data)) != 0) { + memcpy(&mcp_id->login_data, identity->login_data, + sizeof(mcp_id->login_data)); + } else if (application) { int ret = hash_path_and_data(task, hash, data, data_len); if (ret) { @@ -379,63 +426,25 @@ static int check_prepare_identity(const struct mc_identity *identity, return 0; } -/* - * The proxy gives us the fd of its server-side socket, so we can find out the - * PID of its client. put_task_struct() must be called to free the resource. - */ -static struct task_struct *get_task_struct_from_client_fd(int client_fd) -{ - struct task_struct *task = NULL; - struct socket *sock; - int err; - - if (client_fd < 0) { - get_task_struct(current); - return current; - } - - sock = sockfd_lookup(client_fd, &err); - if (!sock) - return NULL; - - if (sock->sk && sock->sk->sk_peer_pid) { - rcu_read_lock(); - task = pid_task(sock->sk->sk_peer_pid, PIDTYPE_PID); - get_task_struct(task); - rcu_read_unlock(); - } - - sockfd_put(sock); - return task; -} - /* * Create a session object. * Note: object is not attached to client yet. */ -struct tee_session *session_create(struct tee_client *client, bool is_gp, - struct mc_identity *identity, int client_fd) +struct tee_session *session_create(struct tee_client *client, + const struct mc_identity *identity) { struct tee_session *session; struct identity mcp_identity; - int i; - if (is_gp) { + if (!IS_ERR_OR_NULL(identity)) { /* Check identity method and data. */ - struct task_struct *task; int ret; - task = get_task_struct_from_client_fd(client_fd); - if (!task) { - mc_dev_err("can't get task from client fd %d", - client_fd); - return ERR_PTR(-EINVAL); - } - - ret = check_prepare_identity(identity, &mcp_identity, task); - put_task_struct(task); + ret = check_prepare_identity(identity, &mcp_identity, current); if (ret) return ERR_PTR(ret); + } else { + memset(&mcp_identity, 0, sizeof(mcp_identity)); } /* Allocate session object */ @@ -446,93 +455,24 @@ struct tee_session *session_create(struct tee_client *client, bool is_gp, /* Increment debug counter */ atomic_inc(&g_ctx.c_sessions); /* Initialise object members */ - if (is_gp) + if (identity) { + session->is_gp = true; iwp_session_init(&session->iwp_session, &mcp_identity); - else + } else { + session->is_gp = false; mcp_session_init(&session->mcp_session); + } client_get(client); session->client = client; kref_init(&session->kref); INIT_LIST_HEAD(&session->list); mutex_init(&session->wsms_lock); - for (i = 0; i < MC_MAP_MAX; i++) - session->wsms_lru[i] = &session->wsms[i]; mc_dev_devel("created session %p: client %p", session, session->client); return session; } -int session_open(struct tee_session *session, const struct tee_object *obj, - const struct tee_mmu *obj_mmu, uintptr_t tci, size_t len, - int client_fd) -{ - struct mcp_buffer_map map; - - if (len > BUFFER_LENGTH_MAX) - return -EINVAL; - - tee_mmu_buffer(obj_mmu, &map); - /* Create wsm object for tci */ - if (tci && len) { - struct tee_wsm *wsm = &session->tci; - struct mcp_buffer_map tci_map; - struct mc_ioctl_buffer buf; - int ret = 0; - - buf.va = tci; - buf.len = len; - buf.flags = MC_IO_MAP_INPUT_OUTPUT; - buf.sva = 0; - ret = wsm_create(session, wsm, &buf, client_fd); - if (ret) - return ret; - - mc_dev_devel("created tci: mmu %p cbuf %p va %lx len %u", - wsm->mmu, wsm->cbuf, wsm->va, wsm->len); - - tee_mmu_buffer(wsm->mmu, &tci_map); - ret = mcp_open_session(&session->mcp_session, obj, &map, - &tci_map); - if (ret) { - wsm_free(session, wsm); - return ret; - } - - return 0; - } - - if (tci || len) { - mc_dev_devel("Tci pointer and length are incoherent"); - return -EINVAL; - } - - return mcp_open_session(&session->mcp_session, obj, &map, NULL); -} - -/* - * Close session and unreference session object. - * Session object is assumed to have been removed from main list, which means - * that session_close cannot be called anymore. - */ -int session_close(struct tee_session *session) -{ - int ret; - - if (nq_session_is_gp(&session->nq_session)) { - ret = iwp_close_session(&session->iwp_session); - if (!ret) - mc_dev_devel("closed GP session %x", - session->iwp_session.sid); - } else { - ret = mcp_close_session(&session->mcp_session); - if (!ret) - mc_dev_devel("closed MC session %x", - session->mcp_session.sid); - } - return ret; -} - /* * Free session object and all objects it contains (wsm). */ @@ -558,7 +498,7 @@ static void session_release(struct kref *kref) wsm_free(session, &session->tci); } - if (nq_session_is_gp(&session->nq_session)) + if (session->is_gp) mc_dev_devel("freed GP session %p: client %p id %x", session, session->client, session->iwp_session.sid); else @@ -580,36 +520,6 @@ int session_put(struct tee_session *session) return kref_put(&session->kref, session_release); } -/* - * Session is to be removed from NWd records as SWd is dead - */ -int session_kill(struct tee_session *session) -{ - mcp_kill_session(&session->mcp_session); - return session_put(session); -} - -/* - * Send a notification to TA - */ -int session_notify_swd(struct tee_session *session) -{ - if (!session) { - mc_dev_devel("Session pointer is null"); - return -EINVAL; - } - - return mcp_notify(&session->mcp_session); -} - -/* - * Read and clear last notification received from TA - */ -s32 session_exitcode(struct tee_session *session) -{ - return mcp_session_exitcode(&session->mcp_session); -} - static int wsm_debug_structs(struct kasnprintf_buf *buf, struct tee_wsm *wsm, int no) { @@ -640,15 +550,122 @@ static int wsm_debug_structs(struct kasnprintf_buf *buf, struct tee_wsm *wsm, return 0; } +int session_mc_open_session(struct tee_session *session, + struct mcp_open_info *info) +{ + struct tee_wsm *wsm = &session->tci; + bool tci_in_use = false; + int ret; + + /* Check that tci and its length make sense */ + if (info->tci_len > MC_MAX_TCI_LEN) + return -EINVAL; + + if (!info->tci_va != !info->tci_len) { + mc_dev_devel("TCI pointer and length are inconsistent"); + return -EINVAL; + } + + /* Add existing TCI map */ + if (info->tci_mmu) { + ret = wsm_wrap(session, wsm, info->tci_mmu); + if (ret) + return ret; + + tci_in_use = true; + mc_dev_devel("wrapped tci: mmu %p len %u flags %x", + wsm->mmu, wsm->len, wsm->flags); + } + + /* Create mapping for TCI */ + if (info->tci_va) { + struct mc_ioctl_buffer buf = { + .va = info->tci_va, + .len = info->tci_len, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; + + ret = wsm_create(session, wsm, &buf); + if (ret) + return ret; + + tci_in_use = true; + info->tci_mmu = wsm->mmu; + mc_dev_devel( + "created tci: mmu %p cbuf %p va %lx len %u flags %x", + wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->flags); + } + + ret = mcp_open_session(&session->mcp_session, info, &tci_in_use); + if (info->tci_va && (ret || !tci_in_use)) + wsm_free(session, &session->tci); + + return ret; +} + +/* + * Close session and unreference session object. + * Session object is assumed to have been removed from main list, which means + * that session_close cannot be called anymore. + */ +int session_close(struct tee_session *session) +{ + int ret; + + if (session->is_gp) { + ret = iwp_close_session(&session->iwp_session); + if (!ret) + mc_dev_devel("closed GP session %x", + session->iwp_session.sid); + } else { + ret = mcp_close_session(&session->mcp_session); + if (!ret) + mc_dev_devel("closed MC session %x", + session->mcp_session.sid); + } + return ret; +} + +/* + * Session is to be removed from NWd records as SWd is dead + */ +int session_mc_cleanup_session(struct tee_session *session) +{ + mcp_cleanup_session(&session->mcp_session); + return session_put(session); +} + +/* + * Send a notification to TA + */ +int session_mc_notify(struct tee_session *session) +{ + if (!session) { + mc_dev_devel("Session pointer is null"); + return -EINVAL; + } + + return mcp_notify(&session->mcp_session); +} + +/* + * Sleep until next notification from SWd. + */ +int session_mc_wait(struct tee_session *session, s32 timeout, + bool silent_expiry) +{ + return mcp_wait(&session->mcp_session, timeout, silent_expiry); +} + /* * Share buffers with SWd and add corresponding WSM objects to session. * This may involve some re-use or cleanup of inactive mappings. */ -int session_map(struct tee_session *session, struct mc_ioctl_buffer *buf, - int client_fd) +int session_mc_map(struct tee_session *session, struct tee_mmu *mmu, + struct mc_ioctl_buffer *buf) { struct tee_wsm *wsm; - struct mcp_buffer_map map; + u32 sva; int i, ret; mutex_lock(&session->wsms_lock); @@ -665,25 +682,25 @@ int session_map(struct tee_session *session, struct mc_ioctl_buffer *buf, } wsm = &session->wsms[i]; - ret = wsm_create(session, wsm, buf, client_fd); + if (!mmu) + ret = wsm_create(session, wsm, buf); + else + ret = wsm_wrap(session, wsm, mmu); + if (ret) { mc_dev_devel("maps[%d] va=%llx create failed: %d", i, buf->va, ret); goto out; } - mc_dev_devel("created wsm #%d: mmu %p cbuf %p va %lx len %u", - i, wsm->mmu, wsm->cbuf, wsm->va, wsm->len); - tee_mmu_buffer(wsm->mmu, &map); - mc_dev_devel("maps[%d] va=%llx: t:%u a:%llx o:%u l:%u", i, buf->va, - map.type, map.phys_addr, map.offset, map.length); - - ret = mcp_map(session->mcp_session.sid, &map); + mc_dev_devel("created wsm #%d: mmu %p cbuf %p va %lx len %u flags %x", + i, wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->flags); + ret = mcp_map(session->mcp_session.sid, wsm->mmu, &sva); if (ret) { wsm_free(session, wsm); } else { - buf->sva = map.secure_va; - wsm->sva = map.secure_va; + buf->sva = sva; + wsm->sva = sva; } out: @@ -695,8 +712,8 @@ out: /* * In theory, stop sharing buffers with the SWd. In fact, mark them inactive. */ -int session_unmap(struct tee_session *session, - const struct mc_ioctl_buffer *buf) +int session_mc_unmap(struct tee_session *session, + const struct mc_ioctl_buffer *buf) { struct tee_wsm *wsm; struct mcp_buffer_map map; @@ -721,6 +738,7 @@ int session_unmap(struct tee_session *session, wsm = &session->wsms[i]; tee_mmu_buffer(wsm->mmu, &map); map.secure_va = wsm->sva; + ret = mcp_unmap(session->mcp_session.sid, &map); if (!ret) wsm_free(session, wsm); @@ -731,13 +749,11 @@ out: } /* - * Sleep until next notification from SWd. + * Read and clear last notification received from TA */ -int session_waitnotif(struct tee_session *session, s32 timeout, - bool silent_expiry) +int session_mc_get_err(struct tee_session *session, s32 *err) { - return mcp_session_waitnotif(&session->mcp_session, timeout, - silent_expiry); + return mcp_get_err(&session->mcp_session, err); } static void unmap_gp_bufs(struct tee_session *session, @@ -760,7 +776,7 @@ static void unmap_gp_bufs(struct tee_session *session, static int map_gp_bufs(struct tee_session *session, const struct mc_ioctl_buffer *bufs, struct gp_shared_memory **parents, - struct iwp_buffer_map *maps, int client_fd) + struct iwp_buffer_map *maps) { int i, ret = 0; @@ -768,13 +784,12 @@ static int map_gp_bufs(struct tee_session *session, mutex_lock(&session->wsms_lock); for (i = 0; i < MC_MAP_MAX; i++) { /* Reset reference for temporary memory */ - maps[i].map.phys_addr = 0; + maps[i].map.addr = 0; /* Reset reference for registered memory */ maps[i].sva = 0; if (bufs[i].va) { /* Temporary memory, needs mapping */ - ret = wsm_create(session, &session->wsms[i], &bufs[i], - client_fd); + ret = wsm_create(session, &session->wsms[i], &bufs[i]); if (ret) { mc_dev_devel( "maps[%d] va=%llx create failed: %d", @@ -792,6 +807,8 @@ static int map_gp_bufs(struct tee_session *session, mc_dev_devel("couldn't find shared mem"); break; } + + mc_dev_devel("param[%d] has sva %x", i, maps[i].sva); } } mutex_unlock(&session->wsms_lock); @@ -804,27 +821,24 @@ static int map_gp_bufs(struct tee_session *session, } int session_gp_open_session(struct tee_session *session, - const struct tee_object *obj, - const struct tee_mmu *obj_mmu, + const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct gp_return *gp_ret, - int client_fd) + struct gp_return *gp_ret) { /* TEEC_MEMREF_TEMP_* buffers to map */ struct mc_ioctl_buffer bufs[MC_MAP_MAX]; struct iwp_buffer_map maps[MC_MAP_MAX]; - struct gp_shared_memory *parents[MC_MAP_MAX]; - struct mcp_buffer_map obj_map; + struct gp_shared_memory *parents[MC_MAP_MAX] = { NULL }; struct client_gp_operation client_operation; int ret = 0; - ret = iwp_open_session_prepare(&session->iwp_session, obj, operation, - bufs, parents, gp_ret); + ret = iwp_open_session_prepare(&session->iwp_session, operation, bufs, + parents, gp_ret); if (ret) return ret; /* Create WSMs from bufs */ - ret = map_gp_bufs(session, bufs, parents, maps, client_fd); + ret = map_gp_bufs(session, bufs, parents, maps); if (ret) { iwp_open_session_abort(&session->iwp_session); return iwp_set_ret(ret, gp_ret); @@ -838,24 +852,56 @@ int session_gp_open_session(struct tee_session *session, iwp_open_session_abort(&session->iwp_session); return iwp_set_ret(-ECANCELED, gp_ret); } + /* Open/call TA */ - tee_mmu_buffer(obj_mmu, &obj_map); - ret = iwp_open_session(&session->iwp_session, operation, &obj_map, maps, - gp_ret); + ret = iwp_open_session(&session->iwp_session, uuid, operation, maps, + NULL, NULL, gp_ret); /* Cleanup */ client_gp_operation_remove(session->client, &client_operation); unmap_gp_bufs(session, maps); return ret; } +int session_gp_open_session_domu(struct tee_session *session, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + /* TEEC_MEMREF_TEMP_* buffers to map */ + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_open_session_prepare(&session->iwp_session, NULL, NULL, NULL, + gp_ret); + if (ret) + return ret; + + /* Tell client about operation */ + client_operation.started = started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_open_session_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Open/call TA */ + ret = iwp_open_session(&session->iwp_session, uuid, NULL, NULL, iws, + mmus, gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + return ret; +} + int session_gp_invoke_command(struct tee_session *session, u32 command_id, struct gp_operation *operation, - struct gp_return *gp_ret, int client_fd) + struct gp_return *gp_ret) { /* TEEC_MEMREF_TEMP_* buffers to map */ struct mc_ioctl_buffer bufs[4]; struct iwp_buffer_map maps[MC_MAP_MAX]; - struct gp_shared_memory *parents[MC_MAP_MAX]; + struct gp_shared_memory *parents[MC_MAP_MAX] = { NULL }; struct client_gp_operation client_operation; int ret = 0; @@ -865,7 +911,7 @@ int session_gp_invoke_command(struct tee_session *session, u32 command_id, return ret; /* Create WSMs from bufs */ - ret = map_gp_bufs(session, bufs, parents, maps, client_fd); + ret = map_gp_bufs(session, bufs, parents, maps); if (ret) { iwp_invoke_command_abort(&session->iwp_session); return iwp_set_ret(ret, gp_ret); @@ -879,16 +925,47 @@ int session_gp_invoke_command(struct tee_session *session, u32 command_id, iwp_invoke_command_abort(&session->iwp_session); return iwp_set_ret(-ECANCELED, gp_ret); } + /* Call TA */ - ret = iwp_invoke_command(&session->iwp_session, operation, maps, - gp_ret); + ret = iwp_invoke_command(&session->iwp_session, operation, maps, NULL, + NULL, gp_ret); /* Cleanup */ client_gp_operation_remove(session->client, &client_operation); unmap_gp_bufs(session, maps); return ret; } -int session_gp_request_cancellation(u32 slot) +int session_gp_invoke_command_domu(struct tee_session *session, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_invoke_command_prepare(&session->iwp_session, 0, NULL, NULL, + NULL, gp_ret); + if (ret) + return ret; + + /* Tell client about operation */ + client_operation.started = started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_invoke_command_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Call TA */ + ret = iwp_invoke_command(&session->iwp_session, NULL, NULL, iws, mmus, + gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + return ret; +} + +int session_gp_request_cancellation(u64 slot) { return iwp_request_cancellation(slot); } @@ -898,22 +975,22 @@ int session_debug_structs(struct kasnprintf_buf *buf, { const char *type; u32 session_id; - s32 exit_code; + s32 err; int i, ret; - if (nq_session_is_gp(&session->nq_session)) { + if (session->is_gp) { session_id = session->iwp_session.sid; - exit_code = 0; + err = 0; type = "GP"; } else { session_id = session->mcp_session.sid; - exit_code = mcp_session_exitcode(&session->mcp_session); + session_mc_get_err(session, &err); type = "MC"; } ret = kasnprintf(buf, "\tsession %pK [%d]: %4x %s ec %d%s\n", session, kref_read(&session->kref), session_id, type, - exit_code, is_closing ? " " : ""); + err, is_closing ? " " : ""); if (ret < 0) return ret; diff --git a/drivers/gud/MobiCoreDriver/session.h b/drivers/gud/MobiCoreDriver/session.h index a272d7b7c665..46ebfe297d51 100644 --- a/drivers/gud/MobiCoreDriver/session.h +++ b/drivers/gud/MobiCoreDriver/session.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2016 TRUSTONIC LIMITED + * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -19,7 +20,6 @@ #include "mcp.h" #include "iwp.h" -#include "nq.h" struct tee_object; struct tee_mmu; @@ -43,13 +43,12 @@ struct tee_wsm { /* Pointer to associated cbuf, if relevant */ struct cbuf *cbuf; /* State of this WSM */ - bool in_use; + int in_use; }; struct tee_session { /* Session descriptor */ union { - struct nq_session nq_session; struct mcp_session mcp_session; struct iwp_session iwp_session; }; @@ -65,42 +64,49 @@ struct tee_session { struct mutex wsms_lock; /* WSMs for a session */ struct tee_wsm wsms[MC_MAP_MAX]; - /* Pointers to WSMs in LRU order (0 is oldest) */ - struct tee_wsm *wsms_lru[MC_MAP_MAX]; + /* This TA is of Global Platform type */ + bool is_gp; }; -struct tee_session *session_create(struct tee_client *client, bool is_gp, - struct mc_identity *identity, int client_fd); -int session_open(struct tee_session *session, const struct tee_object *obj, - const struct tee_mmu *obj_mmu, uintptr_t tci, size_t len, - int client_fd); -int session_close(struct tee_session *session); +struct tee_session *session_create(struct tee_client *client, + const struct mc_identity *identity); static inline void session_get(struct tee_session *session) { kref_get(&session->kref); } int session_put(struct tee_session *session); -int session_kill(struct tee_session *session); -int session_map(struct tee_session *session, struct mc_ioctl_buffer *bufs, - int client_fd); -int session_unmap(struct tee_session *session, - const struct mc_ioctl_buffer *bufs); -s32 session_exitcode(struct tee_session *session); -int session_notify_swd(struct tee_session *session); -int session_waitnotif(struct tee_session *session, s32 timeout, - bool silent_expiry); +int session_close(struct tee_session *session); + +int session_mc_open_session(struct tee_session *session, + struct mcp_open_info *info); +int session_mc_cleanup_session(struct tee_session *session); +int session_mc_notify(struct tee_session *session); +int session_mc_wait(struct tee_session *session, s32 timeout, + bool silent_expiry); +int session_mc_map(struct tee_session *session, struct tee_mmu *mmu, + struct mc_ioctl_buffer *bufs); +int session_mc_unmap(struct tee_session *session, + const struct mc_ioctl_buffer *bufs); +int session_mc_get_err(struct tee_session *session, s32 *err); int session_gp_open_session(struct tee_session *session, - const struct tee_object *obj, - const struct tee_mmu *obj_mmu, + const struct mc_uuid_t *uuid, struct gp_operation *operation, - struct gp_return *gp_ret, - int client_fd); + struct gp_return *gp_ret); +int session_gp_open_session_domu(struct tee_session *session, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); int session_gp_invoke_command(struct tee_session *session, u32 command_id, struct gp_operation *operation, - struct gp_return *gp_ret, int client_fd); -int session_gp_request_cancellation(u32 slot); + struct gp_return *gp_ret); +int session_gp_invoke_command_domu(struct tee_session *session, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int session_gp_request_cancellation(u64 slot); int session_debug_structs(struct kasnprintf_buf *buf, struct tee_session *session, bool is_closing); diff --git a/drivers/gud/MobiCoreDriver/teeclientapi.c b/drivers/gud/MobiCoreDriver/teeclientapi.c index fcb2e0a2f882..ff3a6e29d389 100644 --- a/drivers/gud/MobiCoreDriver/teeclientapi.c +++ b/drivers/gud/MobiCoreDriver/teeclientapi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -21,7 +22,6 @@ #include "public/mc_user.h" #include "main.h" -#include "mci/gptci.h" /* Needs stuff from tee_client_api.h or its includes */ #include "mci/mcinq.h" /* TA termination codes */ #include "client.h" @@ -192,7 +192,7 @@ u32 teec_initialize_context(const char *name, struct teec_context *context) /* Make sure TEE was started */ ret = mc_wait_tee_start(); if (ret) { - mc_dev_err("TEE failed to start, now or in the past"); + mc_dev_err(ret, "TEE failed to start, now or in the past"); return TEEC_ERROR_BAD_STATE; } @@ -246,8 +246,8 @@ u32 teec_open_session(struct teec_context *context, struct teec_operation *operation, u32 *return_origin) { - struct mc_uuid_t tauuid; - struct mc_identity identity; + struct mc_uuid_t uuid; + struct mc_identity identity = {0}; struct tee_client *client = NULL; struct gp_operation gp_op; struct gp_return gp_ret; @@ -278,7 +278,7 @@ u32 teec_open_session(struct teec_context *context, connection_method = TEEC_TT_LOGIN_KERNEL; session->imp.active = false; - _lib_uuid_to_array(destination, tauuid.value); + _lib_uuid_to_array(destination, uuid.value); memset(&gp_op, 0, sizeof(gp_op)); if (operation) { @@ -293,9 +293,8 @@ u32 teec_open_session(struct teec_context *context, /* Wait for GP loading to be possible, maximum 30s */ timeout = 30; do { - ret = client_gp_open_session(client, &session->imp.session_id, - &tauuid, &gp_op, &identity, - &gp_ret, -1); + ret = client_gp_open_session(client, &uuid, &gp_op, &identity, + &gp_ret, &session->imp.session_id); if (!ret || ret != EAGAIN) break; @@ -359,7 +358,7 @@ u32 teec_invoke_command(struct teec_session *session, } ret = client_gp_invoke_command(client, session->imp.session_id, - command_id, &gp_op, &gp_ret, -1); + command_id, &gp_op, &gp_ret); if (ret || gp_ret.value != TEEC_SUCCESS) { mc_dev_devel("client_gp_invoke_command failed(%08x) %08x", ret, @@ -455,8 +454,8 @@ u32 teec_register_shared_memory(struct teec_context *context, memref.buffer = (uintptr_t)shared_mem->buffer; memref.flags = shared_mem->flags; memref.size = shared_mem->size; - ret = client_gp_register_shared_mem(context->imp.client, &memref, - &gp_ret, -1); + ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL, + &memref, &gp_ret); if (ret) return _teec_convert_error(-ret); @@ -507,8 +506,8 @@ u32 teec_allocate_shared_memory(struct teec_context *context, memref.buffer = (uintptr_t)shared_mem->buffer; memref.flags = shared_mem->flags; memref.size = shared_mem->size; - ret = client_gp_register_shared_mem(context->imp.client, &memref, - &gp_ret, -1); + ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL, + &memref, &gp_ret); if (ret) { vfree(shared_mem->buffer); diff --git a/drivers/gud/MobiCoreDriver/user.c b/drivers/gud/MobiCoreDriver/user.c index 8cbd63f88bf9..ca1f112ccf86 100644 --- a/drivers/gud/MobiCoreDriver/user.c +++ b/drivers/gud/MobiCoreDriver/user.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2018 TRUSTONIC LIMITED * All Rights Reserved. @@ -139,10 +140,9 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_open_session(client, &session.sid, &session.uuid, - session.tci, session.tcilen, - session.is_gp_uuid, - &session.identity, -1); + ret = client_mc_open_session(client, &session.uuid, + session.tci, session.tcilen, + &session.sid); if (ret) break; @@ -161,9 +161,10 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_open_trustlet(client, &trustlet.sid, trustlet.spid, - trustlet.buffer, trustlet.tlen, - trustlet.tci, trustlet.tcilen, -1); + ret = client_mc_open_trustlet(client, trustlet.spid, + trustlet.buffer, trustlet.tlen, + trustlet.tci, trustlet.tcilen, + &trustlet.sid); if (ret) break; @@ -205,14 +206,13 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_map_session_wsms(client, map.sid, &map.buf, -1); + ret = client_mc_map(client, map.sid, NULL, &map.buf); if (ret) break; /* Fill in return struct */ if (copy_to_user(uarg, &map, sizeof(map))) { ret = -EFAULT; - client_unmap_session_wsms(client, map.sid, &map.buf); break; } break; @@ -225,7 +225,7 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_unmap_session_wsms(client, map.sid, &map.buf); + ret = client_mc_unmap(client, map.sid, &map.buf); break; } case MC_IO_ERR: { @@ -287,9 +287,9 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_gp_register_shared_mem(client, + ret = client_gp_register_shared_mem(client, NULL, NULL, &shared_mem.memref, - &shared_mem.ret, -1); + &shared_mem.ret); if (copy_to_user(uarg, &shared_mem, sizeof(shared_mem))) { ret = -EFAULT; @@ -316,10 +316,10 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } - ret = client_gp_open_session(client, &session.session_id, - &session.uuid, &session.operation, + ret = client_gp_open_session(client, &session.uuid, + &session.operation, &session.identity, - &session.ret, -1); + &session.ret, &session.session_id); if (copy_to_user(uarg, &session, sizeof(session))) { ret = -EFAULT; @@ -349,7 +349,7 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) ret = client_gp_invoke_command(client, command.session_id, command.command_id, &command.operation, - &command.ret, -1); + &command.ret); if (copy_to_user(uarg, &command, sizeof(command))) { ret = -EFAULT; @@ -371,8 +371,8 @@ static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) break; } default: - mc_dev_err("unsupported command no %d", id); ret = -ENOIOCTLCMD; + mc_dev_err(ret, "unsupported command no %d", id); } return ret; @@ -385,8 +385,11 @@ static int user_mmap(struct file *file, struct vm_area_struct *vmarea) { struct tee_client *client = get_client(file); - if ((vmarea->vm_end - vmarea->vm_start) > BUFFER_LENGTH_MAX) + if ((vmarea->vm_end - vmarea->vm_start) > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %lu too big", + vmarea->vm_end - vmarea->vm_start); return -EINVAL; + } /* Alloc contiguous buffer for this client */ return client_cbuf_create(client, diff --git a/drivers/gud/MobiCoreDriver/user.h b/drivers/gud/MobiCoreDriver/user.h index 8a5e6d125b41..e6972304ece5 100644 --- a/drivers/gud/MobiCoreDriver/user.h +++ b/drivers/gud/MobiCoreDriver/user.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2013-2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or diff --git a/drivers/gud/MobiCoreDriver/xen_be.c b/drivers/gud/MobiCoreDriver/xen_be.c new file mode 100644 index 000000000000..93c6312c667f --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_be.c @@ -0,0 +1,1152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2018 TRUSTONIC LIMITED + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifdef CONFIG_XEN + +#include +#include +#include +#include +#include + +#if KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE +#include +#endif + +#include "platform.h" /* MC_XENBUS_MAP_RING_VALLOC_4_1 */ +#include "main.h" +#include "admin.h" /* tee_object* */ +#include "client.h" /* Consider other VMs as clients */ +#include "mmu.h" +#include "mcp.h" /* mcp_get_version */ +#include "nq.h" +#include "xen_common.h" +#include "xen_be.h" + +#define vaddr(page) ((unsigned long)pfn_to_kaddr(page_to_pfn(page))) + +static struct { + struct list_head xfes; + struct mutex xfes_mutex; /* Protect the above */ +} l_ctx; + +/* Maps */ + +struct xen_be_map { + struct page **pages; + grant_handle_t *handles; + unsigned long nr_pages; + u32 flags; + bool pages_allocd; + bool refs_mapped; + /* To auto-delete */ + struct tee_deleter deleter; +}; + +static void xen_be_map_delete(struct xen_be_map *map) +{ + int i; + + if (map->refs_mapped) { + struct gnttab_unmap_grant_ref *unmaps; + + unmaps = kcalloc(map->nr_pages, sizeof(*unmaps), GFP_KERNEL); + if (!unmaps) + /* Cannot go on */ + return; + + for (i = 0; i < map->nr_pages; i++) + gnttab_set_unmap_op(&unmaps[i], vaddr(map->pages[i]), + map->flags, map->handles[i]); + + if (gnttab_unmap_refs(unmaps, NULL, map->pages, map->nr_pages)) + /* Cannot go on */ + return; + + for (i = 0; i < map->nr_pages; i++) + put_page(map->pages[i]); + + kfree(unmaps); + } + + if (map->pages_allocd) +#if KERNEL_VERSION(4, 0, 0) <= LINUX_VERSION_CODE + gnttab_free_pages(map->nr_pages, map->pages); +#else + free_xenballooned_pages(map->nr_pages, map->pages); +#endif + + kfree(map->handles); + kfree(map->pages); + kfree(map); + mc_dev_devel("freed xen map %p", map); + atomic_dec(&g_ctx.c_xen_maps); +} + +static struct xen_be_map *be_map_create(const struct xen_be_map *pte_map, + grant_ref_t *refs, int nr_refs, + int pte_entries_max, int dom_id, + bool readonly) +{ + struct xen_be_map *map; + struct gnttab_map_grant_ref *maps = NULL; + int i, ret = -ENOMEM; + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return ERR_PTR(-ENOMEM); + + atomic_inc(&g_ctx.c_xen_maps); + map->flags = GNTMAP_host_map; + if (readonly) + map->flags |= GNTMAP_readonly; + + map->nr_pages = nr_refs; + map->pages = kcalloc(map->nr_pages, sizeof(*map->pages), GFP_KERNEL); + if (!map->pages) + goto out; + + map->handles = kcalloc(map->nr_pages, sizeof(*map->handles), + GFP_KERNEL); + if (!map->handles) + goto out; + +#if KERNEL_VERSION(4, 0, 0) <= LINUX_VERSION_CODE + if (gnttab_alloc_pages(map->nr_pages, map->pages)) + goto out; +#else + if (alloc_xenballooned_pages(map->nr_pages, map->pages, false)) + goto out; +#endif + + map->pages_allocd = true; + maps = kcalloc(map->nr_pages, sizeof(*maps), GFP_KERNEL); + if (!maps) + goto out; + + if (pte_map) { + int k = 0, nr_refs_left = nr_refs; + + for (i = 0; i < pte_map->nr_pages; i++) { + int j, nr_refs = nr_refs_left; + grant_ref_t *refs = (void *)vaddr(pte_map->pages[i]); + + if (nr_refs > pte_entries_max) + nr_refs = pte_entries_max; + + for (j = 0; j < nr_refs; j++) { + mc_dev_devel("map [%d, %d] -> %d ref %u", + i, j, k, refs[j]); +#ifdef DEBUG + /* Relax serial interface to not kill the USB */ + usleep_range(100, 200); +#endif + gnttab_set_map_op( + &maps[k], vaddr(map->pages[k]), + map->flags, refs[j], dom_id); + nr_refs_left--; + k++; + } + } + } else { + for (i = 0; i < map->nr_pages; i++) { + mc_dev_devel("map table %d ref %u", i, refs[i]); + gnttab_set_map_op(&maps[i], vaddr(map->pages[i]), + map->flags, refs[i], dom_id); + } + } + + ret = gnttab_map_refs(maps, NULL, map->pages, map->nr_pages); + if (ret) + goto out; + + map->refs_mapped = true; + /* Pin pages */ + for (i = 0; i < map->nr_pages; i++) { + get_page(map->pages[i]); + map->handles[i] = maps[i].handle; + } + +out: + kfree(maps); + + if (ret) { + xen_be_map_delete(map); + return ERR_PTR(-ret); + } + + mc_dev_devel("created %s xen map %p", pte_map ? "buffer" : "ptes", map); + return map; +} + +static struct xen_be_map *xen_be_map_create(struct tee_xen_buffer *buffer, + int pte_entries_max, int dom_id) +{ + struct xen_be_map *map; + struct xen_be_map *pte_map; + int nr_pte_refs = + (buffer->info->nr_refs + pte_entries_max - 1) / pte_entries_max; + + /* First map the PTE pages */ + pte_map = be_map_create(NULL, buffer->data.refs, nr_pte_refs, + pte_entries_max, dom_id, true); + if (IS_ERR(pte_map)) + return pte_map; + + /* Now map the pages */ + map = be_map_create(pte_map, NULL, buffer->info->nr_refs, + pte_entries_max, dom_id, + buffer->info->flags == MC_IO_MAP_INPUT); + /* PTE pages mapping not needed any more */ + xen_be_map_delete(pte_map); + if (!IS_ERR(map)) { + /* Auto-delete */ + map->deleter.object = map; + map->deleter.delete = (void(*)(void *))xen_be_map_delete; + } + + return map; +} + +/* Dom0 call to DomU */ + +/* Must be called under xfe->ring_mutex */ +static inline void call_domu(struct tee_xfe *xfe, enum tee_xen_dom0_cmd cmd, + u32 id, int ret) +{ + WARN_ON(!xfe->ring_busy); + + /* Set command and ID */ + xfe->ring->dom0.cmd = cmd; + xfe->ring->dom0.id = id; + xfe->ring->dom0.cmd_ret = ret; + mc_dev_devel("Dom0 -> DomU request %u id %u ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + /* Call */ + notify_remote_via_irq(xfe->irq_dom0); + wait_for_completion(&xfe->ring_completion); +} + +/* Will be called back under xfe->ring_mutex */ +static irqreturn_t xen_be_irq_handler_dom0_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + if (!xfe->ring->dom0.cmd) { + mc_dev_devel("Ignore IRQ with no command (on DomU connect)"); + return IRQ_HANDLED; + } + + WARN_ON(!xfe->ring_busy); + + /* Response to a dom0 command, our side of ring locked by us */ + mc_dev_devel("Dom0 -> DomU response %u id %u ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + xfe->ring->dom0.cmd = TEE_XEN_DOM0_NONE; + xfe->ring->dom0.id = 0; + xfe->ring->dom0.cmd_ret = -EPERM; + complete(&xfe->ring_completion); + + return IRQ_HANDLED; +} + +/* MC protocol interface */ + +static inline int xen_be_get_version(struct tee_xfe *xfe) +{ + struct mc_version_info version_info; + int ret; + + ret = mcp_get_version(&version_info); + if (ret) + return ret; + + xfe->ring->domu.version_info = version_info; + return 0; +} + +static inline int xen_be_mc_has_sessions(struct tee_xfe *xfe) +{ + return client_has_sessions(xfe->client) ? -EBUSY : 0; +} + +static inline int xen_be_mc_open_session(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct mcp_open_info info = { + .type = TEE_MC_UUID, + .uuid = &xfe->ring->domu.uuid, + }; + int ret; + + if (tci_buffer->info->flags) { + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = tci_buffer->info->offset, + .length = tci_buffer->info->length, + .flags = tci_buffer->info->flags, + }; + + map = xen_be_map_create(tci_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + info.tci_mmu = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(info.tci_mmu)) { + ret = PTR_ERR(info.tci_mmu); + info.tci_mmu = NULL; + goto out; + } + } + + /* Open session */ + ret = client_mc_open_common(xfe->client, &info, + &xfe->ring->domu.session_id); + +out: + if (info.tci_mmu) + tee_mmu_put(info.tci_mmu); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_open_trustlet(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *ta_buffer = &xfe->buffers[1]; + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct mcp_open_info info = { + .type = TEE_MC_TA, + }; + struct xen_be_map *ta_map; + void *addr = NULL; + int ret = -ENOMEM; + + ta_map = xen_be_map_create(ta_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(ta_map)) + return PTR_ERR(ta_map); + + info.spid = xfe->ring->domu.spid; + addr = vmap(ta_map->pages, ta_map->nr_pages, + VM_MAP | VM_IOREMAP | VM_USERMAP, PAGE_KERNEL); + if (!addr) + goto out; + + info.va = (uintptr_t)addr + ta_buffer->info->offset; + info.len = ta_buffer->info->length; + + if (tci_buffer->info->flags) { + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = tci_buffer->info->offset, + .length = tci_buffer->info->length, + .flags = tci_buffer->info->flags, + }; + + map = xen_be_map_create(tci_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + info.tci_mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(info.tci_mmu)) { + ret = PTR_ERR(info.tci_mmu); + info.tci_mmu = NULL; + goto out; + } + } + + /* Open session */ + ret = client_mc_open_common(xfe->client, &info, + &xfe->ring->domu.session_id); + +out: + if (info.tci_mmu) + tee_mmu_put(info.tci_mmu); + + if (addr) + vunmap(addr); + + xen_be_map_delete(ta_map); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_close_session(struct tee_xfe *xfe) +{ + return client_remove_session(xfe->client, xfe->ring->domu.session_id); +} + +static inline int xen_be_mc_notify(struct tee_xfe *xfe) +{ + return client_notify_session(xfe->client, xfe->ring->domu.session_id); +} + +/* mc_wait cannot keep the ring busy while waiting, so we use a worker */ +struct mc_wait_work { + struct work_struct work; + struct tee_xfe *xfe; + u32 session_id; + s32 timeout; + u32 id; +}; + +static void xen_be_mc_wait_worker(struct work_struct *work) +{ + struct mc_wait_work *wait_work = + container_of(work, struct mc_wait_work, work); + struct tee_xfe *xfe = wait_work->xfe; + int ret; + + ret = client_waitnotif_session(wait_work->xfe->client, + wait_work->session_id, + wait_work->timeout, false); + + /* Send return code */ + mc_dev_devel("MC wait session done %x, ret %d", + wait_work->session_id, ret); + ring_get(xfe); + /* In */ + xfe->ring->dom0.session_id = wait_work->session_id; + /* Call */ + call_domu(xfe, TEE_XEN_MC_WAIT_DONE, wait_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(wait_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_mc_wait(struct tee_xfe *xfe) +{ + struct mc_wait_work *wait_work; + + /* Wait in a separate thread to release the communication ring */ + wait_work = kzalloc(sizeof(*wait_work), GFP_KERNEL); + if (!wait_work) + return -ENOMEM; + + tee_xfe_get(xfe); + wait_work->xfe = xfe; + wait_work->session_id = xfe->ring->domu.session_id; + wait_work->timeout = xfe->ring->domu.timeout; + wait_work->id = xfe->ring->domu.id; + INIT_WORK(&wait_work->work, xen_be_mc_wait_worker); + schedule_work(&wait_work->work); + return 0; +} + +static inline int xen_be_mc_map(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct xen_be_map *map; + struct tee_mmu *mmu = NULL; + struct mc_ioctl_buffer buf; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + return ret; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(mmu)) { + xen_be_map_delete(map); + return PTR_ERR(mmu); + } + + ret = client_mc_map(xfe->client, xfe->ring->domu.session_id, mmu, &buf); + /* Releasing the MMU shall also clear the map */ + tee_mmu_put(mmu); + if (!ret) + buffer->info->sva = buf.sva; + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_unmap(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mc_ioctl_buffer buf = { + .len = buffer->info->length, + .sva = buffer->info->sva, + }; + int ret; + + ret = client_mc_unmap(xfe->client, xfe->ring->domu.session_id, &buf); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_get_err(struct tee_xfe *xfe) +{ + int ret; + + ret = client_get_session_exitcode(xfe->client, + xfe->ring->domu.session_id, + &xfe->ring->domu.err); + mc_dev_devel("session %x err %d, exit with %d", + xfe->ring->domu.session_id, xfe->ring->domu.err, ret); + return ret; +} + +/* GP protocol interface */ + +static inline int xen_be_gp_register_shared_mem(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct xen_be_map *map; + struct tee_mmu *mmu = NULL; + struct gp_shared_memory memref = { + .buffer = buffer->info->addr, + .size = buffer->info->length, + .flags = buffer->info->flags, + }; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + return ret; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(mmu)) { + xen_be_map_delete(map); + return PTR_ERR(mmu); + } + + ret = client_gp_register_shared_mem(xfe->client, mmu, + &buffer->info->sva, &memref, + &xfe->ring->domu.gp_ret); + /* Releasing the MMU shall also clear the map */ + tee_mmu_put(mmu); + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_gp_release_shared_mem(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct gp_shared_memory memref = { + .buffer = buffer->info->addr, + .size = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + ret = client_gp_release_shared_mem(xfe->client, &memref); + + mc_dev_devel("exit with %d", ret); + return ret; +} + +/* GP functions cannot keep the ring busy while waiting, so we use a worker */ +struct gp_work { + struct work_struct work; + struct tee_xfe *xfe; + u64 operation_id; + struct interworld_session iws; + struct tee_mmu *mmus[4]; + struct mc_uuid_t uuid; + u32 session_id; + u32 id; +}; + +static void xen_be_gp_open_session_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + struct gp_return gp_ret; + int i, ret; + + ret = client_gp_open_session_domu(xfe->client, &gp_work->uuid, + gp_work->operation_id, &gp_work->iws, + gp_work->mmus, &gp_ret); + mc_dev_devel("GP open session done, ret %d", ret); + for (i = 0; i < TEE_BUFFERS; i++) + if (gp_work->mmus[i]) + tee_mmu_put(gp_work->mmus[i]); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + xfe->ring->dom0.iws = gp_work->iws; + xfe->ring->dom0.gp_ret = gp_ret; + /* Call */ + call_domu(xfe, TEE_XEN_GP_OPEN_SESSION_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_open_session(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + int i, ret = 0; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + /* Map tmpref buffers */ + for (i = 0; i < TEE_BUFFERS; i++) { + struct tee_xen_buffer *buffer = &xfe->buffers[i]; + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + + if (!buffer->info->flags) + continue; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto err_map; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + gp_work->mmus[i] = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(gp_work->mmus[i])) { + xen_be_map_delete(map); + ret = PTR_ERR(gp_work->mmus[i]); + goto err_mmus; + } + } + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->iws = xfe->ring->domu.iws; + gp_work->uuid = xfe->ring->domu.uuid; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_open_session_worker); + schedule_work(&gp_work->work); + return 0; + +err_mmus: + for (i = 0; i < TEE_BUFFERS; i++) + if (!IS_ERR_OR_NULL(gp_work->mmus[i])) + tee_mmu_put(gp_work->mmus[i]); +err_map: + kfree(gp_work); + return ret; +} + +static void xen_be_gp_close_session_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + int ret; + + ret = client_gp_close_session(xfe->client, gp_work->session_id); + mc_dev_devel("GP close session done, ret %d", ret); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + /* Call */ + call_domu(xfe, TEE_XEN_GP_CLOSE_SESSION_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_close_session(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->session_id = xfe->ring->domu.session_id; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_close_session_worker); + schedule_work(&gp_work->work); + return 0; +} + +static void xen_be_gp_invoke_command_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + struct gp_return gp_ret; + int i, ret; + + ret = client_gp_invoke_command_domu(xfe->client, gp_work->session_id, + gp_work->operation_id, + &gp_work->iws, gp_work->mmus, + &gp_ret); + mc_dev_devel("GP invoke command done, ret %d", ret); + for (i = 0; i < TEE_BUFFERS; i++) + if (gp_work->mmus[i]) + tee_mmu_put(gp_work->mmus[i]); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + xfe->ring->dom0.iws = gp_work->iws; + xfe->ring->dom0.gp_ret = gp_ret; + /* Call */ + call_domu(xfe, TEE_XEN_GP_INVOKE_COMMAND_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_invoke_command(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + int i, ret = 0; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + /* Map tmpref buffers */ + for (i = 0; i < TEE_BUFFERS; i++) { + struct tee_xen_buffer *buffer = &xfe->buffers[i]; + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + + if (!buffer->info->flags) + continue; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto err_map; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + gp_work->mmus[i] = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(gp_work->mmus[i])) { + xen_be_map_delete(map); + ret = PTR_ERR(gp_work->mmus[i]); + goto err_mmus; + } + } + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->iws = xfe->ring->domu.iws; + gp_work->session_id = xfe->ring->domu.session_id; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_invoke_command_worker); + schedule_work(&gp_work->work); + return 0; + +err_mmus: + for (i = 0; i < TEE_BUFFERS; i++) + if (!IS_ERR_OR_NULL(gp_work->mmus[i])) + tee_mmu_put(gp_work->mmus[i]); +err_map: + kfree(gp_work); + return ret; +} + +static inline int xen_be_gp_request_cancellation(struct tee_xfe *xfe) +{ + client_gp_request_cancellation(xfe->client, + xfe->ring->domu.operation_id); + return 0; +} + +static irqreturn_t xen_be_irq_handler_domu_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + if (!xfe->ring->domu.cmd) { + mc_dev_devel("Ignore IRQ with no command (on DomU connect)"); + return IRQ_HANDLED; + } + + /* DomU event, their side of ring locked by them */ + schedule_work(&xfe->work); + + return IRQ_HANDLED; +} + +static void xen_be_irq_handler_domu_bh(struct work_struct *data) +{ + struct tee_xfe *xfe = container_of(data, struct tee_xfe, work); + + xfe->ring->domu.otherend_ret = -EINVAL; + mc_dev_devel("DomU -> Dom0 command %u id %u", + xfe->ring->domu.cmd, xfe->ring->domu.id); + switch (xfe->ring->domu.cmd) { + case TEE_XEN_DOMU_NONE: + return; + /* MC */ + case TEE_XEN_MC_HAS_SESSIONS: + xfe->ring->domu.otherend_ret = xen_be_mc_has_sessions(xfe); + break; + case TEE_XEN_GET_VERSION: + xfe->ring->domu.otherend_ret = xen_be_get_version(xfe); + break; + case TEE_XEN_MC_OPEN_SESSION: + xfe->ring->domu.otherend_ret = xen_be_mc_open_session(xfe); + break; + case TEE_XEN_MC_OPEN_TRUSTLET: + xfe->ring->domu.otherend_ret = xen_be_mc_open_trustlet(xfe); + break; + case TEE_XEN_MC_CLOSE_SESSION: + xfe->ring->domu.otherend_ret = xen_be_mc_close_session(xfe); + break; + case TEE_XEN_MC_NOTIFY: + xfe->ring->domu.otherend_ret = xen_be_mc_notify(xfe); + break; + case TEE_XEN_MC_WAIT: + xfe->ring->domu.otherend_ret = xen_be_mc_wait(xfe); + break; + case TEE_XEN_MC_MAP: + xfe->ring->domu.otherend_ret = xen_be_mc_map(xfe); + break; + case TEE_XEN_MC_UNMAP: + xfe->ring->domu.otherend_ret = xen_be_mc_unmap(xfe); + break; + case TEE_XEN_MC_GET_ERR: + xfe->ring->domu.otherend_ret = xen_be_mc_get_err(xfe); + break; + /* GP */ + case TEE_XEN_GP_REGISTER_SHARED_MEM: + xfe->ring->domu.otherend_ret = + xen_be_gp_register_shared_mem(xfe); + break; + case TEE_XEN_GP_RELEASE_SHARED_MEM: + xfe->ring->domu.otherend_ret = + xen_be_gp_release_shared_mem(xfe); + break; + case TEE_XEN_GP_OPEN_SESSION: + xfe->ring->domu.otherend_ret = xen_be_gp_open_session(xfe); + break; + case TEE_XEN_GP_CLOSE_SESSION: + xfe->ring->domu.otherend_ret = xen_be_gp_close_session(xfe); + break; + case TEE_XEN_GP_INVOKE_COMMAND: + xfe->ring->domu.otherend_ret = xen_be_gp_invoke_command(xfe); + break; + case TEE_XEN_GP_REQUEST_CANCELLATION: + xfe->ring->domu.otherend_ret = + xen_be_gp_request_cancellation(xfe); + break; + } + + mc_dev_devel("DomU -> Dom0 result %u id %u ret %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, + xfe->ring->domu.otherend_ret); + notify_remote_via_irq(xfe->irq_domu); +} + +/* Device */ + +static const struct xenbus_device_id xen_be_ids[] = { + { "tee_xen" }, + { "" } +}; + +/* Called when a front-end is created */ +static int xen_be_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + struct tee_xfe *xfe; + int ret = 0; + + ret = xenbus_switch_state(xdev, XenbusStateInitWait); + if (ret) { + xenbus_dev_fatal(xdev, ret, + "failed to change state to initwait"); + return ret; + } + + xfe = tee_xfe_create(xdev); + if (!xfe) { + ret = -ENOMEM; + xenbus_dev_fatal(xdev, ret, "failed to create FE struct"); + goto err_xfe_create; + } + + xfe->client = client_create(true); + if (!xfe->client) { + ret = -ENOMEM; + xenbus_dev_fatal(xdev, ret, "failed to create FE client"); + goto err_client_create; + } + + INIT_WORK(&xfe->work, xen_be_irq_handler_domu_bh); + + mutex_lock(&l_ctx.xfes_mutex); + list_add_tail(&xfe->list, &l_ctx.xfes); + mutex_unlock(&l_ctx.xfes_mutex); + + ret = xenbus_switch_state(xdev, XenbusStateInitialised); + if (ret) { + xenbus_dev_fatal(xdev, ret, + "failed to change state to initialised"); + goto err_switch_state; + } + + return 0; + +err_switch_state: + mutex_lock(&l_ctx.xfes_mutex); + list_del(&xfe->list); + mutex_unlock(&l_ctx.xfes_mutex); +err_client_create: + tee_xfe_put(xfe); +err_xfe_create: + return ret; +} + +/* Called when device is unregistered */ +static int xen_be_remove(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe = dev_get_drvdata(&xdev->dev); + + xenbus_switch_state(xdev, XenbusStateClosed); + + mutex_lock(&l_ctx.xfes_mutex); + list_del(&xfe->list); + mutex_unlock(&l_ctx.xfes_mutex); + + tee_xfe_put(xfe); + return 0; +} + +static inline int xen_be_map_ring_valloc(struct xenbus_device *dev, + grant_ref_t ref, void **vaddr) +{ +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE || \ + defined(MC_XENBUS_MAP_RING_VALLOC_4_1) + return xenbus_map_ring_valloc(dev, &ref, 1, vaddr); +#else + return xenbus_map_ring_valloc(dev, ref, vaddr); +#endif +} + +static inline void frontend_attach(struct tee_xfe *xfe) +{ + int domu_version; + int ret; + int i; + + if (xenbus_read_driver_state(xfe->xdev->nodename) != + XenbusStateInitialised) + return; + + ret = xenbus_gather(XBT_NIL, xfe->xdev->otherend, + "ring-ref", "%u", &xfe->ring_ref, + "pte-entries-max", "%u", &xfe->pte_entries_max, + "event-channel-domu", "%u", &xfe->evtchn_domu, + "event-channel-dom0", "%u", &xfe->evtchn_dom0, + "domu-version", "%u", &domu_version, NULL); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to gather other domain info"); + return; + } + + mc_dev_devel("ring ref %u evtchn domu=%u dom0=%u version=%u", + xfe->ring_ref, xfe->evtchn_domu, xfe->evtchn_dom0, + domu_version); + + if (domu_version != TEE_XEN_VERSION) { + xenbus_dev_fatal( + xfe->xdev, ret, + "front- and back-end versions do not match: %d vs %d", + domu_version, TEE_XEN_VERSION); + return; + } + + ret = xen_be_map_ring_valloc(xfe->xdev, xfe->ring_ref, &xfe->ring_p); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, "failed to map ring"); + return; + } + mc_dev_devel("mapped ring %p", xfe->ring_p); + + /* Map buffers individually */ + for (i = 0; i < TEE_BUFFERS; i++) { + ret = xen_be_map_ring_valloc(xfe->xdev, + xfe->ring->domu.buffers[i].pmd_ref, + &xfe->buffers[i].data.addr); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to map buffer page"); + return; + } + + xfe->buffers[i].info = &xfe->ring->domu.buffers[i]; + } + + ret = bind_interdomain_evtchn_to_irqhandler( + xfe->xdev->otherend_id, xfe->evtchn_domu, + xen_be_irq_handler_domu_th, 0, "tee_be_domu", xfe); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to bind event channel to DomU IRQ"); + return; + } + + xfe->irq_domu = ret; + mc_dev_devel("bound DomU IRQ %d", xfe->irq_domu); + + ret = bind_interdomain_evtchn_to_irqhandler( + xfe->xdev->otherend_id, xfe->evtchn_dom0, + xen_be_irq_handler_dom0_th, 0, "tee_be_dom0", xfe); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to bind event channel to Dom0 IRQ"); + return; + } + + xfe->irq_dom0 = ret; + mc_dev_devel("bound Dom0 IRQ %d", xfe->irq_dom0); + + ret = xenbus_switch_state(xfe->xdev, XenbusStateConnected); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to change state to connected"); + return; + } +} + +static inline void frontend_detach(struct tee_xfe *xfe) +{ + int i; + + xenbus_switch_state(xfe->xdev, XenbusStateClosing); + if (xfe->irq_domu >= 0) + unbind_from_irqhandler(xfe->irq_domu, xfe); + + if (xfe->irq_dom0 >= 0) + unbind_from_irqhandler(xfe->irq_dom0, xfe); + + for (i = 0; i < TEE_BUFFERS; i++) + xenbus_unmap_ring_vfree(xfe->xdev, xfe->buffers[i].data.addr); + + if (xfe->ring_p) + xenbus_unmap_ring_vfree(xfe->xdev, xfe->ring_p); +} + +static void xen_be_frontend_changed(struct xenbus_device *xdev, + enum xenbus_state fe_state) +{ + struct tee_xfe *xfe = dev_get_drvdata(&xdev->dev); + + mc_dev_devel("fe state changed to %d", fe_state); + switch (fe_state) { + case XenbusStateInitialising: + case XenbusStateInitWait: + break; + case XenbusStateInitialised: + frontend_attach(xfe); + break; + case XenbusStateConnected: + break; + case XenbusStateClosing: + frontend_detach(xfe); + break; + case XenbusStateUnknown: + case XenbusStateClosed: + device_unregister(&xfe->xdev->dev); + break; + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + break; + } +} + +static struct xenbus_driver xen_be_driver = { + .ids = xen_be_ids, + .probe = xen_be_probe, + .remove = xen_be_remove, + .otherend_changed = xen_be_frontend_changed, +}; + +int xen_be_init(void) +{ + INIT_LIST_HEAD(&l_ctx.xfes); + mutex_init(&l_ctx.xfes_mutex); + return xenbus_register_backend(&xen_be_driver); +} + +void xen_be_exit(void) +{ + xenbus_unregister_driver(&xen_be_driver); +} + +#endif diff --git a/drivers/gud/MobiCoreDriver/pm.h b/drivers/gud/MobiCoreDriver/xen_be.h similarity index 58% rename from drivers/gud/MobiCoreDriver/pm.h rename to drivers/gud/MobiCoreDriver/xen_be.h index c6fbde581974..8e5cdf66d3c7 100644 --- a/drivers/gud/MobiCoreDriver/pm.h +++ b/drivers/gud/MobiCoreDriver/xen_be.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2013-2015 TRUSTONIC LIMITED + * Copyright (c) 2017 TRUSTONIC LIMITED * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -12,25 +13,25 @@ * GNU General Public License for more details. */ -#ifndef _MC_PM_H_ -#define _MC_PM_H_ +#ifndef _MC_XEN_BE_H_ +#define _MC_XEN_BE_H_ -#include "platform.h" /* MC_BL_NOTIFIER */ +#include -#ifdef MC_BL_NOTIFIER -/* Initialize Power Management */ -int mc_pm_start(void); -/* Free all Power Management resources*/ -void mc_pm_stop(void); +struct xen_be_map; + +#ifdef CONFIG_XEN +int xen_be_init(void); +void xen_be_exit(void); #else -static inline int mc_pm_start(void) +static inline int xen_be_init(void) { return 0; } -static inline void mc_pm_stop(void) +static inline void xen_be_exit(void) { } #endif -#endif /* _MC_PM_H_ */ +#endif /* _MC_XEN_BE_H_ */ diff --git a/drivers/gud/MobiCoreDriver/xen_common.c b/drivers/gud/MobiCoreDriver/xen_common.c new file mode 100644 index 000000000000..80b56f0f627c --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_common.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifdef CONFIG_XEN + +#include "main.h" +#include "client.h" +#include "xen_common.h" + +struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe; + + /* Alloc */ + xfe = kzalloc(sizeof(*xfe), GFP_KERNEL); + if (!xfe) + return NULL; + + atomic_inc(&g_ctx.c_xen_fes); + /* Init */ + dev_set_drvdata(&xdev->dev, xfe); + xfe->xdev = xdev; + kref_init(&xfe->kref); + xfe->evtchn_domu = -1; + xfe->evtchn_dom0 = -1; + xfe->irq_domu = -1; + xfe->irq_dom0 = -1; + INIT_LIST_HEAD(&xfe->list); + mutex_init(&xfe->ring_mutex); + init_completion(&xfe->ring_completion); + return xfe; +} + +static void tee_xfe_release(struct kref *kref) +{ + struct tee_xfe *xfe = container_of(kref, struct tee_xfe, kref); + + if (xfe->client) + client_close(xfe->client); + + kfree(xfe); + atomic_dec(&g_ctx.c_xen_fes); +} + +void tee_xfe_put(struct tee_xfe *xfe) +{ + kref_put(&xfe->kref, tee_xfe_release); +} + +#endif diff --git a/drivers/gud/MobiCoreDriver/xen_common.h b/drivers/gud/MobiCoreDriver/xen_common.h new file mode 100644 index 000000000000..3e30341c29f3 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_common.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MC_XEN_COMMON_H_ +#define _MC_XEN_COMMON_H_ + +#include +#include +#include +#include +#include +#include + +#include "public/mc_user.h" /* many types */ +#include "mci/mciiwp.h" +#include "mci/mcimcp.h" +#include "mmu.h" /* PMD/PTE max entries */ +#include "client.h" /* For BE to treat other VMs as clients */ + +#define TEE_XEN_VERSION 3 + +#define TEE_BUFFERS 4 + +enum tee_xen_domu_cmd { + TEE_XEN_DOMU_NONE, + TEE_XEN_GET_VERSION, + /* TEE_XEN_MC_OPEN_DEVICE = 11, SWd does not support this */ + /* TEE_XEN_MC_CLOSE_DEVICE, SWd does not support this */ + TEE_XEN_MC_HAS_SESSIONS = 13, + TEE_XEN_MC_OPEN_SESSION, + TEE_XEN_MC_OPEN_TRUSTLET, + TEE_XEN_MC_CLOSE_SESSION, + TEE_XEN_MC_NOTIFY, + TEE_XEN_MC_WAIT, + TEE_XEN_MC_MAP, + TEE_XEN_MC_UNMAP, + TEE_XEN_MC_GET_ERR, + /* TEE_XEN_GP_INITIALIZE_CONTEXT = 21, SWd does not support this */ + /* TEE_XEN_GP_FINALIZE_CONTEXT, SWd does not support this */ + TEE_XEN_GP_REGISTER_SHARED_MEM = 23, + TEE_XEN_GP_RELEASE_SHARED_MEM, + TEE_XEN_GP_OPEN_SESSION, + TEE_XEN_GP_CLOSE_SESSION, + TEE_XEN_GP_INVOKE_COMMAND, + TEE_XEN_GP_REQUEST_CANCELLATION, +}; + +enum tee_xen_dom0_cmd { + TEE_XEN_DOM0_NONE, + TEE_XEN_MC_WAIT_DONE = TEE_XEN_MC_WAIT, + TEE_XEN_GP_OPEN_SESSION_DONE = TEE_XEN_GP_OPEN_SESSION, + TEE_XEN_GP_CLOSE_SESSION_DONE = TEE_XEN_GP_CLOSE_SESSION, + TEE_XEN_GP_INVOKE_COMMAND_DONE = TEE_XEN_GP_INVOKE_COMMAND, +}; + +union tee_xen_mmu_table { + /* Array of references to pages (PTE_ENTRIES_MAX or PMD_ENTRIES_MAX) */ + grant_ref_t *refs; + /* Address of table */ + void *addr; + /* Page for table */ + unsigned long page; +}; + +struct tee_xen_buffer_info { + /* Page Middle Directory, refs to tee_xen_pte_table's (full pages) */ + grant_ref_t pmd_ref; + /* Total number of refs for buffer */ + u32 nr_refs; + u64 addr; /* Unique VM address */ + u32 offset; + u32 length; + u32 flags; + u32 sva; +}; + +/* Convenience structure to get buffer info and contents in one place */ +struct tee_xen_buffer { + struct tee_xen_buffer_info *info; + union tee_xen_mmu_table data; +}; + +struct tee_xen_ring { + /* DomU side, synchronous and asynchronous commands */ + struct { + enum tee_xen_domu_cmd cmd; /* in */ + u32 id; /* in (debug) */ + /* Return code of this command from Dom0 */ + int otherend_ret; /* out */ + struct mc_uuid_t uuid; /* in */ + u32 session_id; /* in/out */ + /* Buffers to share (4 for GP, 2 for mcOpenTrustlet) */ + struct tee_xen_buffer_info buffers[TEE_BUFFERS]; /* in */ + /* MC */ + struct mc_version_info version_info; /* out */ + u32 spid; /* in */ + s32 timeout; /* in */ + s32 err; /* out */ + /* GP */ + u64 operation_id; /* in */ + struct gp_return gp_ret; /* out */ + struct interworld_session iws; /* in */ + } domu; + + /* Dom0 side, response to asynchronous command, never read by Dom0 */ + struct { + enum tee_xen_dom0_cmd cmd; /* in */ + u32 id; /* in (debug) */ + /* Return code from command */ + int cmd_ret; /* in */ + /* The operation id is used to match GP request and response */ + u64 operation_id; /* in */ + struct gp_return gp_ret; /* in */ + struct interworld_session iws; /* in */ + /* The session id is used to match MC request and response */ + u32 session_id; /* in */ + } dom0; +}; + +struct tee_xfe { + struct xenbus_device *xdev; + struct kref kref; + grant_ref_t ring_ref; + int pte_entries_max; + int evtchn_domu; + int evtchn_dom0; + int irq_domu; + int irq_dom0; + struct list_head list; + struct tee_client *client; + struct work_struct work; + /* Ring page */ + union { + unsigned long ring_ul; + void *ring_p; + struct tee_xen_ring *ring; + }; + /* Buffer pages */ + struct tee_xen_buffer buffers[TEE_BUFFERS]; + struct mutex ring_mutex; /* Protect our side of ring */ + struct completion ring_completion; + bool ring_busy; + /* Unique ID for commands */ + u32 domu_cmd_id; +}; + +struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev); +static inline void tee_xfe_get(struct tee_xfe *xfe) +{ + kref_get(&xfe->kref); +} + +void tee_xfe_put(struct tee_xfe *xfe); + +static inline void ring_get(struct tee_xfe *xfe) +{ + mutex_lock(&xfe->ring_mutex); + xfe->ring_busy = true; +} + +static inline void ring_put(struct tee_xfe *xfe) +{ + xfe->ring_busy = false; + mutex_unlock(&xfe->ring_mutex); +} + +#endif /* _MC_XEN_COMMON_H_ */ diff --git a/drivers/gud/MobiCoreDriver/xen_fe.c b/drivers/gud/MobiCoreDriver/xen_fe.c new file mode 100644 index 000000000000..8f2fb1b04f36 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_fe.c @@ -0,0 +1,1194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "mci/mciiwp.h" /* struct interworld_session */ + +#include "main.h" + +#ifdef TRUSTONIC_XEN_DOMU + +#include "admin.h" /* tee_object* */ +#include "client.h" +#include "iwp.h" +#include "mcp.h" +#include "xen_common.h" +#include "xen_fe.h" + +#define page_to_gfn(p) (pfn_to_gfn(page_to_phys(p) >> XEN_PAGE_SHIFT)) + +static struct { + int (*probe)(void); + int (*start)(void); + struct tee_xfe *xfe; + /* MC sessions */ + struct mutex mc_sessions_lock; + struct list_head mc_sessions; + /* GP operations */ + struct mutex gp_operations_lock; + struct list_head gp_operations; + /* Last back-end state, + * to overcome an issue in some Xen implementations + */ + int last_be_state; +} l_ctx; + +struct xen_fe_mc_session { + struct list_head list; + struct completion completion; + int ret; + struct mcp_session *session; +}; + +struct xen_fe_gp_operation { + struct list_head list; + struct completion completion; + int ret; + u64 slot; + struct gp_return *gp_ret; + struct interworld_session *iws; +}; + +static inline struct xen_fe_mc_session *find_mc_session(u32 session_id) +{ + struct xen_fe_mc_session *session = ERR_PTR(-ENXIO), *candidate; + + mutex_lock(&l_ctx.mc_sessions_lock); + list_for_each_entry(candidate, &l_ctx.mc_sessions, list) { + struct mcp_session *mcp_session = candidate->session; + + if (mcp_session->sid == session_id) { + session = candidate; + break; + } + } + mutex_unlock(&l_ctx.mc_sessions_lock); + + WARN(IS_ERR(session), "MC session not found for ID %u", session_id); + return session; +} + +static inline int xen_fe_mc_wait_done(struct tee_xfe *xfe) +{ + struct xen_fe_mc_session *session; + + mc_dev_devel("received response to mc_wait for session %x: %d", + xfe->ring->dom0.session_id, xfe->ring->dom0.cmd_ret); + session = find_mc_session(xfe->ring->dom0.session_id); + if (IS_ERR(session)) + return PTR_ERR(session); + + session->ret = xfe->ring->dom0.cmd_ret; + complete(&session->completion); + return 0; +} + +static struct xen_fe_gp_operation *find_gp_operation(u64 operation_id) +{ + struct xen_fe_gp_operation *operation = ERR_PTR(-ENXIO), *candidate; + + mutex_lock(&l_ctx.gp_operations_lock); + list_for_each_entry(candidate, &l_ctx.gp_operations, list) { + if (candidate->slot == operation_id) { + operation = candidate; + list_del(&operation->list); + break; + } + } + mutex_unlock(&l_ctx.gp_operations_lock); + + WARN(IS_ERR(operation), "GP operation not found for op id %llx", + operation_id); + return operation; +} + +static inline int xen_fe_gp_open_session_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_open_session for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + *operation->iws = xfe->ring->dom0.iws; + *operation->gp_ret = xfe->ring->dom0.gp_ret; + complete(&operation->completion); + return 0; +} + +static inline int xen_fe_gp_close_session_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_close_session for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + complete(&operation->completion); + return 0; +} + +static inline int xen_fe_gp_invoke_command_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_invoke_command for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + *operation->iws = xfe->ring->dom0.iws; + *operation->gp_ret = xfe->ring->dom0.gp_ret; + complete(&operation->completion); + return 0; +} + +static irqreturn_t xen_fe_irq_handler_dom0_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + /* Dom0 event, their side of ring locked by them */ + schedule_work(&xfe->work); + + return IRQ_HANDLED; +} + +static void xen_fe_irq_handler_dom0_bh(struct work_struct *data) +{ + struct tee_xfe *xfe = container_of(data, struct tee_xfe, work); + int ret = -EINVAL; + + mc_dev_devel("Dom0 -> DomU command %u id %u cmd ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + switch (xfe->ring->dom0.cmd) { + case TEE_XEN_DOM0_NONE: + return; + case TEE_XEN_MC_WAIT_DONE: + ret = xen_fe_mc_wait_done(xfe); + break; + case TEE_XEN_GP_OPEN_SESSION_DONE: + ret = xen_fe_gp_open_session_done(xfe); + break; + case TEE_XEN_GP_CLOSE_SESSION_DONE: + ret = xen_fe_gp_close_session_done(xfe); + break; + case TEE_XEN_GP_INVOKE_COMMAND_DONE: + ret = xen_fe_gp_invoke_command_done(xfe); + break; + } + + if (ret) + mc_dev_err(ret, "Dom0 -> DomU result %u id %u", + xfe->ring->dom0.cmd, xfe->ring->dom0.id); + else + mc_dev_devel("Dom0 -> DomU result %u id %u", + xfe->ring->dom0.cmd, xfe->ring->dom0.id); + + notify_remote_via_evtchn(xfe->evtchn_dom0); +} + +/* Buffer management */ + +struct xen_fe_map { + /* Array of PTE tables, so we can release the associated buffer refs */ + union tee_xen_mmu_table *pte_tables; + int nr_pte_tables; + int nr_refs; + bool readonly; + int pages_created; /* Leak check */ + int refs_granted; /* Leak check */ + /* To auto-delete */ + struct tee_deleter deleter; +}; + +static void xen_fe_map_release_pmd(struct xen_fe_map *map, + const struct tee_xen_buffer *buffer) +{ + int i; + + if (IS_ERR_OR_NULL(map)) + return; + + for (i = 0; i < map->nr_pte_tables; i++) { + gnttab_end_foreign_access(buffer->data.refs[i], true, 0); + map->refs_granted--; + mc_dev_devel("unmapped table %d ref %u", + i, buffer->data.refs[i]); + } +} + +static void xen_fe_map_release(struct xen_fe_map *map, + const struct tee_xen_buffer *buffer) +{ + int nr_refs_left = map->nr_refs; + int i; + + if (buffer) + xen_fe_map_release_pmd(map, buffer); + + for (i = 0; i < map->nr_pte_tables; i++) { + int j, nr_refs = nr_refs_left; + + if (nr_refs > PTE_ENTRIES_MAX) + nr_refs = PTE_ENTRIES_MAX; + + for (j = 0; j < nr_refs; j++) { + gnttab_end_foreign_access(map->pte_tables[i].refs[j], + map->readonly, 0); + map->refs_granted--; + nr_refs_left--; + mc_dev_devel("unmapped [%d, %d] ref %u, left %d", + i, j, map->pte_tables[i].refs[j], + nr_refs_left); + } + + free_page(map->pte_tables[i].page); + map->pages_created--; + } + + kfree(map->pte_tables); + if (map->pages_created || map->refs_granted) + mc_dev_err(-EUCLEAN, + "leak detected: still in use %d, still ref'd %d", + map->pages_created, map->refs_granted); + + kfree(map); + atomic_dec(&g_ctx.c_xen_maps); + mc_dev_devel("freed map %p: refs=%u nr_pte_tables=%d", + map, map->nr_refs, map->nr_pte_tables); +} + +static void xen_fe_map_delete(void *arg) +{ + struct xen_fe_map *map = arg; + + xen_fe_map_release(map, NULL); +} + +static struct xen_fe_map *xen_fe_map_create(struct tee_xen_buffer *buffer, + const struct mcp_buffer_map *b_map, + int dom_id) +{ + /* b_map describes the PMD which contains pointers to PTE tables */ + uintptr_t *pte_tables = (uintptr_t *)(uintptr_t)b_map->addr; + struct xen_fe_map *map; + unsigned long nr_pte_tables = + (b_map->nr_pages + PTE_ENTRIES_MAX - 1) / PTE_ENTRIES_MAX; + unsigned long nr_pages_left = b_map->nr_pages; + int readonly = !(b_map->flags & MC_IO_MAP_OUTPUT); + int ret, i; + + /* + * We always map the same way, to simplify: + * * the buffer contains references to PTE pages + * * PTE pages contain references to the buffer pages + */ + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return ERR_PTR(-ENOMEM); + + atomic_inc(&g_ctx.c_xen_maps); + map->readonly = readonly; + + map->pte_tables = kcalloc(nr_pte_tables, + sizeof(union tee_xen_mmu_table), GFP_KERNEL); + if (!map->pte_tables) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < nr_pte_tables; i++) { + /* As expected, PTE tables contain pointers to buffer pages */ + struct page **pages = (struct page **)pte_tables[i]; + unsigned long nr_pages = nr_pages_left; + int j; + + map->pte_tables[i].page = get_zeroed_page(GFP_KERNEL); + if (!map->pte_tables[i].page) { + ret = -ENOMEM; + goto err; + } + map->pages_created++; + map->nr_pte_tables++; + + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + /* Create ref for this PTE table */ + ret = gnttab_grant_foreign_access( + dom_id, virt_to_gfn(map->pte_tables[i].addr), true); + if (ret < 0) { + mc_dev_err( + ret, + "gnttab_grant_foreign_access failed:\t" + "PTE table %d", i); + goto err; + } + + map->refs_granted++; + buffer->data.refs[i] = ret; + mc_dev_devel("mapped table %d ref %u for %lu pages", + i, buffer->data.refs[i], nr_pages); + + /* Create refs for pages */ + for (j = 0; j < nr_pages; j++) { + ret = gnttab_grant_foreign_access( + dom_id, page_to_gfn(pages[j]), readonly); + if (ret < 0) { + mc_dev_err( + ret, + "gnttab_grant_foreign_access failed:\t" + "PTE %d pg %d", i, j); + goto err; + } + + map->refs_granted++; + map->pte_tables[i].refs[j] = ret; + map->nr_refs++; + nr_pages_left--; + mc_dev_devel("mapped [%d, %d] ref %u, left %lu", + i, j, map->pte_tables[i].refs[j], + nr_pages_left); + } + } + + buffer->info->nr_refs = map->nr_refs; + buffer->info->addr = (uintptr_t)b_map->mmu; + buffer->info->offset = b_map->offset; + buffer->info->length = b_map->length; + buffer->info->flags = b_map->flags; + + /* Auto-delete */ + map->deleter.object = map; + map->deleter.delete = xen_fe_map_delete; + tee_mmu_set_deleter(b_map->mmu, &map->deleter); + + mc_dev_devel("created map %p: refs=%u nr_pte_tables=%d", + map, map->nr_refs, map->nr_pte_tables); + return map; + +err: + xen_fe_map_release(map, buffer); + return ERR_PTR(ret); +} + +/* DomU call to Dom0 */ + +/* Must be called under xfe->ring_mutex */ +static inline void call_dom0(struct tee_xfe *xfe, enum tee_xen_domu_cmd cmd) +{ + WARN_ON(!xfe->ring_busy); + + xfe->domu_cmd_id++; + if (!xfe->domu_cmd_id) + xfe->domu_cmd_id++; + + /* Set command and ID */ + xfe->ring->domu.cmd = cmd; + xfe->ring->domu.id = xfe->domu_cmd_id; + mc_dev_devel("DomU -> Dom0 request %u id %u pid %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, current->pid); + /* Call */ + notify_remote_via_evtchn(xfe->evtchn_domu); + wait_for_completion(&xfe->ring_completion); +} + +/* Will be called back under xfe->ring_mutex */ +static irqreturn_t xen_fe_irq_handler_domu_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + WARN_ON(!xfe->ring_busy); + + /* Response to a domU command, our side of ring locked by us */ + mc_dev_devel("DomU -> Dom0 response %u id %u ret %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, + xfe->ring->domu.otherend_ret); + xfe->ring->domu.cmd = TEE_XEN_DOMU_NONE; + xfe->ring->domu.id = 0; + complete(&xfe->ring_completion); + + return IRQ_HANDLED; +} + +/* MC protocol interface */ + +int xen_mc_get_version(struct mc_version_info *version_info) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + ring_get(xfe); + /* Call */ + call_dom0(xfe, TEE_XEN_GET_VERSION); + /* Out */ + memcpy(version_info, &xfe->ring->domu.version_info, + sizeof(*version_info)); + ring_put(xfe); + return xfe->ring->domu.otherend_ret; +} + +int xen_mc_open_session(struct mcp_session *session, + struct mcp_open_info *info) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + struct tee_xen_buffer *ta_buffer = &xfe->buffers[1]; + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct xen_fe_map *ta_map = NULL; + struct xen_fe_map *tci_map = NULL; + struct tee_mmu *mmu = NULL; + enum tee_xen_domu_cmd cmd; + int ret; + + fe_mc_session = kzalloc(sizeof(*fe_mc_session), GFP_KERNEL); + if (!fe_mc_session) + return -ENOMEM; + + INIT_LIST_HEAD(&fe_mc_session->list); + init_completion(&fe_mc_session->completion); + fe_mc_session->session = session; + + ring_get(xfe); + /* In */ + if (info->type == TEE_MC_UUID) { + cmd = TEE_XEN_MC_OPEN_SESSION; + xfe->ring->domu.uuid = *info->uuid; + } else { + struct mc_ioctl_buffer buf = { + .va = info->va, + .len = info->len, + .flags = MC_IO_MAP_INPUT, + }; + struct mcp_buffer_map b_map; + + cmd = TEE_XEN_MC_OPEN_TRUSTLET; + /* Use an otherwise unused field to pass the SPID */ + xfe->ring->domu.spid = info->spid; + mmu = tee_mmu_create(info->user ? current->mm : NULL, &buf); + if (IS_ERR(mmu)) { + ret = PTR_ERR(mmu); + mmu = NULL; + goto out; + } + + tee_mmu_buffer(mmu, &b_map); + ta_map = xen_fe_map_create(ta_buffer, &b_map, + xfe->xdev->otherend_id); + if (IS_ERR(ta_map)) { + ret = PTR_ERR(ta_map); + goto out; + } + } + + /* Convert IPAs to grant references in-place */ + if (info->tci_mmu) { + struct mcp_buffer_map b_map; + + tee_mmu_buffer(info->tci_mmu, &b_map); + tci_map = xen_fe_map_create(tci_buffer, &b_map, + xfe->xdev->otherend_id); + if (IS_ERR(tci_map)) { + ret = PTR_ERR(tci_map); + goto out; + } + } else { + tci_buffer->info->flags = 0; + } + + /* Call */ + call_dom0(xfe, cmd); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + session->sid = xfe->ring->domu.session_id; + +out: + if (!ret) { + mutex_lock(&l_ctx.mc_sessions_lock); + list_add_tail(&fe_mc_session->list, &l_ctx.mc_sessions); + mutex_unlock(&l_ctx.mc_sessions_lock); + } else { + kfree(fe_mc_session); + } + + xen_fe_map_release_pmd(ta_map, ta_buffer); + xen_fe_map_release_pmd(tci_map, tci_buffer); + if (mmu) + tee_mmu_put(mmu); + + ring_put(xfe); + return ret; +} + +int xen_mc_close_session(struct mcp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + int ret; + + fe_mc_session = find_mc_session(session->sid); + if (!fe_mc_session) + return -ENXIO; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_CLOSE_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + mutex_lock(&l_ctx.mc_sessions_lock); + session->state = MCP_SESSION_CLOSED; + list_del(&fe_mc_session->list); + mutex_unlock(&l_ctx.mc_sessions_lock); + kfree(fe_mc_session); + } + + ring_put(xfe); + return ret; +} + +int xen_mc_notify(struct mcp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + mc_dev_devel("MC notify session %x", session->sid); + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_NOTIFY); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + return ret; +} + +int xen_mc_wait(struct mcp_session *session, s32 timeout, bool silent_expiry) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + int ret; + + /* Locked by caller so no two waits can happen on one session */ + fe_mc_session = find_mc_session(session->sid); + if (!fe_mc_session) + return -ENXIO; + + fe_mc_session->ret = 0; + + mc_dev_devel("MC wait session %x", session->sid); + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.timeout = timeout; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_WAIT); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + + if (ret) + return ret; + + /* Now wait for notification from Dom0 */ + ret = wait_for_completion_interruptible(&fe_mc_session->completion); + if (!ret) + ret = fe_mc_session->ret; + + return ret; +} + +int xen_mc_map(u32 session_id, struct tee_mmu *mmu, u32 *sva) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mcp_buffer_map b_map; + struct xen_fe_map *map = NULL; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session_id; + tee_mmu_buffer(mmu, &b_map); + map = xen_fe_map_create(buffer, &b_map, xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Call */ + call_dom0(xfe, TEE_XEN_MC_MAP); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + *sva = buffer->info->sva; + atomic_inc(&g_ctx.c_maps); + } + +out: + xen_fe_map_release_pmd(map, buffer); + ring_put(xfe); + return ret; +} + +int xen_mc_unmap(u32 session_id, const struct mcp_buffer_map *map) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session_id; + buffer->info->length = map->length; + buffer->info->sva = map->secure_va; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_UNMAP); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + atomic_dec(&g_ctx.c_maps); + + ring_put(xfe); + return ret; +} + +int xen_mc_get_err(struct mcp_session *session, s32 *err) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_GET_ERR); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + *err = xfe->ring->domu.err; + + mc_dev_devel("MC get_err session %x err %d", session->sid, *err); + ring_put(xfe); + return ret; +} + +/* GP protocol interface */ + +int xen_gp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mcp_buffer_map b_map; + struct xen_fe_map *map = NULL; + int ret; + + ring_get(xfe); + /* In */ + tee_mmu_buffer(mmu, &b_map); + map = xen_fe_map_create(buffer, &b_map, xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Call */ + call_dom0(xfe, TEE_XEN_GP_REGISTER_SHARED_MEM); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + *sva = buffer->info->sva; + atomic_inc(&g_ctx.c_maps); + } + + if (xfe->ring->domu.gp_ret.origin) + *gp_ret = xfe->ring->domu.gp_ret; + +out: + xen_fe_map_release_pmd(map, buffer); + ring_put(xfe); + return ret; +} + +int xen_gp_release_shared_mem(struct mcp_buffer_map *map) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + int ret; + + ring_get(xfe); + /* In */ + buffer->info->addr = (uintptr_t)map->mmu; + buffer->info->length = map->length; + buffer->info->flags = map->flags; + buffer->info->sva = map->secure_va; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_RELEASE_SHARED_MEM); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + atomic_dec(&g_ctx.c_maps); + + ring_put(xfe); + return ret; +} + +int xen_gp_open_session(struct iwp_session *session, + const struct mc_uuid_t *uuid, + const struct iwp_buffer_map *b_maps, + struct interworld_session *iws, + struct interworld_session *op_iws, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + struct xen_fe_map *maps[4] = { NULL, NULL, NULL, NULL }; + int i, ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + operation.gp_ret = gp_ret; + operation.iws = iws; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* The operation may contain tmpref's to map */ + for (i = 0; i < TEE_BUFFERS; i++) { + if (!b_maps[i].map.addr) { + xfe->buffers[i].info->flags = 0; + continue; + } + + maps[i] = xen_fe_map_create(&xfe->buffers[i], &b_maps[i].map, + xfe->xdev->otherend_id); + if (IS_ERR(maps[i])) { + ret = PTR_ERR(maps[i]); + goto err; + } + } + + /* In */ + xfe->ring->domu.uuid = *uuid; + xfe->ring->domu.operation_id = session->slot; + xfe->ring->domu.iws = *op_iws; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_OPEN_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; +err: + for (i = 0; i < TEE_BUFFERS; i++) + xen_fe_map_release_pmd(maps[i], &xfe->buffers[i]); + + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + /* FIXME origins? */ + return operation.ret; +} + +int xen_gp_close_session(struct iwp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + int ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.operation_id = session->slot; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_CLOSE_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + return operation.ret; +} + +int xen_gp_invoke_command(struct iwp_session *session, + const struct iwp_buffer_map *b_maps, + struct interworld_session *iws, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + struct xen_fe_map *maps[4] = { NULL, NULL, NULL, NULL }; + int i, ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + operation.gp_ret = gp_ret; + operation.iws = iws; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* The operation is in op_iws and may contain tmpref's to map */ + for (i = 0; i < TEE_BUFFERS; i++) { + if (!b_maps[i].map.addr) { + xfe->buffers[i].info->flags = 0; + continue; + } + + maps[i] = xen_fe_map_create(&xfe->buffers[i], &b_maps[i].map, + xfe->xdev->otherend_id); + if (IS_ERR(maps[i])) { + ret = PTR_ERR(maps[i]); + goto err; + } + } + + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.operation_id = session->slot; + xfe->ring->domu.iws = *iws; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_INVOKE_COMMAND); + /* Out */ + ret = xfe->ring->domu.otherend_ret; +err: + for (i = 0; i < TEE_BUFFERS; i++) + xen_fe_map_release_pmd(maps[i], &xfe->buffers[i]); + + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + /* FIXME origins? */ + return operation.ret; +} + +int xen_gp_request_cancellation(u64 slot) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.operation_id = slot; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_REQUEST_CANCELLATION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + return ret; +} + +/* Device */ + +static inline void xfe_release(struct tee_xfe *xfe) +{ + int i; + + if (xfe->irq_domu >= 0) + unbind_from_irqhandler(xfe->irq_domu, xfe); + + if (xfe->irq_dom0 >= 0) + unbind_from_irqhandler(xfe->irq_dom0, xfe); + + if (xfe->evtchn_domu >= 0) + xenbus_free_evtchn(xfe->xdev, xfe->evtchn_domu); + + if (xfe->evtchn_dom0 >= 0) + xenbus_free_evtchn(xfe->xdev, xfe->evtchn_dom0); + + for (i = 0; i < TEE_BUFFERS; i++) { + if (!xfe->buffers[i].data.page) + break; + + gnttab_end_foreign_access(xfe->ring->domu.buffers[i].pmd_ref, 0, + xfe->buffers[i].data.page); + free_page(xfe->buffers[i].data.page); + } + + if (xfe->ring_ul) { + gnttab_end_foreign_access(xfe->ring_ref, 0, xfe->ring_ul); + free_page(xfe->ring_ul); + } + + kfree(xfe); +} + +static inline struct tee_xfe *xfe_create(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe; + struct xenbus_transaction trans; + int i, ret = -ENOMEM; + + /* Alloc */ + xfe = tee_xfe_create(xdev); + if (!xfe) + return ERR_PTR(-ENOMEM); + + /* Create shared information buffer */ + xfe->ring_ul = get_zeroed_page(GFP_KERNEL); + if (!xfe->ring_ul) + goto err; + + /* Connect */ + ret = xenbus_grant_ring(xfe->xdev, xfe->ring, 1, &xfe->ring_ref); + if (ret < 0) + goto err; + + for (i = 0; i < TEE_BUFFERS; i++) { + xfe->buffers[i].data.page = get_zeroed_page(GFP_KERNEL); + if (!xfe->buffers[i].data.page) + goto err; + + ret = xenbus_grant_ring(xfe->xdev, xfe->buffers[i].data.addr, 1, + &xfe->ring->domu.buffers[i].pmd_ref); + if (ret < 0) + goto err; + + xfe->buffers[i].info = &xfe->ring->domu.buffers[i]; + } + + ret = xenbus_alloc_evtchn(xfe->xdev, &xfe->evtchn_domu); + if (ret) + goto err; + + ret = xenbus_alloc_evtchn(xfe->xdev, &xfe->evtchn_dom0); + if (ret) + goto err; + + ret = bind_evtchn_to_irqhandler(xfe->evtchn_domu, + xen_fe_irq_handler_domu_th, 0, + "tee_fe_domu", xfe); + if (ret < 0) + goto err; + + xfe->irq_domu = ret; + + ret = bind_evtchn_to_irqhandler(xfe->evtchn_dom0, + xen_fe_irq_handler_dom0_th, 0, + "tee_fe_dom0", xfe); + if (ret < 0) + goto err; + + xfe->irq_dom0 = ret; + + /* Publish */ + do { + ret = xenbus_transaction_start(&trans); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to start transaction"); + goto err_transaction; + } + + /* Ring is one page to support older kernels */ + ret = xenbus_printf(trans, xfe->xdev->nodename, + "ring-ref", "%u", xfe->ring_ref); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write ring ref"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "pte-entries-max", "%u", + PTE_ENTRIES_MAX); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write PTE entries max"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "event-channel-domu", "%u", + xfe->evtchn_domu); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write event channel domu"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "event-channel-dom0", "%u", + xfe->evtchn_dom0); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write event channel dom0"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "domu-version", "%u", TEE_XEN_VERSION); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write version"); + goto err_transaction; + } + + ret = xenbus_transaction_end(trans, 0); + if (ret) { + if (ret == -EAGAIN) + mc_dev_devel("retry"); + else + xenbus_dev_fatal(xfe->xdev, ret, + "failed to end transaction"); + } + } while (ret == -EAGAIN); + + mc_dev_devel("evtchn domu=%u dom0=%u version=%u", + xfe->evtchn_domu, xfe->evtchn_dom0, TEE_XEN_VERSION); + xenbus_switch_state(xfe->xdev, XenbusStateInitialised); + return xfe; + +err_transaction: +err: + xenbus_switch_state(xfe->xdev, XenbusStateClosed); + xfe_release(xfe); + return ERR_PTR(ret); +} + +static const struct xenbus_device_id xen_fe_ids[] = { + { "tee_xen" }, + { "" } +}; + +static int xen_fe_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + int ret; + + ret = l_ctx.probe(); + if (ret) + return ret; + + l_ctx.xfe = xfe_create(xdev); + if (IS_ERR(l_ctx.xfe)) + return PTR_ERR(l_ctx.xfe); + + INIT_WORK(&l_ctx.xfe->work, xen_fe_irq_handler_dom0_bh); + + return 0; +} + +static void xen_fe_backend_changed(struct xenbus_device *xdev, + enum xenbus_state be_state) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + mc_dev_devel("be state changed to %d", be_state); + + if (be_state == l_ctx.last_be_state) { + /* Protection against duplicated notifications (TBUG-1387) */ + mc_dev_devel("be state (%d) already set... ignoring", be_state); + return; + } + + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + break; + case XenbusStateConnected: + if (l_ctx.start()) + xenbus_switch_state(xfe->xdev, XenbusStateClosing); + else + xenbus_switch_state(xfe->xdev, XenbusStateConnected); + break; + case XenbusStateClosing: + case XenbusStateClosed: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + break; + } + + /* Refresh last back-end state */ + l_ctx.last_be_state = be_state; +} + +static struct xenbus_driver xen_fe_driver = { + .ids = xen_fe_ids, + .probe = xen_fe_probe, + .otherend_changed = xen_fe_backend_changed, +}; + +int xen_fe_init(int (*probe)(void), int (*start)(void)) +{ + l_ctx.probe = probe; + l_ctx.start = start; + mutex_init(&l_ctx.mc_sessions_lock); + INIT_LIST_HEAD(&l_ctx.mc_sessions); + mutex_init(&l_ctx.gp_operations_lock); + INIT_LIST_HEAD(&l_ctx.gp_operations); + return xenbus_register_frontend(&xen_fe_driver); +} + +void xen_fe_exit(void) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + tee_xfe_put(xfe); + xenbus_unregister_driver(&xen_fe_driver); +} + +#endif /* TRUSTONIC_XEN_DOMU */ diff --git a/drivers/gud/MobiCoreDriver/xen_fe.h b/drivers/gud/MobiCoreDriver/xen_fe.h new file mode 100644 index 000000000000..f4eec823d2cc --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_fe.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MC_XEN_FE_H_ +#define _MC_XEN_FE_H_ + +#include + +#include "main.h" +#include "client.h" +#include "iwp.h" +#include "mcp.h" + +#ifdef TRUSTONIC_XEN_DOMU +/* MC protocol interface */ +int xen_mc_get_version(struct mc_version_info *version_info); +int xen_mc_open_session(struct mcp_session *session, + struct mcp_open_info *info); +int xen_mc_close_session(struct mcp_session *session); +int xen_mc_map(u32 session_id, struct tee_mmu *mmu, u32 *sva); +int xen_mc_unmap(u32 session_id, const struct mcp_buffer_map *map); +int xen_mc_notify(struct mcp_session *session); +int xen_mc_wait(struct mcp_session *session, s32 timeout, bool silent_expiry); +int xen_mc_get_err(struct mcp_session *session, s32 *err); +/* GP protocol interface */ +int xen_gp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, + struct gp_return *gp_ret); +int xen_gp_release_shared_mem(struct mcp_buffer_map *map); +int xen_gp_open_session(struct iwp_session *session, + const struct mc_uuid_t *uuid, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct interworld_session *op_iws, + struct gp_return *gp_ret); +int xen_gp_close_session(struct iwp_session *session); +int xen_gp_invoke_command(struct iwp_session *session, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct gp_return *gp_ret); +int xen_gp_request_cancellation(u64 slot); + +int xen_fe_init(int (*probe)(void), int (*start)(void)); +void xen_fe_exit(void); +#else +static inline int xen_fe_init(int (*probe)(void), int (*start)(void)) +{ + return 0; +} + +static inline void xen_fe_exit(void) +{ +} +#endif + +#endif /* _MC_XEN_FE_H_ */