gpoulios commented on code in PR #16309: URL: https://github.com/apache/nuttx/pull/16309#discussion_r2072953422
########## drivers/misc/optee.c: ########## @@ -36,165 +36,977 @@ #include <sys/mman.h> #include <sys/param.h> #include <sys/un.h> +#include <sys/queue.h> + +#ifdef CONFIG_ARCH_ADDRENV +# include <nuttx/pgalloc.h> +# include <nuttx/sched.h> +# include <nuttx/arch.h> +#endif + +#ifdef CONFIG_DEV_OPTEE_SMC64 +# include <syslog.h> +# include <uuid.h> +# include <nuttx/bits.h> +# include <nuttx/cache.h> +# include <arch/syscall.h> +# include "optee_smc.h" +#endif #include "optee_msg.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. - * TEE implementation is optee_os(https://github.com/OP-TEE/optee_os). - ****************************************************************************/ +/**************************************************************************** + * 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. + * 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_MAX_IOVEC_NUM 7 +#define OPTEE_MAX_PARAM_NUM 6 + +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +#define PAGELIST_ENTRIES_PER_PAGE \ + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(uint64_t)) - 1) + +#ifdef CONFIG_DEV_OPTEE_SMC64 + +/* is there a nice way to include arm64_arch.h instead? */ +# define SCTLR_C_BIT BIT(2) + +# ifdef CONFIG_DEV_OPTEE_SMC64_TEST +# define PTA_DEVICE_ENUM { 0x7011a688, 0xddde, 0x4053, \ + 0xa5, 0xa9, \ + { 0x7b, 0x3c, 0x4d, 0xdf, \ + 0x13, 0xb8 } } +# define PTA_CMD_GET_DEVICES 0x0 +# endif +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_DEV_OPTEE_SMC64 +typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, + struct arm64_smccc_res *); + +struct rpc_param +{ + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; +}; + +union os_revision +{ + struct arm64_smccc_res smccc; + struct optee_smc_call_get_os_revision_result result; +}; +#endif + +struct optee_priv_data; + +struct optee_shm_entry +{ + struct optee_priv_data *priv; + struct tee_ioctl_shm_register_data shm; + SLIST_ENTRY(optee_shm_entry) link; +}; + +SLIST_HEAD(optee_shm_head, optee_shm_entry); + +struct optee_priv_data +{ +#ifdef CONFIG_DEV_OPTEE_SMC64 + optee_invoke_fn *invoke_fn; +#else + FAR struct socket *psock; +#endif + struct optee_shm_head list_shm; +}; + +struct page_data +{ + uint64_t pages_list[PAGELIST_ENTRIES_PER_PAGE]; + uint64_t next_page_data; +}; + +/**************************************************************************** + * 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); + +/* Other forward-declared functions */ + +static int optee_shm_add(FAR struct optee_priv_data *priv, uintptr_t align, + FAR void *addr, size_t size, uint32_t flags, + FAR struct optee_shm_entry **shmep); +static void optee_shm_free(FAR struct optee_shm_entry *shme); +static int get_msg_arg(FAR struct optee_priv_data *priv, + uint32_t num_params, + FAR struct optee_shm_entry **shmep, + FAR struct optee_msg_arg **msg_arg); +static int +optee_do_call_with_arg(FAR struct optee_priv_data *priv, + FAR struct optee_msg_arg *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: virt_to_phys + * + * 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 +static uintptr_t virt_to_phys(FAR void *va) +{ + FAR struct tcb_s *tcb; + FAR arch_addrenv_t *addrenv; + 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); +} + +/**************************************************************************** + * Name: safe_va_range + * + * Description: + * Check whether provided virtual 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 belongs to the user or the caller is + * a kernel thread. False otherwise. + * + ****************************************************************************/ + +static bool safe_va_range(FAR void *va, size_t size) +{ + FAR struct tcb_s *tcb; + uint8_t ttype; + + tcb = nxsched_self(); + ttype = tcb->flags & TCB_FLAG_TTYPE_MASK; + + if (ttype == TCB_FLAG_TTYPE_KERNEL) + { + return 1; + } + + if (up_addrenv_user_vaddr((uintptr_t)va) && + up_addrenv_user_vaddr((uintptr_t)va + size - 1)) + { + return 1; + } + + return 0; +} +#else /* !CONFIG_ARCH_ADDRENV */ +#define virt_to_phys(va) ((uintptr_t)va) +#define safe_va_range(addr, size) (true) +#endif + +#ifdef CONFIG_DEV_OPTEE_SMC64 +static int is_optee_api(optee_invoke_fn *invoke_fn) +{ + struct arm64_smccc_res res; + + invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); + + return res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && + res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3; +} + +static void print_os_revision(optee_invoke_fn *invoke_fn) +{ + union os_revision res = + { + .result = + { + .build_id = 0 + } + }; + + invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.build_id) + { + syslog(LOG_INFO, "OP-TEE: revision %lu.%lu (%08lx)\n", + res.result.major, res.result.minor, + res.result.build_id); + } + else + { + syslog(LOG_INFO, "OP-TEE: revision %lu.%lu\n", + res.result.major, res.result.minor); + } +} + +static int api_revision_is_compatible(optee_invoke_fn *invoke_fn) +{ + union { + struct arm64_smccc_res smccc; + struct optee_smc_calls_revision_result result; + } res; + + invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + + return res.result.major == OPTEE_MSG_REVISION_MAJOR && + (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR; +} + +static int exchange_capabilities(optee_invoke_fn *invoke_fn, + unsigned long *sec_caps) +{ + union { + struct arm64_smccc_res smccc; + struct optee_smc_exchange_capabilities_result result; + } res; + + invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, + OPTEE_SMC_NSEC_CAP_UNIPROCESSOR, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.status != OPTEE_SMC_RETURN_OK) + { + return 0; + } + + *sec_caps = res.result.capabilities; + + return 1; +} + +/**************************************************************************** + * Name: reg_pair_to_ptr + * + * Description: + * Make a pointer of 2 32-bit values + * + * Parameters: + * reg0 - High bits of the pointer + * reg1 - Low bits of the pointer + * + * Returned Values: + * The combined result. Note that if a pointer is 32-bit wide `reg0` + * will be discarded. + * + ****************************************************************************/ + +static void *reg_pair_to_ptr(uint32_t reg0, uint32_t reg1) +{ + return (void *)(uintptr_t)(((uint64_t)reg0 << 32) | reg1); +} + +/**************************************************************************** + * Name: reg_pair_from_64 + * + * Description: + * Split a 64-bit value into two 32-bit values + * + * Parameters: + * reg0 - High bits of `val` + * reg1 - Low bits of `val` + * val - The value to split + * + * Returned Values: + * None + * + ****************************************************************************/ + +static void reg_pair_from_64(uint32_t *reg0, uint32_t *reg1, uint64_t val) +{ + *reg0 = val >> 32; + *reg1 = val; +} + +/**************************************************************************** + * Name: dcache_enabled + * + * Description: + * Return arm64 EL1 D-Cache status. This should probably be re-located in + * arm64_cache.c once respective implementations for other archs are + * provided. + * + * Parameters: + * None + * + * Returned Values: + * 0 if CONFIG_ARM64_DCACHE_DISABLE is set, or EL1 D-Cache is not enabled. + * Non-zero otherwise. + * + ****************************************************************************/ + +static int dcache_enabled(void) +{ +#ifdef CONFIG_ARM64_DCACHE_DISABLE + return 0; +#else + + /* arm64_cache.c assumes EL1 all around, so do we. + * NuttX doesn't support EL2 yet + */ + + uint64_t value = read_sysreg(sctlr_el1); + return (value & SCTLR_C_BIT) != 0; +#endif +} + +/**************************************************************************** + * Name: flush_shm_dcache + * + * Description: + * Clean and invalidate shared memory D cache on OP-TEE message arguments + * and OP-TEE shared memory allocations registered in the driver's private + * data linked list. + * + * Parameters: + * priv - the driver's private data structure + * arg - the OP-TEE message arguments to flush + * + * Returned Values: + * None + * + ****************************************************************************/ + +static void flush_shm_dcache(FAR struct optee_priv_data *priv, + FAR struct optee_msg_arg *arg) +{ + struct optee_shm_entry *shme; + size_t sz = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); + + up_flush_dcache((uintptr_t)arg, (uintptr_t)arg + sz); + + SLIST_FOREACH(shme, &priv->list_shm, link) + { + up_flush_dcache((uintptr_t)shme->shm.addr, + (uintptr_t)shme->shm.addr + shme->shm.length); + } +} + +static void handle_rpc(FAR struct optee_priv_data *priv, + FAR struct rpc_param *param) +{ + FAR struct optee_shm_entry *shme; + + switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) + { + case OPTEE_SMC_RPC_FUNC_ALLOC: + if (!optee_shm_add(priv, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL, + param->a1, TEE_SHM_ALLOC | TEE_SHM_REGISTER, + &shme)) + { + reg_pair_from_64(¶m->a1, ¶m->a2, + virt_to_phys((void *)(uintptr_t)shme->shm.addr)); + + /* "cookie" */ + + reg_pair_from_64(¶m->a4, ¶m->a5, (uintptr_t)shme); + } + else + { + param->a1 = 0; + param->a2 = 0; + param->a4 = 0; + param->a5 = 0; + } + break; + case OPTEE_SMC_RPC_FUNC_FREE: + shme = reg_pair_to_ptr(param->a1, param->a2); + optee_shm_free(shme); + break; + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: + break; + case OPTEE_SMC_RPC_FUNC_CMD: + shme = reg_pair_to_ptr(param->a1, param->a2); + + /* optee_suppl_cmd(priv, shme, page_list); */ + + syslog(LOG_ERR, "OP-TEE RPC handling of supplicant commands not " + "implemented"); + break; + default: + break; + } + + param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; +} + +static int call_err_to_res(uint32_t call_err) +{ + switch (call_err) + { + case OPTEE_SMC_RETURN_OK: + return TEE_SUCCESS; + default: + return (int)call_err; + } +} + +static int optee_smc64_call(FAR struct optee_priv_data *priv, + FAR struct optee_msg_arg *arg) +{ + struct arm64_smccc_res res; + struct rpc_param param = + { + .a0 = OPTEE_SMC_CALL_WITH_ARG + }; + + reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg)); + + while (1) + { + memset(&res, 0, sizeof(struct arm64_smccc_res)); + + /* If cache is off from NuttX, sync the cache shared with OP-TEE */ + + if (!dcache_enabled()) Review Comment: Perhaps because it can be enabled on secure world and then one side is accessing memory directly while the other is accessing it through the cache. But like I said, leaning towards assuming the cache is always enabled (but then someone with disable cache might have a hard time troubleshooting weird errors). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org