| /**************************************************************************** |
| * 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 <fcntl.h> |
| #include <nuttx/tee.h> |
| #include <nuttx/nuttx.h> |
| #include <nuttx/cache.h> |
| #include <nuttx/drivers/optee.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/lib/math32.h> |
| #include <sys/mman.h> |
| #include <sys/param.h> |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| # include <nuttx/pgalloc.h> |
| # include <nuttx/sched.h> |
| # include <nuttx/arch.h> |
| #endif |
| |
| #include "optee.h" |
| |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| # include "optee_supplicant.h" |
| #endif |
| |
| /**************************************************************************** |
| * 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 |
| ****************************************************************************/ |
| |
| #define OPTEE_DEV_PATH "/dev/tee0" |
| #define OPTEE_SUPPLICANT_DEV_PATH "/dev/teepriv0" |
| |
| /* According to optee_msg.h#OPTEE_MSG_ATTR_NONCONTIG */ |
| |
| #define OPTEE_PAGES_ARRAY_LEN \ |
| ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(uint64_t)) - 1) |
| |
| /* Name: optee_msg_alloc |
| * |
| * Description: |
| * Allocate OP-TEE page-aligned memory for use as arguments to OP-TEE |
| * calls, large enough to fit `numparams` parameters. Initialize the |
| * buffer to 0, and set the `.num_params` field to the specified value. |
| * |
| * Use `optee_msg_free()` to free the memory returned. |
| * |
| * Parameters: |
| * priv - pointer to the driver's optee_priv_data struct |
| * numparams - the number of arguments to allocate shared memory for. |
| * Can be zero. |
| * msh - pointer to a struct optee_msg_arg to receive the result. |
| * Will be set to NULL on failure. |
| */ |
| |
| #define optee_msg_alloc(priv, numparams, msg) \ |
| do \ |
| { \ |
| if ((priv)->alignment > sizeof(uintptr_t)) \ |
| { \ |
| (msg) = kmm_memalign((priv)->alignment, \ |
| OPTEE_MSG_GET_ARG_SIZE(numparams)); \ |
| } \ |
| else \ |
| { \ |
| (msg) = alloca(OPTEE_MSG_GET_ARG_SIZE(numparams)); \ |
| } \ |
| \ |
| if (msg) \ |
| { \ |
| memset(msg, 0, OPTEE_MSG_GET_ARG_SIZE(numparams)); \ |
| (msg)->num_params = numparams; \ |
| } \ |
| } \ |
| while (0) |
| |
| #define optee_msg_free(priv, msg) \ |
| if ((priv)->alignment > sizeof(uintptr_t)) \ |
| { \ |
| kmm_free(msg); \ |
| } |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* According to optee_msg.h#OPTEE_MSG_ATTR_NONCONTIG documentation */ |
| |
| struct optee_page_list_entry |
| { |
| uint64_t pages_array[OPTEE_PAGES_ARRAY_LEN]; |
| uint64_t next_entry; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* File operation functions for /dev/tee* */ |
| |
| 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); |
| |
| /* File operation functions for shm fds. */ |
| |
| static int optee_shm_close(FAR struct file *filep); |
| static int optee_shm_mmap(FAR struct file *filep, |
| FAR struct mm_map_entry_s *map); |
| |
| /**************************************************************************** |
| * 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 */ |
| }; |
| |
| static const struct file_operations g_optee_shm_ops = |
| { |
| .close = optee_shm_close, |
| .mmap = optee_shm_mmap, |
| }; |
| |
| static struct inode g_optee_shm_inode = |
| { |
| .i_flags = FSNODEFLAG_TYPE_SHM, /* could also be DRIVER; doesn't matter */ |
| .i_crefs = 1, /* inode never to be released */ |
| .u.i_ops = &g_optee_shm_ops |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: optee_is_valid_range |
| * |
| * Description: |
| * Check whether provided virtual address is not NULL and the address |
| * range belongs to user-owned memory. If this function is called from a |
| * kernel thread, it returns true. If this function is called in a build |
| * without CONFIG_ARCH_ADDRENV it always returns true. |
| * |
| * Parameters: |
| * va - Beginning of address range to check. |
| * size - Size of memory to check. |
| * |
| * Returned Values: |
| * True if the provided address range is not NULL and belongs to the user |
| * or the caller is a kernel thread. False otherwise. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| static bool optee_is_valid_range(FAR const void *va, size_t size) |
| { |
| FAR struct tcb_s *tcb; |
| uint8_t ttype; |
| |
| if (va == NULL) |
| { |
| return false; |
| } |
| |
| tcb = nxsched_self(); |
| ttype = tcb->flags & TCB_FLAG_TTYPE_MASK; |
| |
| if (ttype == TCB_FLAG_TTYPE_KERNEL) |
| { |
| return true; |
| } |
| |
| if (up_addrenv_user_vaddr((uintptr_t)va) && |
| up_addrenv_user_vaddr((uintptr_t)va + size - 1)) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| #else |
| # define optee_is_valid_range(addr, size) (true) |
| #endif |
| |
| /**************************************************************************** |
| * Name: optee_shm_to_page_list |
| * |
| * Description: |
| * Provide a page list of a shared memory buffer. Secure world doesn't |
| * know about the address environment mapping of NuttX, so physical |
| * pointers are needed when sharing memory. This implementation enables |
| * sharing of physically non-contiguous buffers according to |
| * optee_msg.h#OPTEE_MSG_ATTR_NONCONTIG. |
| * Each entry in the generated page list is an array of the physical, |
| * potentially non-contiguous pages making up the actual buffer to |
| * represent. Note that this representation does not account for the page |
| * offset of the shared memory buffer. The offset is encoded in the |
| * physical address returned in `list_pa`. |
| * |
| * Parameters: |
| * shm - Shared memory object to create a page list for. |
| * list_pa - If not NULL, will be set to the page list's physical address |
| * (which is aligned to OPTEE_MSG_NONCONTIG_PAGE_SIZE) added |
| * with shared memory page offset. |
| * |
| * Returned Values: |
| * A pointer to the kernel virtual address of the page list on success. |
| * NULL on failure. Caller responsible to free the returned list using |
| * `kmm_free()`. |
| * |
| ****************************************************************************/ |
| |
| static FAR void * |
| optee_shm_to_page_list(FAR struct optee_shm *shm, FAR uintptr_t *list_pa) |
| { |
| FAR struct optee_page_list_entry *list_entry; |
| size_t pgsize = OPTEE_MSG_NONCONTIG_PAGE_SIZE; |
| uintptr_t pgoff; |
| uintptr_t page; |
| FAR void *list; |
| uint32_t total_pages; |
| uint32_t list_size; |
| uint32_t i = 0; |
| |
| pgoff = shm->vaddr & (pgsize - 1); |
| total_pages = (uint32_t)div_round_up(pgoff + shm->length, pgsize); |
| list_size = div_round_up(total_pages, OPTEE_PAGES_ARRAY_LEN) |
| * sizeof(struct optee_page_list_entry); |
| |
| /* Page list's address should be page aligned, conveniently leaving |
| * log2(<page size>) zero least-significant bits to use as the page |
| * offset of the shm buffer (added last before return below). |
| */ |
| |
| list = kmm_memalign(pgsize, list_size); |
| if (!list) |
| { |
| return NULL; |
| } |
| |
| list_entry = (FAR struct optee_page_list_entry *)list; |
| page = ALIGN_DOWN(shm->vaddr, pgsize); |
| while (total_pages) |
| { |
| list_entry->pages_array[i++] = optee_va_to_pa((FAR const void *)page); |
| page += pgsize; |
| total_pages--; |
| |
| if (i == OPTEE_PAGES_ARRAY_LEN) |
| { |
| list_entry->next_entry = optee_va_to_pa(list_entry + 1); |
| list_entry++; |
| i = 0; |
| } |
| } |
| |
| if (list_pa) |
| { |
| *list_pa = optee_va_to_pa(list) | pgoff; |
| } |
| |
| #ifndef CONFIG_ARCH_USE_MMU |
| up_clean_dcache((uintptr_t)list, (uintptr_t)list + list_size); |
| #endif |
| |
| return list; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_shm_register |
| * |
| * Description: |
| * Register specified shared memory object with OP-TEE. |
| * |
| * Parameters: |
| * priv - The driver's private data structure |
| * shm - Pointer to shared memory object to register. Neither the shm |
| * object, nor the referenced shared buffer pointer can be NULL. |
| * |
| * Returned Values: |
| * 0 on success, negative error code otherwise. |
| * |
| ****************************************************************************/ |
| |
| static int optee_shm_register(FAR struct optee_priv_data *priv, |
| FAR struct optee_shm *shm) |
| { |
| FAR struct optee_msg_arg *msg; |
| uintptr_t page_list_pa; |
| FAR void *page_list; |
| int ret = -ENOMEM; |
| |
| optee_msg_alloc(priv, 1, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| page_list = optee_shm_to_page_list(shm, &page_list_pa); |
| if (page_list == NULL) |
| { |
| goto errout_with_msg; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; |
| msg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | |
| OPTEE_MSG_ATTR_NONCONTIG; |
| msg->params[0].u.tmem.buf_ptr = page_list_pa; |
| msg->params[0].u.tmem.shm_ref = (uintptr_t)shm; |
| msg->params[0].u.tmem.size = shm->length; |
| |
| ret = optee_transport_call(priv, msg); |
| if (ret < 0) |
| { |
| goto errout_with_list; |
| } |
| |
| if (msg->ret) |
| { |
| ret = optee_convert_to_errno(msg->ret); |
| } |
| |
| errout_with_list: |
| kmm_free(page_list); |
| errout_with_msg: |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_shm_unregister |
| * |
| * Description: |
| * Unregister specified shared memory object from OP-TEE. |
| * |
| * Parameters: |
| * priv - the driver's private data structure |
| * shm - Pointer to shared memory object to unregister. The shared memory |
| * entry must have been previously registered previously with the |
| * OP-TEE OS and cannot be NULL. |
| * |
| * Returned Values: |
| * 0 on success, negative error code otherwise. |
| * |
| ****************************************************************************/ |
| |
| static int optee_shm_unregister(FAR struct optee_priv_data *priv, |
| FAR struct optee_shm *shm) |
| { |
| FAR struct optee_msg_arg *msg; |
| int ret = 0; |
| |
| optee_msg_alloc(priv, 1, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; |
| msg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; |
| msg->params[0].u.rmem.shm_ref = (uintptr_t)shm; |
| |
| ret = optee_transport_call(priv, msg); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| if (msg->ret) |
| { |
| ret = optee_convert_to_errno(msg->ret); |
| } |
| |
| errout_with_msg: |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_shm_close |
| * |
| * Description: |
| * shm close operation |
| * |
| * Parameters: |
| * filep - the file instance |
| * |
| * Returned Values: |
| * Always OK. |
| * |
| ****************************************************************************/ |
| |
| static int optee_shm_close(FAR struct file *filep) |
| { |
| FAR struct optee_shm *shm = filep->f_priv; |
| |
| if (shm != NULL && shm->id > -1) |
| { |
| filep->f_priv = NULL; |
| optee_shm_free(shm); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_shm_mmap |
| * |
| * Description: |
| * shm mmap operation |
| * |
| * Parameters: |
| * filep - the file instance |
| * map - Filled by the userspace, with the mapping parameters. |
| * |
| * Returned Values: |
| * OK on success; A negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| static int optee_shm_mmap(FAR struct file *filep, |
| FAR struct mm_map_entry_s *map) |
| { |
| FAR struct optee_shm *shm = filep->f_priv; |
| int32_t ret = OK; |
| |
| if ((map->flags & MAP_PRIVATE) && (map->flags & MAP_SHARED)) |
| { |
| return -EINVAL; |
| } |
| |
| /* Forbid multiple mmaps of the same fd. */ |
| |
| if (shm->vaddr != 0) |
| { |
| return -EBUSY; |
| } |
| |
| /* Forbid allocations with bigger size than what registered with |
| * with optee_ioctl_shm_alloc(). |
| */ |
| |
| if (shm->length < map->length) |
| { |
| return -EINVAL; |
| } |
| |
| ret = map_anonymous(map, false); |
| |
| if (ret == OK) |
| { |
| DEBUGASSERT(map->vaddr != NULL); |
| shm->vaddr = (uint64_t)map->vaddr; |
| shm->paddr = optee_va_to_pa(map->vaddr); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * 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; |
| enum optee_role_e role = (uintptr_t)filep->f_inode->i_private; |
| int ret; |
| |
| ret = optee_transport_open(&priv); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| priv->role = role; |
| |
| if (role == OPTEE_ROLE_CA) |
| { |
| priv->shms = idr_init(); |
| } |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| else if (role == OPTEE_ROLE_SUPPLICANT) |
| { |
| /* Allow only one process to open the device. */ |
| |
| if (filep->f_inode->i_crefs > 2) |
| { |
| return -EBUSY; |
| } |
| |
| optee_supplicant_init(&priv->shms); |
| } |
| #endif |
| else |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| filep->f_priv = priv; |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_close |
| * |
| * Description: |
| * optee close operation |
| * |
| * Parameters: |
| * filep - the file instance |
| * |
| * Returned Values: |
| * Always OK. |
| * |
| ****************************************************************************/ |
| |
| static int optee_close(FAR struct file *filep) |
| { |
| FAR struct optee_priv_data *priv = filep->f_priv; |
| FAR struct optee_shm *shm; |
| int id = 0; |
| |
| idr_for_each_entry(priv->shms, shm, id) |
| { |
| /* Here, we only free, unfreed kernel allocations, the rest will be |
| * done by optee_shm_close(). |
| */ |
| |
| if (shm->fd == -1) |
| { |
| optee_shm_free(shm); |
| } |
| } |
| |
| idr_destroy(priv->shms); |
| optee_transport_close(priv); |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| if (priv->role == OPTEE_ROLE_SUPPLICANT) |
| { |
| optee_supplicant_uninit(); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| static int optee_memref_to_msg_param(FAR struct optee_priv_data *priv, |
| FAR struct optee_msg_param *mp, |
| FAR const struct tee_ioctl_param *p) |
| { |
| FAR struct optee_shm *shm; |
| uintptr_t page_list_pa; |
| bool external_vm_context = false; |
| |
| if (p->c == TEE_MEMREF_NULL) |
| { |
| mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - |
| TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
| mp->u.tmem.buf_ptr = 0; |
| mp->u.tmem.shm_ref = 0; |
| mp->u.tmem.size = p->b; |
| return 0; |
| } |
| |
| shm = idr_find(priv->shms, p->c); |
| if (shm == NULL) |
| { |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| /* Search also the shared memory registered by the supplicant. */ |
| |
| shm = optee_supplicant_search_shm(p->c); |
| external_vm_context = true; |
| |
| if (shm == NULL) |
| #endif |
| { |
| return -EINVAL; |
| } |
| } |
| |
| if (shm->flags & TEE_SHM_REGISTER) |
| { |
| /* registered memory */ |
| |
| mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - |
| TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
| mp->u.rmem.offs = p->a; |
| mp->u.rmem.size = p->b; |
| mp->u.rmem.shm_ref = (uintptr_t)shm; |
| } |
| else |
| { |
| /* non-registered memory (temporary) */ |
| |
| mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - |
| TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
| mp->attr |= OPTEE_MSG_ATTR_NONCONTIG; |
| |
| /* This shouldn't happen, we can't translate vmas from |
| * another vm context. |
| */ |
| |
| if (external_vm_context) |
| { |
| return -EPROTO; |
| } |
| |
| shm->page_list = optee_shm_to_page_list(shm, &page_list_pa); |
| if (shm->page_list == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| mp->u.tmem.buf_ptr = page_list_pa; |
| mp->u.tmem.shm_ref = (uintptr_t)shm; |
| mp->u.tmem.size = p->b; |
| } |
| |
| #ifndef CONFIG_ARCH_USE_MMU |
| up_clean_dcache(shm->vaddr, shm->vaddr + shm->length); |
| #endif |
| |
| return 0; |
| } |
| |
| static int optee_close_session(FAR struct optee_priv_data *priv, |
| uint32_t session) |
| { |
| FAR struct optee_msg_arg *msg; |
| int ret; |
| |
| optee_msg_alloc(priv, 0, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; |
| msg->session = session; |
| |
| ret = optee_transport_call(priv, msg); |
| |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| static int optee_ioctl_open_session(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_buf_data *buf) |
| { |
| FAR struct tee_ioctl_open_session_arg *arg; |
| FAR struct optee_msg_arg *msg; |
| int ret; |
| |
| if (!optee_is_valid_range(buf, sizeof(*buf))) |
| { |
| return -EFAULT; |
| } |
| |
| 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 (!optee_is_valid_range(arg, buf->buf_len)) |
| { |
| return -EFAULT; |
| } |
| |
| 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; |
| |
| optee_msg_alloc(priv, arg->num_params + 2, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; |
| msg->cancel_id = arg->cancel_id; |
| |
| /* 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(priv, msg->params + 2, arg->num_params, |
| arg->params); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| ret = optee_transport_call(priv, msg); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| ret = optee_from_msg_param(arg->params, arg->num_params, msg->params + 2); |
| if (ret < 0) |
| { |
| optee_close_session(priv, msg->session); |
| goto errout_with_msg; |
| } |
| |
| arg->session = msg->session; |
| arg->ret = msg->ret; |
| arg->ret_origin = msg->ret_origin; |
| |
| errout_with_msg: |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| static int optee_ioctl_invoke(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_buf_data *buf) |
| { |
| FAR struct tee_ioctl_invoke_arg *arg; |
| FAR struct optee_msg_arg *msg; |
| int ret; |
| |
| if (!optee_is_valid_range(buf, sizeof(*buf))) |
| { |
| return -EFAULT; |
| } |
| |
| 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 (!optee_is_valid_range(arg, buf->buf_len)) |
| { |
| return -EFAULT; |
| } |
| |
| 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; |
| |
| optee_msg_alloc(priv, arg->num_params, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; |
| msg->func = arg->func; |
| msg->session = arg->session; |
| msg->cancel_id = arg->cancel_id; |
| |
| ret = optee_to_msg_param(priv, msg->params, arg->num_params, arg->params); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| ret = optee_transport_call(priv, msg); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| ret = optee_from_msg_param(arg->params, arg->num_params, msg->params); |
| if (ret < 0) |
| { |
| goto errout_with_msg; |
| } |
| |
| arg->ret = msg->ret; |
| arg->ret_origin = msg->ret_origin; |
| |
| errout_with_msg: |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| static int |
| optee_ioctl_close_session(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_close_session_arg *arg) |
| { |
| if (!optee_is_valid_range(arg, sizeof(*arg))) |
| { |
| return -EFAULT; |
| } |
| |
| 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 | |
| TEE_GEN_CAP_REG_MEM; |
| return 0; |
| } |
| |
| static int optee_ioctl_cancel(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_cancel_arg *arg) |
| { |
| FAR struct optee_msg_arg *msg; |
| int ret; |
| |
| if (!optee_is_valid_range(arg, sizeof(*arg))) |
| { |
| return -EFAULT; |
| } |
| |
| optee_msg_alloc(priv, 0, msg); |
| if (msg == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| msg->cmd = OPTEE_MSG_CMD_CANCEL; |
| msg->session = arg->session; |
| msg->cancel_id = arg->cancel_id; |
| ret = optee_transport_call(priv, msg); |
| |
| optee_msg_free(priv, msg); |
| return ret; |
| } |
| |
| static int |
| optee_ioctl_shm_alloc(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_shm_alloc_data *data) |
| { |
| FAR struct optee_shm *shm; |
| int ret; |
| |
| if (!optee_is_valid_range(data, sizeof(*data))) |
| { |
| return -EFAULT; |
| } |
| |
| ret = optee_shm_alloc(priv, NULL, data->size, TEE_SHM_USER_MAP, &shm); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = file_allocate_from_inode(&g_optee_shm_inode, |
| O_CLOEXEC | O_RDOK, 0, shm, 0); |
| |
| if (ret < 0) |
| { |
| optee_shm_free(shm); |
| return ret; |
| } |
| |
| shm->fd = ret; |
| |
| data->id = shm->id; |
| return shm->fd; |
| } |
| |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| static int |
| optee_shm_register_supplicant(FAR struct optee_priv_data *priv, |
| uintptr_t addr, uint64_t length, |
| FAR struct optee_shm **shmp) |
| { |
| FAR struct optee_shm *shm; |
| uintptr_t page_list_pa; |
| int ret = 0; |
| |
| shm = kmm_zalloc(sizeof(struct optee_shm)); |
| *shmp = shm; |
| if (shm == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| shm->fd = -1; |
| shm->priv = priv; |
| shm->vaddr = addr; |
| shm->length = length; |
| shm->flags = TEE_SHM_REGISTER | TEE_SHM_SUPP; |
| shm->page_list = optee_shm_to_page_list(shm, &page_list_pa); |
| shm->paddr = page_list_pa; |
| shm->id = idr_alloc(priv->shms, shm, 0, 0); |
| |
| if (shm->id < 0) |
| { |
| kmm_free(shm->page_list); |
| kmm_free(shm); |
| return -ENOMEM; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static int |
| optee_ioctl_shm_register(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_shm_register_data *rdata) |
| { |
| FAR struct optee_shm *shm; |
| int ret; |
| |
| if (!optee_is_valid_range(rdata, sizeof(*rdata))) |
| { |
| return -EFAULT; |
| } |
| |
| if (!optee_is_valid_range((FAR void *)(uintptr_t) |
| rdata->addr, rdata->length)) |
| { |
| return -EFAULT; |
| } |
| |
| if (rdata->flags) |
| { |
| return -EINVAL; |
| } |
| |
| if (priv->role == OPTEE_ROLE_CA) |
| { |
| ret = optee_shm_alloc(priv, (FAR void *)(uintptr_t)rdata->addr, |
| rdata->length, TEE_SHM_REGISTER, &shm); |
| } |
| |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| else if (priv->role == OPTEE_ROLE_SUPPLICANT) |
| { |
| ret = optee_shm_register_supplicant(priv, (uintptr_t)rdata->addr, |
| rdata->length, &shm); |
| rdata->flags = shm->flags; |
| } |
| #endif |
| else |
| { |
| return -ENODEV; |
| } |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = file_allocate_from_inode(&g_optee_shm_inode, O_CLOEXEC, 0, shm, 0); |
| if (ret < 0) |
| { |
| optee_shm_free(shm); |
| return ret; |
| } |
| |
| shm->fd = ret; |
| rdata->id = shm->id; |
| return ret; |
| } |
| |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| static int |
| optee_ioctl_supplicant_recv(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_buf_data *data) |
| { |
| int n; |
| int ret; |
| FAR struct tee_iocl_supp_recv_arg *arg; |
| |
| if (!optee_is_valid_range(data, sizeof(*data))) |
| { |
| return -EFAULT; |
| } |
| |
| if (!optee_is_valid_range((FAR void *)data->buf_ptr, data->buf_len)) |
| { |
| return -EFAULT; |
| } |
| |
| if (data->buf_len > TEE_MAX_ARG_SIZE || |
| data->buf_len < sizeof(struct tee_iocl_supp_recv_arg)) |
| { |
| return -EINVAL; |
| } |
| |
| arg = (FAR struct tee_iocl_supp_recv_arg *)(uintptr_t)data->buf_ptr; |
| |
| if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) != |
| data->buf_len) |
| { |
| return -EINVAL; |
| } |
| |
| if (arg->num_params > OPTEE_MAX_PARAM_NUM) |
| { |
| return -EINVAL; |
| } |
| |
| ret = optee_supplicant_recv(&arg->func, &arg->num_params, arg->params); |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| for (n = 0; n < arg->num_params; n++) |
| { |
| FAR struct tee_ioctl_param *p = arg->params + n; |
| |
| switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) |
| { |
| case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: |
| case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: |
| case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: |
| if (!p->b) |
| { |
| p->a = 0; |
| p->c = (uint64_t)-1; /* Invalid shm id. */ |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| optee_ioctl_supplicant_send(FAR struct optee_priv_data *priv, |
| FAR struct tee_ioctl_buf_data *data) |
| { |
| FAR struct tee_iocl_supp_send_arg *arg; |
| |
| if (!optee_is_valid_range(data, sizeof(*data))) |
| { |
| return -EFAULT; |
| } |
| |
| if (!optee_is_valid_range((FAR void *)data->buf_ptr, data->buf_len)) |
| { |
| return -EFAULT; |
| } |
| |
| if (data->buf_len > TEE_MAX_ARG_SIZE || |
| data->buf_len < sizeof(struct tee_iocl_supp_send_arg)) |
| { |
| return -EINVAL; |
| } |
| |
| arg = (FAR struct tee_iocl_supp_send_arg *)(uintptr_t)data->buf_ptr; |
| |
| if (sizeof(*arg) + TEE_IOCTL_PARAM_SIZE(arg->num_params) != |
| data->buf_len) |
| { |
| return -EINVAL; |
| } |
| |
| if (arg->num_params > OPTEE_MAX_PARAM_NUM) |
| { |
| return -EINVAL; |
| } |
| |
| return optee_supplicant_send(arg->ret, arg->num_params, arg->params); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * 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(priv, parg); |
| case TEE_IOC_SHM_REGISTER: |
| return optee_ioctl_shm_register(priv, parg); |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| case TEE_IOC_SUPPL_RECV: |
| return optee_ioctl_supplicant_recv(priv, parg); |
| case TEE_IOC_SUPPL_SEND: |
| return optee_ioctl_supplicant_send(priv, parg); |
| #endif |
| default: |
| return -ENOTTY; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: optee_convert_to_errno |
| * |
| * Description: |
| * Convert TEE errors to errno values |
| * |
| * Parameters: |
| * oterr - TEE error code. |
| * |
| * Returned Values: |
| * The converted errno value. |
| * |
| ****************************************************************************/ |
| |
| int optee_convert_to_errno(uint32_t oterr) |
| { |
| switch (oterr) |
| { |
| case TEE_SUCCESS: |
| return 0; |
| case TEE_ERROR_ACCESS_DENIED: |
| case TEE_ERROR_SECURITY: |
| return -EACCES; |
| case TEE_ERROR_BAD_FORMAT: |
| case TEE_ERROR_BAD_PARAMETERS: |
| return -EINVAL; |
| case TEE_ERROR_NOT_SUPPORTED: |
| return -EOPNOTSUPP; |
| case TEE_ERROR_OUT_OF_MEMORY: |
| return -ENOMEM; |
| case TEE_ERROR_BUSY: |
| return -EBUSY; |
| case TEE_ERROR_COMMUNICATION: |
| return -ECOMM; |
| case TEE_ERROR_SHORT_BUFFER: |
| return -ENOBUFS; |
| case TEE_ERROR_TIMEOUT: |
| return -ETIMEDOUT; |
| default: |
| return -EIO; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: optee_convert_from_errno |
| * |
| * Description: |
| * Convert errno values to TEE errors. |
| * |
| * Parameters: |
| * err - errno value (negative). |
| * |
| * Returned Values: |
| * The converted TEE error code. |
| * |
| ****************************************************************************/ |
| |
| uint32_t optee_convert_from_errno(int err) |
| { |
| /* Make sure we handle negative errno values */ |
| |
| switch (-err) |
| { |
| case 0: |
| return TEE_SUCCESS; |
| case EACCES: |
| return TEE_ERROR_ACCESS_DENIED; |
| case EINVAL: |
| return TEE_ERROR_BAD_PARAMETERS; |
| case ENOTSUP: |
| case EOPNOTSUPP: |
| return TEE_ERROR_NOT_SUPPORTED; |
| case ENOMEM: |
| return TEE_ERROR_OUT_OF_MEMORY; |
| case EBUSY: |
| return TEE_ERROR_BUSY; |
| case ECOMM: |
| case EPROTO: |
| return TEE_ERROR_COMMUNICATION; |
| case ENOBUFS: |
| return TEE_ERROR_SHORT_BUFFER; |
| case ETIMEDOUT: |
| return TEE_ERROR_TIMEOUT; |
| default: |
| return TEE_ERROR_GENERIC; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: optee_va_to_pa |
| * |
| * Description: |
| * Convert the specified virtual address to a physical address. If the |
| * virtual address does not belong to the user, it is assumed to be a |
| * kernel virtual address with a 1-1 mapping and the VA is returned as-is. |
| * The VA is also returned as-is if this is a build without |
| * CONFIG_ARCH_ADDRENV. |
| * |
| * Parameters: |
| * va - The virtual address to convert. |
| * |
| * Returned Values: |
| * The physical address corresponding to the specified VA. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| uintptr_t optee_va_to_pa(FAR const void *va) |
| { |
| FAR arch_addrenv_t *addrenv; |
| FAR struct tcb_s *tcb; |
| uintptr_t page; |
| |
| tcb = nxsched_self(); |
| addrenv = &tcb->addrenv_curr->addrenv; |
| |
| page = up_addrenv_find_page(addrenv, (uintptr_t)va); |
| if (page == 0) |
| { |
| return (uintptr_t)va; |
| } |
| |
| return page | ((uintptr_t)va & MM_PGMASK); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: optee_shm_alloc |
| * |
| * Description: |
| * Allocate and/or register shared memory with the OP-TEE OS. |
| * This function always allocates a shared memory object and adds it |
| * to the tree maintained by the driver, regardless of flags. The rest of |
| * this function's behaviour is determined by `flags`: |
| * - If `TEE_SHM_ALLOC` is specified, then a buffer of length `size` will |
| * be allocated. In this case `addr` will be ignored. The allocated |
| * buffer will be aligned to `priv->alignment`. `TEE_SHM_ALLOC` flag |
| * is reserved for kernel use only. |
| * - If `TEE_SHM_REGISTER` is specified, then the memory specified by |
| * `addr` or allocated through `TEE_SHM_ALLOC`, will be registered with |
| * OP-TEE as dynamic shared memory. |
| * |
| * Use `optee_shm_free()` to undo this operation, i.e. to remove the |
| * shared memory boject from driver, and/or free, and/or unregister it |
| * from the OP-TEE OS. |
| * |
| * Parameters: |
| * priv - The driver's private data structure |
| * addr - The address of the shared memory to register with OP-TEE and/or |
| * add to the driver's linked list of shared memory chunks. |
| * size - The size of the shared memory buffer to allocate/add/register. |
| * flags - Flags specifying the behaviour of this function. Supports |
| * combinations of `TEE_SHM_{ALLOC,REGISTER,SEC_REGISTER}`. |
| * shmp - Pass-by-reference pointer to return the shared memory object |
| * allocated. Cannot be NULL. |
| * |
| * Returned Values: |
| * 0 on success, negative error code otherwise. |
| * |
| ****************************************************************************/ |
| |
| int optee_shm_alloc(FAR struct optee_priv_data *priv, FAR void *addr, |
| size_t size, uint32_t flags, |
| FAR struct optee_shm **shmp) |
| { |
| FAR struct optee_shm *shm; |
| FAR void *ptr; |
| int ret = -ENOMEM; |
| |
| shm = kmm_zalloc(sizeof(struct optee_shm)); |
| if (shm == NULL) |
| { |
| return ret; |
| } |
| |
| if (flags & TEE_SHM_ALLOC) |
| { |
| if (priv->alignment) |
| { |
| ptr = kmm_memalign(priv->alignment, size); |
| } |
| else |
| { |
| ptr = kmm_malloc(size); |
| } |
| } |
| else |
| { |
| ptr = addr; |
| } |
| |
| if (!(flags & TEE_SHM_USER_MAP) && ptr == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| shm->fd = -1; |
| shm->priv = priv; |
| shm->vaddr = (uintptr_t)ptr; |
| shm->paddr = shm->vaddr ? optee_va_to_pa((FAR void *)shm->vaddr) : 0; |
| shm->length = size; |
| shm->flags = flags; |
| shm->id = idr_alloc(priv->shms, shm, 0, 0); |
| |
| if (shm->id < 0) |
| { |
| goto err; |
| } |
| |
| if (flags & TEE_SHM_REGISTER) |
| { |
| ret = optee_shm_register(priv, shm); |
| if (ret < 0) |
| { |
| goto err_with_idr; |
| } |
| } |
| |
| *shmp = shm; |
| return 0; |
| |
| err_with_idr: |
| idr_remove(priv->shms, shm->id); |
| err: |
| kmm_free(shm); |
| if (flags & TEE_SHM_ALLOC) |
| { |
| kmm_free(ptr); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_shm_free |
| * |
| * Description: |
| * Free and/or unregister shared memory allocated by `optee_shm_alloc()`. |
| * |
| * Parameters: |
| * shm - Pointer to shared memory object to free. Can be NULL, in which |
| * case, this is a no-op. |
| * |
| * Returned Values: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void optee_shm_free(FAR struct optee_shm *shm) |
| { |
| if (!shm || !shm->priv) |
| { |
| return; |
| } |
| |
| if (!(shm->flags & TEE_SHM_SUPP) && (shm->flags & TEE_SHM_REGISTER)) |
| { |
| optee_shm_unregister(shm->priv, shm); |
| } |
| |
| if (shm->flags & TEE_SHM_ALLOC) |
| { |
| kmm_free((FAR void *)(uintptr_t)shm->vaddr); |
| } |
| |
| idr_remove(shm->priv->shms, shm->id); |
| kmm_free(shm); |
| } |
| |
| /**************************************************************************** |
| * 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; |
| } |
| |
| #ifdef CONFIG_DEV_OPTEE_SUPPLICANT |
| ret = register_driver(OPTEE_SUPPLICANT_DEV_PATH, &g_optee_ops, 0666, |
| (FAR void *)OPTEE_ROLE_SUPPLICANT); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| #endif |
| |
| return register_driver(OPTEE_DEV_PATH, &g_optee_ops, 0666, |
| (FAR void *)OPTEE_ROLE_CA); |
| } |
| |
| /**************************************************************************** |
| * Name: optee_from_msg_param |
| * |
| * Description: |
| * Converts and copies the message parameters received by OP-TEE to buffer |
| * for processing by nuttx. |
| * |
| * Parameters: |
| * params - Pointer, to copy the received parameters after some processing. |
| * num_params - Number of these parameters. |
| * mparams - Pointer to the message parameters received by OP-TEE. |
| * |
| * Returned Values: |
| * OK on success; A negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| 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; |
| FAR struct optee_shm *shm = NULL; |
| |
| 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_TMEM_INPUT: |
| case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: |
| case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: |
| p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + |
| mp->attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; |
| p->b = mp->u.tmem.size; |
| |
| shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref; |
| if (shm && shm->page_list) |
| { |
| kmm_free(shm->page_list); |
| shm->page_list = NULL; |
| p->c = shm->id; |
| } |
| else |
| { |
| p->c = TEE_MEMREF_NULL; |
| } |
| 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; |
| p->a = mp->u.rmem.offs; |
| shm = (FAR struct optee_shm *)(uintptr_t)mp->u.tmem.shm_ref; |
| if (shm) |
| { |
| p->c = shm->id; |
| } |
| else |
| { |
| p->c = TEE_MEMREF_NULL; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| #ifndef CONFIG_ARCH_USE_MMU |
| if (shm) |
| { |
| up_invalidate_dcache(shm->vaddr, shm->vaddr + shm->length); |
| } |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: optee_to_msg_param |
| * |
| * Description: |
| * Converts and copies the processed by nuttx parameters to the shared |
| * memory area containing the message to/from the OP-TEE. |
| * |
| * Parameters: |
| * priv - pointer to the driver's optee_priv_data struct |
| * mparams - Pointer to the message parameters provided by OP-TEE. |
| * params - Pointer, of the processed by nuttx parameters containing the |
| * response. |
| * num_params - Number of these parameters. |
| * |
| * Returned Values: |
| * OK on success; A negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| int optee_to_msg_param(FAR struct optee_priv_data *priv, |
| FAR struct optee_msg_param *mparams, |
| size_t num_params, |
| FAR const struct tee_ioctl_param *params) |
| { |
| size_t n; |
| int ret; |
| |
| 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: |
| ret = optee_memref_to_msg_param(priv, mp, p); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |