Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
437 lines
9.4 KiB
C
437 lines
9.4 KiB
C
/*
|
|
* Copyright (c) 2015-2019, MICROTRUST Incorporated
|
|
* 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/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/idr.h>
|
|
|
|
#include <tee_client_api.h>
|
|
#include "capi_proxy.h"
|
|
|
|
#define IMSG_TAG "[tz_capi_proxy]"
|
|
#include <imsg_log.h>
|
|
|
|
#define HOSTNAME "bta_loader"
|
|
|
|
static struct TEEC_Context capi_proxy_ctx;
|
|
|
|
#define MAX_CAPI_SESSIONS 10
|
|
#define MAX_CAPI_SHMS 12
|
|
|
|
static struct TEEC_Session capi_session[MAX_CAPI_SESSIONS];
|
|
static struct TEEC_SharedMemory capi_shm[MAX_CAPI_SHMS];
|
|
|
|
struct ida capi_session_ida;
|
|
struct ida capi_shm_ida;
|
|
|
|
static int alloc_capi_session(void)
|
|
{
|
|
return ida_simple_get(&capi_session_ida, 0,
|
|
MAX_CAPI_SESSIONS, GFP_KERNEL);
|
|
}
|
|
|
|
static void free_capi_session(int id)
|
|
{
|
|
ida_simple_remove(&capi_session_ida, id);
|
|
memset(&capi_session[id], 0, sizeof(struct TEEC_Session));
|
|
}
|
|
|
|
static int alloc_capi_shm(void)
|
|
{
|
|
return ida_simple_get(&capi_shm_ida, 0, MAX_CAPI_SHMS, GFP_KERNEL);
|
|
}
|
|
|
|
static void free_capi_shm(int id)
|
|
{
|
|
ida_simple_remove(&capi_shm_ida, id);
|
|
memset(&capi_shm[id], 0, sizeof(struct TEEC_SharedMemory));
|
|
}
|
|
|
|
static int capi_params_to_op(struct capi_proxy_param *params,
|
|
struct TEEC_Operation *op)
|
|
{
|
|
size_t n;
|
|
long ret = 0;
|
|
union TEEC_Parameter *op_param;
|
|
struct TEEC_SharedMemory *op_shm;
|
|
size_t op_shm_off;
|
|
struct capi_proxy_param_memref *memref;
|
|
|
|
memset(op, 0, sizeof(struct TEEC_Operation));
|
|
|
|
for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
|
|
op->paramTypes |= (uint32_t)params[n].attr << (4 * n);
|
|
op_param = &op->params[n];
|
|
|
|
switch (params[n].attr) {
|
|
case TEEC_NONE:
|
|
break;
|
|
case TEEC_VALUE_INPUT:
|
|
case TEEC_VALUE_OUTPUT:
|
|
case TEEC_VALUE_INOUT:
|
|
op_param->value.a = params[n].value.a;
|
|
op_param->value.b = params[n].value.b;
|
|
break;
|
|
case TEEC_MEMREF_TEMP_INPUT:
|
|
case TEEC_MEMREF_TEMP_OUTPUT:
|
|
case TEEC_MEMREF_TEMP_INOUT:
|
|
memref = ¶ms[n].memref;
|
|
|
|
op_param->tmpref.buffer =
|
|
kmalloc(memref->size, GFP_KERNEL);
|
|
if (!op_param->tmpref.buffer) {
|
|
IMSG_ERROR("failed to allocate buffer\n");
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
op_param->tmpref.size = memref->size;
|
|
|
|
ret = copy_from_user(
|
|
(void *)(unsigned long)
|
|
(op_param->tmpref.buffer),
|
|
(void *)(unsigned long)(memref->buffer),
|
|
memref->size);
|
|
if (ret) {
|
|
IMSG_ERROR("failed to copy from user\n");
|
|
ret = -EIO;
|
|
goto err;
|
|
}
|
|
break;
|
|
case TEEC_MEMREF_WHOLE:
|
|
case TEEC_MEMREF_PARTIAL_INPUT:
|
|
case TEEC_MEMREF_PARTIAL_OUTPUT:
|
|
case TEEC_MEMREF_PARTIAL_INOUT:
|
|
memref = ¶ms[n].memref;
|
|
|
|
op_param->memref.parent =
|
|
&capi_shm[memref->shm_id];
|
|
|
|
op_param->memref.size = memref->size;
|
|
op_param->memref.offset = memref->shm_offset;
|
|
|
|
op_shm = op_param->memref.parent;
|
|
op_shm_off = op_param->memref.offset;
|
|
|
|
ret = copy_from_user(
|
|
(char *)(unsigned long)(op_shm->buffer)
|
|
+ op_shm_off,
|
|
(char *)(unsigned long)(memref->buffer)
|
|
+ op_shm_off,
|
|
memref->size);
|
|
if (ret) {
|
|
IMSG_ERROR("failed to copy from user\n");
|
|
ret = -EIO;
|
|
goto err;
|
|
}
|
|
break;
|
|
default:
|
|
IMSG_ERROR("unknown param type (0x%llx)\n",
|
|
params[n].attr);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
err:
|
|
if (ret) {
|
|
for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
|
|
switch (params[n].attr) {
|
|
case TEEC_MEMREF_TEMP_INPUT:
|
|
case TEEC_MEMREF_TEMP_OUTPUT:
|
|
case TEEC_MEMREF_TEMP_INOUT:
|
|
kfree(op->params[n].tmpref.buffer);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void capi_op_to_params(struct capi_proxy_param *params,
|
|
struct TEEC_Operation *op)
|
|
{
|
|
size_t n;
|
|
int ret;
|
|
union TEEC_Parameter *op_param;
|
|
struct TEEC_SharedMemory *op_shm;
|
|
size_t op_shm_off;
|
|
struct capi_proxy_param_memref *memref;
|
|
|
|
for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
|
|
op_param = &op->params[n];
|
|
|
|
switch (params[n].attr) {
|
|
case TEEC_VALUE_OUTPUT:
|
|
case TEEC_VALUE_INOUT:
|
|
params[n].value.a = op_param->value.a;
|
|
params[n].value.b = op_param->value.b;
|
|
break;
|
|
case TEEC_MEMREF_TEMP_OUTPUT:
|
|
case TEEC_MEMREF_TEMP_INOUT:
|
|
memref = ¶ms[n].memref;
|
|
|
|
memref->size = op_param->tmpref.size;
|
|
|
|
ret = copy_to_user(
|
|
(void *)(unsigned long)(memref->buffer),
|
|
(void *)(unsigned long)
|
|
(op_param->tmpref.buffer),
|
|
memref->size);
|
|
if (ret)
|
|
IMSG_ERROR("failed to copy to user\n");
|
|
|
|
kfree(op->params[n].tmpref.buffer);
|
|
break;
|
|
case TEEC_MEMREF_WHOLE:
|
|
case TEEC_MEMREF_PARTIAL_OUTPUT:
|
|
case TEEC_MEMREF_PARTIAL_INOUT:
|
|
memref = ¶ms[n].memref;
|
|
|
|
memref->size = op_param->memref.size;
|
|
|
|
op_shm = op->params[n].memref.parent;
|
|
op_shm_off = op->params[n].memref.offset;
|
|
ret = copy_to_user(
|
|
(char *)(unsigned long)(memref->buffer)
|
|
+ op_shm_off,
|
|
(char *)(unsigned long)(op_shm->buffer)
|
|
+ op_shm_off,
|
|
memref->size);
|
|
if (ret)
|
|
IMSG_ERROR("failed to copy to user\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int do_capi_init_context(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
arg->ret = TEEC_InitializeContext(HOSTNAME, &capi_proxy_ctx);
|
|
|
|
if (arg->ret)
|
|
return -1;
|
|
|
|
ida_init(&capi_session_ida);
|
|
ida_init(&capi_shm_ida);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_capi_final_context(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
TEEC_FinalizeContext(&capi_proxy_ctx);
|
|
|
|
ida_destroy(&capi_session_ida);
|
|
ida_destroy(&capi_shm_ida);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_capi_open_session(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
struct TEEC_Session *session;
|
|
struct TEEC_Operation op;
|
|
struct TEEC_UUID *uuid = (struct TEEC_UUID *)arg->uuid;
|
|
int ret = 0;
|
|
int sid;
|
|
|
|
sid = alloc_capi_session();
|
|
if (sid < 0) {
|
|
IMSG_ERROR("no free capi session\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
session = &capi_session[sid];
|
|
|
|
capi_params_to_op(arg->params, &op);
|
|
|
|
arg->ret = TEEC_OpenSession(&capi_proxy_ctx, session, uuid,
|
|
TEEC_LOGIN_PUBLIC, NULL, &op, &arg->ret_orig);
|
|
|
|
capi_op_to_params(arg->params, &op);
|
|
|
|
if (arg->ret) {
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
|
|
arg->session = (__u32)sid;
|
|
return 0;
|
|
|
|
err:
|
|
free_capi_session(sid);
|
|
return ret;
|
|
}
|
|
|
|
static int do_capi_invoke_command(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
struct TEEC_Session *session;
|
|
struct TEEC_Operation op;
|
|
|
|
session = &capi_session[arg->session];
|
|
|
|
capi_params_to_op(arg->params, &op);
|
|
|
|
arg->ret = TEEC_InvokeCommand(session, arg->func, &op,
|
|
&arg->ret_orig);
|
|
|
|
capi_op_to_params(arg->params, &op);
|
|
|
|
return (arg->ret) ? -EFAULT : 0;
|
|
}
|
|
|
|
static int do_capi_close_session(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
struct TEEC_Session *session;
|
|
|
|
session = &capi_session[arg->session];
|
|
|
|
TEEC_CloseSession(session);
|
|
free_capi_session((int)arg->session);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_capi_register_shm(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
struct TEEC_SharedMemory *shm;
|
|
int ret = 0;
|
|
int sid;
|
|
|
|
sid = alloc_capi_shm();
|
|
if (sid < 0) {
|
|
IMSG_ERROR("no free capi shm\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
shm = &capi_shm[sid];
|
|
shm->flags = arg->params[0].attr;
|
|
shm->size = arg->params[0].memref.size;
|
|
shm->buffer = kmalloc(shm->size, GFP_KERNEL);
|
|
if (!shm->buffer) {
|
|
IMSG_ERROR("failed to allocate buffer\n");
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
arg->ret = TEEC_RegisterSharedMemory(&capi_proxy_ctx, shm);
|
|
if (arg->ret) {
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
|
|
arg->params[0].memref.shm_id = (__u64)sid;
|
|
return 0;
|
|
|
|
err:
|
|
free_capi_shm(sid);
|
|
return ret;
|
|
}
|
|
|
|
static int do_capi_allocate_shm(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
struct TEEC_SharedMemory *shm;
|
|
int ret = 0;
|
|
int sid;
|
|
|
|
sid = alloc_capi_shm();
|
|
if (sid < 0) {
|
|
IMSG_ERROR("no free capi shm\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
shm = &capi_shm[sid];
|
|
|
|
shm->flags = arg->params[0].attr;
|
|
shm->size = arg->params[0].memref.size;
|
|
|
|
arg->ret = TEEC_AllocateSharedMemory(&capi_proxy_ctx, shm);
|
|
if (arg->ret) {
|
|
ret = -EFAULT;
|
|
goto err;
|
|
}
|
|
|
|
arg->params[0].memref.shm_id = (__u64)sid;
|
|
return 0;
|
|
|
|
err:
|
|
free_capi_shm(sid);
|
|
return ret;
|
|
}
|
|
|
|
static int do_capi_release_shm(struct tee_ioctl_capi_proxy_arg *arg)
|
|
{
|
|
int sid = (int)arg->params[0].memref.shm_id;
|
|
struct TEEC_SharedMemory *shm = &capi_shm[sid];
|
|
|
|
if (shm->buffer && shm->shadow_buffer)
|
|
kfree(shm->buffer);
|
|
|
|
TEEC_ReleaseSharedMemory(shm);
|
|
free_capi_shm(sid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct capi_op {
|
|
int opcode;
|
|
int (*callback)(struct tee_ioctl_capi_proxy_arg *arg);
|
|
};
|
|
|
|
static struct capi_op capi_ops[] = {
|
|
{CAPI_OP_INIT_CONTEXT, do_capi_init_context},
|
|
{CAPI_OP_FINAL_CONTEXT, do_capi_final_context},
|
|
{CAPI_OP_OPEN_SESSION, do_capi_open_session},
|
|
{CAPI_OP_INVOKE_COMMAND, do_capi_invoke_command},
|
|
{CAPI_OP_CLOSE_SESSION, do_capi_close_session},
|
|
{CAPI_OP_REGISTER_SHM, do_capi_register_shm},
|
|
{CAPI_OP_ALLOCATE_SHM, do_capi_allocate_shm},
|
|
{CAPI_OP_RELEASE_SHM, do_capi_release_shm},
|
|
};
|
|
|
|
static const size_t num_capi_ops = sizeof(capi_ops) / sizeof(struct capi_op);
|
|
|
|
int tee_ioctl_capi_proxy(struct tee_context *ctx,
|
|
struct tee_ioctl_capi_proxy_arg __user *uarg)
|
|
{
|
|
struct tee_ioctl_capi_proxy_arg arg;
|
|
int ret;
|
|
|
|
if (copy_from_user(&arg, uarg, sizeof(arg))) {
|
|
IMSG_ERROR("failed to copy from user\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (arg.capi_op >= num_capi_ops) {
|
|
IMSG_ERROR("unknown CAPI operation (%u)\n",
|
|
arg.capi_op);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = capi_ops[arg.capi_op].callback(&arg);
|
|
|
|
if (copy_to_user(uarg, &arg, sizeof(arg))) {
|
|
IMSG_ERROR("failed to copy to user\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|