walnux/drivers/misc/optee.c
George Poulios 5c8e5d2ef2 drivers/misc/optee.c: Close session on open error
Signed-off-by: George Poulios <gpoulios@census-labs.com>
2025-05-09 10:29:26 +08:00

526 lines
15 KiB
C

/****************************************************************************
* drivers/misc/optee.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/tee.h>
#include <fcntl.h>
#include <nuttx/drivers/optee.h>
#include <nuttx/fs/fs.h>
#include <nuttx/kmalloc.h>
#include <sys/mman.h>
#include <sys/param.h>
#include "optee.h"
/****************************************************************************
* The driver's main purpose is to support the porting of the open source
* component optee_client (https://github.com/OP-TEE/optee_client) to NuttX.
* The basic function of the driver module is to convert the REE application
* layer data and send it to the TEE through rpmsg or SMCs. TEE
* implementation is optee_os(https://github.com/OP-TEE/optee_os).
****************************************************************************/
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Some GlobalPlatform error codes used in this driver */
#define TEE_SUCCESS 0x00000000
#define TEE_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEE_ERROR_NOT_SUPPORTED 0xFFFF000A
#define TEE_ERROR_COMMUNICATION 0xFFFF000E
#define TEE_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEE_ERROR_BUSY 0xFFFF000D
#define TEE_ERROR_SHORT_BUFFER 0xFFFF0010
#define TEE_ORIGIN_COMMS 0x00000002
#define OPTEE_DEV_PATH "/dev/tee0"
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* The file operation functions */
static int optee_open(FAR struct file *filep);
static int optee_close(FAR struct file *filep);
static int optee_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
/* File operations */
static const struct file_operations g_optee_ops =
{
optee_open, /* open */
optee_close, /* close */
NULL, /* read */
NULL, /* write */
NULL, /* seek */
optee_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
NULL /* poll */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: optee_open
*
* Description:
* optee open operation
*
* Parameters:
* filep - the file instance
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int optee_open(FAR struct file *filep)
{
FAR struct optee_priv_data *priv;
int ret;
ret = optee_transport_open(&priv);
if (ret < 0)
{
return ret;
}
filep->f_priv = priv;
return 0;
}
/****************************************************************************
* Name: optee_close
*
* Description:
* optee close operation
*
* Parameters:
* filep - the file instance
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int optee_close(FAR struct file *filep)
{
FAR struct optee_priv_data *priv = filep->f_priv;
optee_transport_close(priv);
return 0;
}
static int optee_to_msg_param(FAR struct optee_msg_param *mparams,
size_t num_params,
FAR const struct tee_ioctl_param *params)
{
size_t n;
for (n = 0; n < num_params; n++)
{
FAR const struct tee_ioctl_param *p = params + n;
FAR struct optee_msg_param *mp = mparams + n;
if (p->attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
{
return -EINVAL;
}
switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
{
case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
break;
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
mp->u.value.a = p->a;
mp->u.value.b = p->b;
mp->u.value.c = p->c;
break;
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
if (p->c != TEE_MEMREF_NULL)
{
mp->u.rmem.shm_ref = p->c;
}
else
{
mp->u.rmem.shm_ref = 0;
}
mp->u.rmem.size = p->b;
mp->u.rmem.offs = p->a;
break;
default:
return -EINVAL;
}
}
return 0;
}
static int optee_from_msg_param(FAR struct tee_ioctl_param *params,
size_t num_params,
FAR const struct optee_msg_param *mparams)
{
size_t n;
for (n = 0; n < num_params; n++)
{
FAR const struct optee_msg_param *mp = mparams + n;
FAR struct tee_ioctl_param *p = params + n;
switch (mp->attr & OPTEE_MSG_ATTR_TYPE_MASK)
{
case OPTEE_MSG_ATTR_TYPE_NONE:
p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
p->a = 0;
p->b = 0;
p->c = 0;
break;
case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
mp->attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
p->a = mp->u.value.a;
p->b = mp->u.value.b;
p->c = mp->u.value.c;
break;
case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
mp->attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
p->b = mp->u.rmem.size;
break;
default:
return -EINVAL;
}
}
return 0;
}
static int optee_close_session(FAR struct optee_priv_data *priv,
uint32_t session)
{
struct optee_msg_arg msg;
memset(&msg, 0, sizeof(struct optee_msg_arg));
msg.cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
msg.session = session;
return optee_transport_call(priv, &msg);
}
static int optee_ioctl_open_session(FAR struct optee_priv_data *priv,
FAR struct tee_ioctl_buf_data *buf)
{
char msg_buf[OPTEE_MSG_GET_ARG_SIZE(OPTEE_MAX_PARAM_NUM)];
FAR struct tee_ioctl_open_session_arg *arg;
FAR struct optee_msg_arg *msg;
int ret;
if (buf->buf_len > TEE_MAX_ARG_SIZE ||
buf->buf_len < sizeof(struct tee_ioctl_open_session_arg))
{
return -EINVAL;
}
arg = (FAR struct tee_ioctl_open_session_arg *)(uintptr_t)buf->buf_ptr;
if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) !=
buf->buf_len)
{
return -EINVAL;
}
if (arg->num_params + 2 > OPTEE_MAX_PARAM_NUM)
{
return -EINVAL;
}
if (arg->clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN &&
arg->clnt_login <= TEE_IOCTL_LOGIN_REE_KERNEL_MAX)
{
return -EPERM;
}
arg->ret = TEE_ERROR_COMMUNICATION;
arg->ret_origin = TEE_ORIGIN_COMMS;
memset(msg_buf, 0, sizeof(msg_buf));
msg = (FAR struct optee_msg_arg *)&msg_buf[0];
msg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
msg->cancel_id = arg->cancel_id;
msg->num_params = arg->num_params + 2;
/* Initialize and add the meta parameters needed when opening a
* session.
*/
msg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
OPTEE_MSG_ATTR_META;
msg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
OPTEE_MSG_ATTR_META;
memcpy(&msg->params[0].u.value, arg->uuid, sizeof(arg->uuid));
msg->params[1].u.value.c = arg->clnt_login;
ret = optee_to_msg_param(msg->params + 2, arg->num_params, arg->params);
if (ret < 0)
{
return ret;
}
ret = optee_transport_call(priv, msg);
if (ret < 0)
{
return ret;
}
ret = optee_from_msg_param(arg->params, arg->num_params,
msg->params + 2);
if (ret < 0)
{
optee_close_session(priv, msg->session);
return ret;
}
arg->session = msg->session;
arg->ret = msg->ret;
arg->ret_origin = msg->ret_origin;
return ret;
}
static int optee_ioctl_invoke(FAR struct optee_priv_data *priv,
FAR struct tee_ioctl_buf_data *buf)
{
char msg_buf[OPTEE_MSG_GET_ARG_SIZE(OPTEE_MAX_PARAM_NUM)];
FAR struct tee_ioctl_invoke_arg *arg;
FAR struct optee_msg_arg *msg;
int ret;
if (buf->buf_len > TEE_MAX_ARG_SIZE ||
buf->buf_len < sizeof(struct tee_ioctl_invoke_arg))
{
return -EINVAL;
}
arg = (FAR struct tee_ioctl_invoke_arg *)(uintptr_t)buf->buf_ptr;
if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) !=
buf->buf_len)
{
return -EINVAL;
}
if (arg->num_params > OPTEE_MAX_PARAM_NUM)
{
return -EINVAL;
}
arg->ret = TEE_ERROR_COMMUNICATION;
arg->ret_origin = TEE_ORIGIN_COMMS;
memset(msg_buf, 0, sizeof(msg_buf));
msg = (FAR struct optee_msg_arg *)&msg_buf[0];
msg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND;
msg->func = arg->func;
msg->session = arg->session;
msg->cancel_id = arg->cancel_id;
msg->num_params = arg->num_params;
ret = optee_to_msg_param(msg->params, arg->num_params, arg->params);
if (ret < 0)
{
return ret;
}
ret = optee_transport_call(priv, msg);
if (ret < 0)
{
return ret;
}
ret = optee_from_msg_param(arg->params, arg->num_params, msg->params);
if (ret < 0)
{
return ret;
}
arg->ret = msg->ret;
arg->ret_origin = msg->ret_origin;
return ret;
}
static int
optee_ioctl_close_session(FAR struct optee_priv_data *priv,
FAR struct tee_ioctl_close_session_arg *arg)
{
return optee_close_session(priv, arg->session);
}
static int optee_ioctl_version(FAR struct tee_ioctl_version_data *vers)
{
vers->impl_id = TEE_IMPL_ID_OPTEE;
vers->impl_caps = TEE_OPTEE_CAP_TZ;
vers->gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_MEMREF_NULL;
return 0;
}
static int optee_ioctl_cancel(FAR struct optee_priv_data *priv,
FAR struct tee_ioctl_cancel_arg *arg)
{
struct optee_msg_arg msg;
memset(&msg, 0, sizeof(struct optee_msg_arg));
msg.cmd = OPTEE_MSG_CMD_CANCEL;
msg.session = arg->session;
msg.cancel_id = arg->cancel_id;
return optee_transport_call(priv, &msg);
}
static int
optee_ioctl_shm_alloc(FAR struct tee_ioctl_shm_alloc_data *data)
{
int memfd = memfd_create(OPTEE_SERVER_PATH, O_CREAT | O_CLOEXEC);
if (memfd < 0)
{
return get_errno();
}
if (ftruncate(memfd, data->size) < 0)
{
close(memfd);
return get_errno();
}
data->id = (uintptr_t)mmap(NULL, data->size, PROT_READ | PROT_WRITE,
MAP_SHARED, memfd, 0);
if (data->id == (uintptr_t)MAP_FAILED)
{
return get_errno();
}
return memfd;
}
/****************************************************************************
* Name: optee_ioctl
*
* Description:
* optee ioctl operation
*
* Parameters:
* filep - the file instance
* cmd - the ioctl command
* arg - the ioctl arguments
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int optee_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct optee_priv_data *priv = filep->f_priv;
FAR void *parg = (FAR void *)arg;
switch (cmd)
{
case TEE_IOC_VERSION:
return optee_ioctl_version(parg);
case TEE_IOC_OPEN_SESSION:
return optee_ioctl_open_session(priv, parg);
case TEE_IOC_INVOKE:
return optee_ioctl_invoke(priv, parg);
case TEE_IOC_CLOSE_SESSION:
return optee_ioctl_close_session(priv, parg);
case TEE_IOC_CANCEL:
return optee_ioctl_cancel(priv, parg);
case TEE_IOC_SHM_ALLOC:
return optee_ioctl_shm_alloc(parg);
default:
return -ENOTTY;
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: optee_register
*
* Description:
* optee client initialize function, the client cpu should call
* this function in the board initialize process.
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
int optee_register(void)
{
int ret;
ret = optee_transport_init();
if (ret < 0)
{
return ret;
}
return register_driver(OPTEE_DEV_PATH, &g_optee_ops, 0666, NULL);
}