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_ */