Add Trustonic TEE driver module

Cherry-picked from: (CR)
Updated mcp.c, mcp.h and platform.h to make driver
compatible to kernel 4.19.
Add the Trustonic TEE driver module to the kernel.  This is needed
to run trusted applications in the Trustonic TEE.
Updated Kbuild with current files.

Kinibi version t-base-QC8996-Android-410a-V106.

Change-Id: Id237e0acb2b0e6fe37045796106e3d1e5cb4b1b0
Reviewed-on: https://gerrit.mot.com/1685214
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Joel Voss <jvoss@motorola.com>
Reviewed-by: Konstantin Makariev <kmakariev@motorola.com>
Reviewed-by: Igor Kovalenko <igork@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Jefferson Capovilla 2020-02-20 16:06:07 -03:00 • committed by Jefferson Rodrigo Capovilla
commit a23c1813fc
53 changed files with 6327 additions and 3042 deletions

View file

@ -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

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
* All Rights Reserved.
@ -25,13 +26,15 @@
#include <linux/module.h>
#include <linux/random.h>
#include <linux/delay.h>
#include <linux/version.h>
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
#include <linux/sched/signal.h> /* signal_pending */
#endif
#include <linux/freezer.h>
#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();
}

View file

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2013-2017 TRUSTONIC LIMITED
* All Rights Reserved.

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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 <linux/list.h>
#include <linux/sched.h> /* 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,

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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 <linux/kthread.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/cpu.h>
#include <linux/moduleparam.h>
#include <linux/debugfs.h>
#include <linux/sched.h> /* local_clock */
#include <linux/version.h>
#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
#include <linux/sched/clock.h> /* local_clock */
#endif
#include <linux/uaccess.h>
#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

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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 <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/device.h>
@ -20,9 +21,7 @@
#include <linux/version.h>
#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");
}

View file

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

View file

@ -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 <linux/cdev.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/suspend.h>
#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);
}

View file

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
* All Rights Reserved.
@ -20,14 +21,16 @@
#include <linux/fs.h> /* struct inode and struct file */
#include <linux/mutex.h>
#include <linux/version.h>
#include <xen/xen.h>
#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_ */

View file

@ -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

View file

@ -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

View file

@ -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.
*/

View file

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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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();
}

View file

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

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2018 TRUSTONIC LIMITED
* All Rights Reserved.
@ -24,15 +25,22 @@
#include <linux/version.h>
#include <linux/dma-buf.h>
#ifdef CONFIG_XEN
/* To get the MFN */
#include <linux/pfn.h>
#include <xen/page.h>
#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);
}

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -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 <linux/list.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
/* 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_ */

View file

@ -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 <linux/types.h>
/*--------------- Implementation -------------- */
#include <soc/qcom/scm.h>
#include <soc/qcom/qseecomi.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <linux/errno.h>
#include <linux/types.h>
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
#define USE_SHM_BRIDGE
#endif
#include <soc/qcom/scm.h>
#include <soc/qcom/qseecomi.h>
#if defined USE_SHM_BRIDGE
#include <soc/qcom/qtee_shmbridge.h>
#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_ */

View file

@ -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 <asm/bL_switcher.h>
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 */

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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 <linux/types.h>
@ -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 */
};
/*

View file

@ -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

View file

@ -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 <linux/slab.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/stringify.h>
#include <linux/version.h>
#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;
}

View file

@ -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__ */

View file

@ -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 ? " <closing>" : "");
err, is_closing ? " <closing>" : "");
if (ret < 0)
return ret;

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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 <linux/version.h>
#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_ */

View file

@ -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

View file

@ -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 <linux/list.h>
#include <linux/workqueue.h>
#include <xen/events.h>
#include <xen/grant_table.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
#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_ */

File diff suppressed because it is too large Load diff

View file

@ -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 <linux/version.h>
#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_ */