18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2016, Linaro Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/cdev.h> 98c2ecf20Sopenharmony_ci#include <linux/cred.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/idr.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/tee_drv.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <crypto/hash.h> 178c2ecf20Sopenharmony_ci#include <crypto/sha.h> 188c2ecf20Sopenharmony_ci#include "tee_private.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TEE_NUM_DEVICES 32 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define TEE_IOCTL_PARAM_SIZE(x) (sizeof(struct tee_param) * (x)) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define TEE_UUID_NS_NAME_SIZE 128 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * TEE Client UUID name space identifier (UUIDv4) 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Value here is random UUID that is allocated as name space identifier for 308c2ecf20Sopenharmony_ci * forming Client UUID's for TEE environment using UUIDv5 scheme. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683, 338c2ecf20Sopenharmony_ci 0xa1, 0xb8, 0xec, 0x4b, 348c2ecf20Sopenharmony_ci 0xc0, 0x8e, 0x01, 0xb6); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Unprivileged devices in the lower half range and privileged devices in 388c2ecf20Sopenharmony_ci * the upper half range. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); 418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(driver_lock); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct class *tee_class; 448c2ecf20Sopenharmony_cistatic dev_t tee_devt; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct tee_context *teedev_open(struct tee_device *teedev) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int rc; 498c2ecf20Sopenharmony_ci struct tee_context *ctx; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!tee_device_get(teedev)) 528c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 558c2ecf20Sopenharmony_ci if (!ctx) { 568c2ecf20Sopenharmony_ci rc = -ENOMEM; 578c2ecf20Sopenharmony_ci goto err; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci kref_init(&ctx->refcount); 618c2ecf20Sopenharmony_ci ctx->teedev = teedev; 628c2ecf20Sopenharmony_ci rc = teedev->desc->ops->open(ctx); 638c2ecf20Sopenharmony_ci if (rc) 648c2ecf20Sopenharmony_ci goto err; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return ctx; 678c2ecf20Sopenharmony_cierr: 688c2ecf20Sopenharmony_ci kfree(ctx); 698c2ecf20Sopenharmony_ci tee_device_put(teedev); 708c2ecf20Sopenharmony_ci return ERR_PTR(rc); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(teedev_open); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_civoid teedev_ctx_get(struct tee_context *ctx) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci if (ctx->releasing) 788c2ecf20Sopenharmony_ci return; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci kref_get(&ctx->refcount); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void teedev_ctx_release(struct kref *ref) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct tee_context *ctx = container_of(ref, struct tee_context, 868c2ecf20Sopenharmony_ci refcount); 878c2ecf20Sopenharmony_ci ctx->releasing = true; 888c2ecf20Sopenharmony_ci ctx->teedev->desc->ops->release(ctx); 898c2ecf20Sopenharmony_ci kfree(ctx); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_civoid teedev_ctx_put(struct tee_context *ctx) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci if (ctx->releasing) 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci kref_put(&ctx->refcount, teedev_ctx_release); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_civoid teedev_close_context(struct tee_context *ctx) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci teedev_ctx_put(ctx); 1058c2ecf20Sopenharmony_ci tee_device_put(teedev); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(teedev_close_context); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int tee_open(struct inode *inode, struct file *filp) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct tee_context *ctx; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev)); 1148c2ecf20Sopenharmony_ci if (IS_ERR(ctx)) 1158c2ecf20Sopenharmony_ci return PTR_ERR(ctx); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * Default user-space behaviour is to wait for tee-supplicant 1198c2ecf20Sopenharmony_ci * if not present for any requests in this context. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci ctx->supp_nowait = false; 1228c2ecf20Sopenharmony_ci filp->private_data = ctx; 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int tee_release(struct inode *inode, struct file *filp) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci teedev_close_context(filp->private_data); 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/** 1338c2ecf20Sopenharmony_ci * uuid_v5() - Calculate UUIDv5 1348c2ecf20Sopenharmony_ci * @uuid: Resulting UUID 1358c2ecf20Sopenharmony_ci * @ns: Name space ID for UUIDv5 function 1368c2ecf20Sopenharmony_ci * @name: Name for UUIDv5 function 1378c2ecf20Sopenharmony_ci * @size: Size of name 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * UUIDv5 is specific in RFC 4122. 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * This implements section (for SHA-1): 1428c2ecf20Sopenharmony_ci * 4.3. Algorithm for Creating a Name-Based UUID 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistatic int uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, 1458c2ecf20Sopenharmony_ci size_t size) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned char hash[SHA1_DIGEST_SIZE]; 1488c2ecf20Sopenharmony_ci struct crypto_shash *shash = NULL; 1498c2ecf20Sopenharmony_ci struct shash_desc *desc = NULL; 1508c2ecf20Sopenharmony_ci int rc; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci shash = crypto_alloc_shash("sha1", 0, 0); 1538c2ecf20Sopenharmony_ci if (IS_ERR(shash)) { 1548c2ecf20Sopenharmony_ci rc = PTR_ERR(shash); 1558c2ecf20Sopenharmony_ci pr_err("shash(sha1) allocation failed\n"); 1568c2ecf20Sopenharmony_ci return rc; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(shash), 1608c2ecf20Sopenharmony_ci GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (!desc) { 1628c2ecf20Sopenharmony_ci rc = -ENOMEM; 1638c2ecf20Sopenharmony_ci goto out_free_shash; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci desc->tfm = shash; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci rc = crypto_shash_init(desc); 1698c2ecf20Sopenharmony_ci if (rc < 0) 1708c2ecf20Sopenharmony_ci goto out_free_desc; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci rc = crypto_shash_update(desc, (const u8 *)ns, sizeof(*ns)); 1738c2ecf20Sopenharmony_ci if (rc < 0) 1748c2ecf20Sopenharmony_ci goto out_free_desc; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci rc = crypto_shash_update(desc, (const u8 *)name, size); 1778c2ecf20Sopenharmony_ci if (rc < 0) 1788c2ecf20Sopenharmony_ci goto out_free_desc; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci rc = crypto_shash_final(desc, hash); 1818c2ecf20Sopenharmony_ci if (rc < 0) 1828c2ecf20Sopenharmony_ci goto out_free_desc; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci memcpy(uuid->b, hash, UUID_SIZE); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Tag for version 5 */ 1878c2ecf20Sopenharmony_ci uuid->b[6] = (hash[6] & 0x0F) | 0x50; 1888c2ecf20Sopenharmony_ci uuid->b[8] = (hash[8] & 0x3F) | 0x80; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciout_free_desc: 1918c2ecf20Sopenharmony_ci kfree(desc); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciout_free_shash: 1948c2ecf20Sopenharmony_ci crypto_free_shash(shash); 1958c2ecf20Sopenharmony_ci return rc; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, 1998c2ecf20Sopenharmony_ci const u8 connection_data[TEE_IOCTL_UUID_LEN]) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci gid_t ns_grp = (gid_t)-1; 2028c2ecf20Sopenharmony_ci kgid_t grp = INVALID_GID; 2038c2ecf20Sopenharmony_ci char *name = NULL; 2048c2ecf20Sopenharmony_ci int name_len; 2058c2ecf20Sopenharmony_ci int rc; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || 2088c2ecf20Sopenharmony_ci connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { 2098c2ecf20Sopenharmony_ci /* Nil UUID to be passed to TEE environment */ 2108c2ecf20Sopenharmony_ci uuid_copy(uuid, &uuid_null); 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * In Linux environment client UUID is based on UUIDv5. 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Determine client UUID with following semantics for 'name': 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * For TEEC_LOGIN_USER: 2208c2ecf20Sopenharmony_ci * uid=<uid> 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * For TEEC_LOGIN_GROUP: 2238c2ecf20Sopenharmony_ci * gid=<gid> 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci name = kzalloc(TEE_UUID_NS_NAME_SIZE, GFP_KERNEL); 2288c2ecf20Sopenharmony_ci if (!name) 2298c2ecf20Sopenharmony_ci return -ENOMEM; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci switch (connection_method) { 2328c2ecf20Sopenharmony_ci case TEE_IOCTL_LOGIN_USER: 2338c2ecf20Sopenharmony_ci name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "uid=%x", 2348c2ecf20Sopenharmony_ci current_euid().val); 2358c2ecf20Sopenharmony_ci if (name_len >= TEE_UUID_NS_NAME_SIZE) { 2368c2ecf20Sopenharmony_ci rc = -E2BIG; 2378c2ecf20Sopenharmony_ci goto out_free_name; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case TEE_IOCTL_LOGIN_GROUP: 2428c2ecf20Sopenharmony_ci memcpy(&ns_grp, connection_data, sizeof(gid_t)); 2438c2ecf20Sopenharmony_ci grp = make_kgid(current_user_ns(), ns_grp); 2448c2ecf20Sopenharmony_ci if (!gid_valid(grp) || !in_egroup_p(grp)) { 2458c2ecf20Sopenharmony_ci rc = -EPERM; 2468c2ecf20Sopenharmony_ci goto out_free_name; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "gid=%x", 2508c2ecf20Sopenharmony_ci grp.val); 2518c2ecf20Sopenharmony_ci if (name_len >= TEE_UUID_NS_NAME_SIZE) { 2528c2ecf20Sopenharmony_ci rc = -E2BIG; 2538c2ecf20Sopenharmony_ci goto out_free_name; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci default: 2588c2ecf20Sopenharmony_ci rc = -EINVAL; 2598c2ecf20Sopenharmony_ci goto out_free_name; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci rc = uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); 2638c2ecf20Sopenharmony_ciout_free_name: 2648c2ecf20Sopenharmony_ci kfree(name); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return rc; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_session_calc_client_uuid); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int tee_ioctl_version(struct tee_context *ctx, 2718c2ecf20Sopenharmony_ci struct tee_ioctl_version_data __user *uvers) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct tee_ioctl_version_data vers; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) 2788c2ecf20Sopenharmony_ci vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (copy_to_user(uvers, &vers, sizeof(vers))) 2818c2ecf20Sopenharmony_ci return -EFAULT; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int tee_ioctl_shm_alloc(struct tee_context *ctx, 2878c2ecf20Sopenharmony_ci struct tee_ioctl_shm_alloc_data __user *udata) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci long ret; 2908c2ecf20Sopenharmony_ci struct tee_ioctl_shm_alloc_data data; 2918c2ecf20Sopenharmony_ci struct tee_shm *shm; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (copy_from_user(&data, udata, sizeof(data))) 2948c2ecf20Sopenharmony_ci return -EFAULT; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Currently no input flags are supported */ 2978c2ecf20Sopenharmony_ci if (data.flags) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); 3018c2ecf20Sopenharmony_ci if (IS_ERR(shm)) 3028c2ecf20Sopenharmony_ci return PTR_ERR(shm); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci data.id = shm->id; 3058c2ecf20Sopenharmony_ci data.flags = shm->flags; 3068c2ecf20Sopenharmony_ci data.size = shm->size; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (copy_to_user(udata, &data, sizeof(data))) 3098c2ecf20Sopenharmony_ci ret = -EFAULT; 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci ret = tee_shm_get_fd(shm); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * When user space closes the file descriptor the shared memory 3158c2ecf20Sopenharmony_ci * should be freed or if tee_shm_get_fd() failed then it will 3168c2ecf20Sopenharmony_ci * be freed immediately. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci tee_shm_put(shm); 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int 3238c2ecf20Sopenharmony_citee_ioctl_shm_register(struct tee_context *ctx, 3248c2ecf20Sopenharmony_ci struct tee_ioctl_shm_register_data __user *udata) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci long ret; 3278c2ecf20Sopenharmony_ci struct tee_ioctl_shm_register_data data; 3288c2ecf20Sopenharmony_ci struct tee_shm *shm; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (copy_from_user(&data, udata, sizeof(data))) 3318c2ecf20Sopenharmony_ci return -EFAULT; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Currently no input flags are supported */ 3348c2ecf20Sopenharmony_ci if (data.flags) 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!access_ok((void __user *)(unsigned long)data.addr, data.length)) 3388c2ecf20Sopenharmony_ci return -EFAULT; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci shm = tee_shm_register(ctx, data.addr, data.length, 3418c2ecf20Sopenharmony_ci TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED); 3428c2ecf20Sopenharmony_ci if (IS_ERR(shm)) 3438c2ecf20Sopenharmony_ci return PTR_ERR(shm); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci data.id = shm->id; 3468c2ecf20Sopenharmony_ci data.flags = shm->flags; 3478c2ecf20Sopenharmony_ci data.length = shm->size; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (copy_to_user(udata, &data, sizeof(data))) 3508c2ecf20Sopenharmony_ci ret = -EFAULT; 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci ret = tee_shm_get_fd(shm); 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * When user space closes the file descriptor the shared memory 3558c2ecf20Sopenharmony_ci * should be freed or if tee_shm_get_fd() failed then it will 3568c2ecf20Sopenharmony_ci * be freed immediately. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci tee_shm_put(shm); 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int params_from_user(struct tee_context *ctx, struct tee_param *params, 3638c2ecf20Sopenharmony_ci size_t num_params, 3648c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *uparams) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci size_t n; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 3698c2ecf20Sopenharmony_ci struct tee_shm *shm; 3708c2ecf20Sopenharmony_ci struct tee_ioctl_param ip; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (copy_from_user(&ip, uparams + n, sizeof(ip))) 3738c2ecf20Sopenharmony_ci return -EFAULT; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* All unused attribute bits has to be zero */ 3768c2ecf20Sopenharmony_ci if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci params[n].attr = ip.attr; 3808c2ecf20Sopenharmony_ci switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { 3818c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: 3828c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: 3858c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 3868c2ecf20Sopenharmony_ci params[n].u.value.a = ip.a; 3878c2ecf20Sopenharmony_ci params[n].u.value.b = ip.b; 3888c2ecf20Sopenharmony_ci params[n].u.value.c = ip.c; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: 3918c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 3928c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * If a NULL pointer is passed to a TA in the TEE, 3958c2ecf20Sopenharmony_ci * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL 3968c2ecf20Sopenharmony_ci * indicating a NULL memory reference. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci if (ip.c != TEE_MEMREF_NULL) { 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * If we fail to get a pointer to a shared 4018c2ecf20Sopenharmony_ci * memory object (and increase the ref count) 4028c2ecf20Sopenharmony_ci * from an identifier we return an error. All 4038c2ecf20Sopenharmony_ci * pointers that has been added in params have 4048c2ecf20Sopenharmony_ci * an increased ref count. It's the callers 4058c2ecf20Sopenharmony_ci * responibility to do tee_shm_put() on all 4068c2ecf20Sopenharmony_ci * resolved pointers. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci shm = tee_shm_get_from_id(ctx, ip.c); 4098c2ecf20Sopenharmony_ci if (IS_ERR(shm)) 4108c2ecf20Sopenharmony_ci return PTR_ERR(shm); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Ensure offset + size does not overflow 4148c2ecf20Sopenharmony_ci * offset and does not overflow the size of 4158c2ecf20Sopenharmony_ci * the referred shared memory object. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci if ((ip.a + ip.b) < ip.a || 4188c2ecf20Sopenharmony_ci (ip.a + ip.b) > shm->size) { 4198c2ecf20Sopenharmony_ci tee_shm_put(shm); 4208c2ecf20Sopenharmony_ci return -EINVAL; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } else if (ctx->cap_memref_null) { 4238c2ecf20Sopenharmony_ci /* Pass NULL pointer to OP-TEE */ 4248c2ecf20Sopenharmony_ci shm = NULL; 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci params[n].u.memref.shm_offs = ip.a; 4308c2ecf20Sopenharmony_ci params[n].u.memref.size = ip.b; 4318c2ecf20Sopenharmony_ci params[n].u.memref.shm = shm; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci /* Unknown attribute */ 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int params_to_user(struct tee_ioctl_param __user *uparams, 4428c2ecf20Sopenharmony_ci size_t num_params, struct tee_param *params) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci size_t n; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 4478c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *up = uparams + n; 4488c2ecf20Sopenharmony_ci struct tee_param *p = params + n; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci switch (p->attr) { 4518c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 4528c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 4538c2ecf20Sopenharmony_ci if (put_user(p->u.value.a, &up->a) || 4548c2ecf20Sopenharmony_ci put_user(p->u.value.b, &up->b) || 4558c2ecf20Sopenharmony_ci put_user(p->u.value.c, &up->c)) 4568c2ecf20Sopenharmony_ci return -EFAULT; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 4598c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 4608c2ecf20Sopenharmony_ci if (put_user((u64)p->u.memref.size, &up->b)) 4618c2ecf20Sopenharmony_ci return -EFAULT; 4628c2ecf20Sopenharmony_ci default: 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic int tee_ioctl_open_session(struct tee_context *ctx, 4708c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data __user *ubuf) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci int rc; 4738c2ecf20Sopenharmony_ci size_t n; 4748c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data buf; 4758c2ecf20Sopenharmony_ci struct tee_ioctl_open_session_arg __user *uarg; 4768c2ecf20Sopenharmony_ci struct tee_ioctl_open_session_arg arg; 4778c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *uparams = NULL; 4788c2ecf20Sopenharmony_ci struct tee_param *params = NULL; 4798c2ecf20Sopenharmony_ci bool have_session = false; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->open_session) 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (copy_from_user(&buf, ubuf, sizeof(buf))) 4858c2ecf20Sopenharmony_ci return -EFAULT; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (buf.buf_len > TEE_MAX_ARG_SIZE || 4888c2ecf20Sopenharmony_ci buf.buf_len < sizeof(struct tee_ioctl_open_session_arg)) 4898c2ecf20Sopenharmony_ci return -EINVAL; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci uarg = u64_to_user_ptr(buf.buf_ptr); 4928c2ecf20Sopenharmony_ci if (copy_from_user(&arg, uarg, sizeof(arg))) 4938c2ecf20Sopenharmony_ci return -EFAULT; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) 4968c2ecf20Sopenharmony_ci return -EINVAL; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (arg.num_params) { 4998c2ecf20Sopenharmony_ci params = kcalloc(arg.num_params, sizeof(struct tee_param), 5008c2ecf20Sopenharmony_ci GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (!params) 5028c2ecf20Sopenharmony_ci return -ENOMEM; 5038c2ecf20Sopenharmony_ci uparams = uarg->params; 5048c2ecf20Sopenharmony_ci rc = params_from_user(ctx, params, arg.num_params, uparams); 5058c2ecf20Sopenharmony_ci if (rc) 5068c2ecf20Sopenharmony_ci goto out; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (arg.clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN && 5108c2ecf20Sopenharmony_ci arg.clnt_login <= TEE_IOCTL_LOGIN_REE_KERNEL_MAX) { 5118c2ecf20Sopenharmony_ci pr_debug("login method not allowed for user-space client\n"); 5128c2ecf20Sopenharmony_ci rc = -EPERM; 5138c2ecf20Sopenharmony_ci goto out; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params); 5178c2ecf20Sopenharmony_ci if (rc) 5188c2ecf20Sopenharmony_ci goto out; 5198c2ecf20Sopenharmony_ci have_session = true; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (put_user(arg.session, &uarg->session) || 5228c2ecf20Sopenharmony_ci put_user(arg.ret, &uarg->ret) || 5238c2ecf20Sopenharmony_ci put_user(arg.ret_origin, &uarg->ret_origin)) { 5248c2ecf20Sopenharmony_ci rc = -EFAULT; 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci rc = params_to_user(uparams, arg.num_params, params); 5288c2ecf20Sopenharmony_ciout: 5298c2ecf20Sopenharmony_ci /* 5308c2ecf20Sopenharmony_ci * If we've succeeded to open the session but failed to communicate 5318c2ecf20Sopenharmony_ci * it back to user space, close the session again to avoid leakage. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ci if (rc && have_session && ctx->teedev->desc->ops->close_session) 5348c2ecf20Sopenharmony_ci ctx->teedev->desc->ops->close_session(ctx, arg.session); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (params) { 5378c2ecf20Sopenharmony_ci /* Decrease ref count for all valid shared memory pointers */ 5388c2ecf20Sopenharmony_ci for (n = 0; n < arg.num_params; n++) 5398c2ecf20Sopenharmony_ci if (tee_param_is_memref(params + n) && 5408c2ecf20Sopenharmony_ci params[n].u.memref.shm) 5418c2ecf20Sopenharmony_ci tee_shm_put(params[n].u.memref.shm); 5428c2ecf20Sopenharmony_ci kfree(params); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return rc; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic int tee_ioctl_invoke(struct tee_context *ctx, 5498c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data __user *ubuf) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci int rc; 5528c2ecf20Sopenharmony_ci size_t n; 5538c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data buf; 5548c2ecf20Sopenharmony_ci struct tee_ioctl_invoke_arg __user *uarg; 5558c2ecf20Sopenharmony_ci struct tee_ioctl_invoke_arg arg; 5568c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *uparams = NULL; 5578c2ecf20Sopenharmony_ci struct tee_param *params = NULL; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->invoke_func) 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (copy_from_user(&buf, ubuf, sizeof(buf))) 5638c2ecf20Sopenharmony_ci return -EFAULT; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (buf.buf_len > TEE_MAX_ARG_SIZE || 5668c2ecf20Sopenharmony_ci buf.buf_len < sizeof(struct tee_ioctl_invoke_arg)) 5678c2ecf20Sopenharmony_ci return -EINVAL; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci uarg = u64_to_user_ptr(buf.buf_ptr); 5708c2ecf20Sopenharmony_ci if (copy_from_user(&arg, uarg, sizeof(arg))) 5718c2ecf20Sopenharmony_ci return -EFAULT; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) 5748c2ecf20Sopenharmony_ci return -EINVAL; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (arg.num_params) { 5778c2ecf20Sopenharmony_ci params = kcalloc(arg.num_params, sizeof(struct tee_param), 5788c2ecf20Sopenharmony_ci GFP_KERNEL); 5798c2ecf20Sopenharmony_ci if (!params) 5808c2ecf20Sopenharmony_ci return -ENOMEM; 5818c2ecf20Sopenharmony_ci uparams = uarg->params; 5828c2ecf20Sopenharmony_ci rc = params_from_user(ctx, params, arg.num_params, uparams); 5838c2ecf20Sopenharmony_ci if (rc) 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params); 5888c2ecf20Sopenharmony_ci if (rc) 5898c2ecf20Sopenharmony_ci goto out; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (put_user(arg.ret, &uarg->ret) || 5928c2ecf20Sopenharmony_ci put_user(arg.ret_origin, &uarg->ret_origin)) { 5938c2ecf20Sopenharmony_ci rc = -EFAULT; 5948c2ecf20Sopenharmony_ci goto out; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci rc = params_to_user(uparams, arg.num_params, params); 5978c2ecf20Sopenharmony_ciout: 5988c2ecf20Sopenharmony_ci if (params) { 5998c2ecf20Sopenharmony_ci /* Decrease ref count for all valid shared memory pointers */ 6008c2ecf20Sopenharmony_ci for (n = 0; n < arg.num_params; n++) 6018c2ecf20Sopenharmony_ci if (tee_param_is_memref(params + n) && 6028c2ecf20Sopenharmony_ci params[n].u.memref.shm) 6038c2ecf20Sopenharmony_ci tee_shm_put(params[n].u.memref.shm); 6048c2ecf20Sopenharmony_ci kfree(params); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci return rc; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int tee_ioctl_cancel(struct tee_context *ctx, 6108c2ecf20Sopenharmony_ci struct tee_ioctl_cancel_arg __user *uarg) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct tee_ioctl_cancel_arg arg; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->cancel_req) 6158c2ecf20Sopenharmony_ci return -EINVAL; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (copy_from_user(&arg, uarg, sizeof(arg))) 6188c2ecf20Sopenharmony_ci return -EFAULT; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->cancel_req(ctx, arg.cancel_id, 6218c2ecf20Sopenharmony_ci arg.session); 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic int 6258c2ecf20Sopenharmony_citee_ioctl_close_session(struct tee_context *ctx, 6268c2ecf20Sopenharmony_ci struct tee_ioctl_close_session_arg __user *uarg) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct tee_ioctl_close_session_arg arg; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->close_session) 6318c2ecf20Sopenharmony_ci return -EINVAL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (copy_from_user(&arg, uarg, sizeof(arg))) 6348c2ecf20Sopenharmony_ci return -EFAULT; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->close_session(ctx, arg.session); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int params_to_supp(struct tee_context *ctx, 6408c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *uparams, 6418c2ecf20Sopenharmony_ci size_t num_params, struct tee_param *params) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci size_t n; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 6468c2ecf20Sopenharmony_ci struct tee_ioctl_param ip; 6478c2ecf20Sopenharmony_ci struct tee_param *p = params + n; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ip.attr = p->attr; 6508c2ecf20Sopenharmony_ci switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { 6518c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: 6528c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 6538c2ecf20Sopenharmony_ci ip.a = p->u.value.a; 6548c2ecf20Sopenharmony_ci ip.b = p->u.value.b; 6558c2ecf20Sopenharmony_ci ip.c = p->u.value.c; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: 6588c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 6598c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 6608c2ecf20Sopenharmony_ci ip.b = p->u.memref.size; 6618c2ecf20Sopenharmony_ci if (!p->u.memref.shm) { 6628c2ecf20Sopenharmony_ci ip.a = 0; 6638c2ecf20Sopenharmony_ci ip.c = (u64)-1; /* invalid shm id */ 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci ip.a = p->u.memref.shm_offs; 6678c2ecf20Sopenharmony_ci ip.c = p->u.memref.shm->id; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci default: 6708c2ecf20Sopenharmony_ci ip.a = 0; 6718c2ecf20Sopenharmony_ci ip.b = 0; 6728c2ecf20Sopenharmony_ci ip.c = 0; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (copy_to_user(uparams + n, &ip, sizeof(ip))) 6778c2ecf20Sopenharmony_ci return -EFAULT; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int tee_ioctl_supp_recv(struct tee_context *ctx, 6848c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data __user *ubuf) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci int rc; 6878c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data buf; 6888c2ecf20Sopenharmony_ci struct tee_iocl_supp_recv_arg __user *uarg; 6898c2ecf20Sopenharmony_ci struct tee_param *params; 6908c2ecf20Sopenharmony_ci u32 num_params; 6918c2ecf20Sopenharmony_ci u32 func; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->supp_recv) 6948c2ecf20Sopenharmony_ci return -EINVAL; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (copy_from_user(&buf, ubuf, sizeof(buf))) 6978c2ecf20Sopenharmony_ci return -EFAULT; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (buf.buf_len > TEE_MAX_ARG_SIZE || 7008c2ecf20Sopenharmony_ci buf.buf_len < sizeof(struct tee_iocl_supp_recv_arg)) 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci uarg = u64_to_user_ptr(buf.buf_ptr); 7048c2ecf20Sopenharmony_ci if (get_user(num_params, &uarg->num_params)) 7058c2ecf20Sopenharmony_ci return -EFAULT; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) != buf.buf_len) 7088c2ecf20Sopenharmony_ci return -EINVAL; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); 7118c2ecf20Sopenharmony_ci if (!params) 7128c2ecf20Sopenharmony_ci return -ENOMEM; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci rc = params_from_user(ctx, params, num_params, uarg->params); 7158c2ecf20Sopenharmony_ci if (rc) 7168c2ecf20Sopenharmony_ci goto out; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); 7198c2ecf20Sopenharmony_ci if (rc) 7208c2ecf20Sopenharmony_ci goto out; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (put_user(func, &uarg->func) || 7238c2ecf20Sopenharmony_ci put_user(num_params, &uarg->num_params)) { 7248c2ecf20Sopenharmony_ci rc = -EFAULT; 7258c2ecf20Sopenharmony_ci goto out; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci rc = params_to_supp(ctx, uarg->params, num_params, params); 7298c2ecf20Sopenharmony_ciout: 7308c2ecf20Sopenharmony_ci kfree(params); 7318c2ecf20Sopenharmony_ci return rc; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic int params_from_supp(struct tee_param *params, size_t num_params, 7358c2ecf20Sopenharmony_ci struct tee_ioctl_param __user *uparams) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci size_t n; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci for (n = 0; n < num_params; n++) { 7408c2ecf20Sopenharmony_ci struct tee_param *p = params + n; 7418c2ecf20Sopenharmony_ci struct tee_ioctl_param ip; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (copy_from_user(&ip, uparams + n, sizeof(ip))) 7448c2ecf20Sopenharmony_ci return -EFAULT; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* All unused attribute bits has to be zero */ 7478c2ecf20Sopenharmony_ci if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci p->attr = ip.attr; 7518c2ecf20Sopenharmony_ci switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { 7528c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 7538c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 7548c2ecf20Sopenharmony_ci /* Only out and in/out values can be updated */ 7558c2ecf20Sopenharmony_ci p->u.value.a = ip.a; 7568c2ecf20Sopenharmony_ci p->u.value.b = ip.b; 7578c2ecf20Sopenharmony_ci p->u.value.c = ip.c; 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 7608c2ecf20Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * Only the size of the memref can be updated. 7638c2ecf20Sopenharmony_ci * Since we don't have access to the original 7648c2ecf20Sopenharmony_ci * parameters here, only store the supplied size. 7658c2ecf20Sopenharmony_ci * The driver will copy the updated size into the 7668c2ecf20Sopenharmony_ci * original parameters. 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_ci p->u.memref.shm = NULL; 7698c2ecf20Sopenharmony_ci p->u.memref.shm_offs = 0; 7708c2ecf20Sopenharmony_ci p->u.memref.size = ip.b; 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci default: 7738c2ecf20Sopenharmony_ci memset(&p->u, 0, sizeof(p->u)); 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic int tee_ioctl_supp_send(struct tee_context *ctx, 7818c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data __user *ubuf) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci long rc; 7848c2ecf20Sopenharmony_ci struct tee_ioctl_buf_data buf; 7858c2ecf20Sopenharmony_ci struct tee_iocl_supp_send_arg __user *uarg; 7868c2ecf20Sopenharmony_ci struct tee_param *params; 7878c2ecf20Sopenharmony_ci u32 num_params; 7888c2ecf20Sopenharmony_ci u32 ret; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Not valid for this driver */ 7918c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->supp_send) 7928c2ecf20Sopenharmony_ci return -EINVAL; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (copy_from_user(&buf, ubuf, sizeof(buf))) 7958c2ecf20Sopenharmony_ci return -EFAULT; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (buf.buf_len > TEE_MAX_ARG_SIZE || 7988c2ecf20Sopenharmony_ci buf.buf_len < sizeof(struct tee_iocl_supp_send_arg)) 7998c2ecf20Sopenharmony_ci return -EINVAL; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci uarg = u64_to_user_ptr(buf.buf_ptr); 8028c2ecf20Sopenharmony_ci if (get_user(ret, &uarg->ret) || 8038c2ecf20Sopenharmony_ci get_user(num_params, &uarg->num_params)) 8048c2ecf20Sopenharmony_ci return -EFAULT; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) > buf.buf_len) 8078c2ecf20Sopenharmony_ci return -EINVAL; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); 8108c2ecf20Sopenharmony_ci if (!params) 8118c2ecf20Sopenharmony_ci return -ENOMEM; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci rc = params_from_supp(params, num_params, uarg->params); 8148c2ecf20Sopenharmony_ci if (rc) 8158c2ecf20Sopenharmony_ci goto out; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci rc = ctx->teedev->desc->ops->supp_send(ctx, ret, num_params, params); 8188c2ecf20Sopenharmony_ciout: 8198c2ecf20Sopenharmony_ci kfree(params); 8208c2ecf20Sopenharmony_ci return rc; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct tee_context *ctx = filp->private_data; 8268c2ecf20Sopenharmony_ci void __user *uarg = (void __user *)arg; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci switch (cmd) { 8298c2ecf20Sopenharmony_ci case TEE_IOC_VERSION: 8308c2ecf20Sopenharmony_ci return tee_ioctl_version(ctx, uarg); 8318c2ecf20Sopenharmony_ci case TEE_IOC_SHM_ALLOC: 8328c2ecf20Sopenharmony_ci return tee_ioctl_shm_alloc(ctx, uarg); 8338c2ecf20Sopenharmony_ci case TEE_IOC_SHM_REGISTER: 8348c2ecf20Sopenharmony_ci return tee_ioctl_shm_register(ctx, uarg); 8358c2ecf20Sopenharmony_ci case TEE_IOC_OPEN_SESSION: 8368c2ecf20Sopenharmony_ci return tee_ioctl_open_session(ctx, uarg); 8378c2ecf20Sopenharmony_ci case TEE_IOC_INVOKE: 8388c2ecf20Sopenharmony_ci return tee_ioctl_invoke(ctx, uarg); 8398c2ecf20Sopenharmony_ci case TEE_IOC_CANCEL: 8408c2ecf20Sopenharmony_ci return tee_ioctl_cancel(ctx, uarg); 8418c2ecf20Sopenharmony_ci case TEE_IOC_CLOSE_SESSION: 8428c2ecf20Sopenharmony_ci return tee_ioctl_close_session(ctx, uarg); 8438c2ecf20Sopenharmony_ci case TEE_IOC_SUPPL_RECV: 8448c2ecf20Sopenharmony_ci return tee_ioctl_supp_recv(ctx, uarg); 8458c2ecf20Sopenharmony_ci case TEE_IOC_SUPPL_SEND: 8468c2ecf20Sopenharmony_ci return tee_ioctl_supp_send(ctx, uarg); 8478c2ecf20Sopenharmony_ci default: 8488c2ecf20Sopenharmony_ci return -EINVAL; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic const struct file_operations tee_fops = { 8538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8548c2ecf20Sopenharmony_ci .open = tee_open, 8558c2ecf20Sopenharmony_ci .release = tee_release, 8568c2ecf20Sopenharmony_ci .unlocked_ioctl = tee_ioctl, 8578c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 8588c2ecf20Sopenharmony_ci}; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic void tee_release_device(struct device *dev) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct tee_device *teedev = container_of(dev, struct tee_device, dev); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci spin_lock(&driver_lock); 8658c2ecf20Sopenharmony_ci clear_bit(teedev->id, dev_mask); 8668c2ecf20Sopenharmony_ci spin_unlock(&driver_lock); 8678c2ecf20Sopenharmony_ci mutex_destroy(&teedev->mutex); 8688c2ecf20Sopenharmony_ci idr_destroy(&teedev->idr); 8698c2ecf20Sopenharmony_ci kfree(teedev); 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci/** 8738c2ecf20Sopenharmony_ci * tee_device_alloc() - Allocate a new struct tee_device instance 8748c2ecf20Sopenharmony_ci * @teedesc: Descriptor for this driver 8758c2ecf20Sopenharmony_ci * @dev: Parent device for this device 8768c2ecf20Sopenharmony_ci * @pool: Shared memory pool, NULL if not used 8778c2ecf20Sopenharmony_ci * @driver_data: Private driver data for this device 8788c2ecf20Sopenharmony_ci * 8798c2ecf20Sopenharmony_ci * Allocates a new struct tee_device instance. The device is 8808c2ecf20Sopenharmony_ci * removed by tee_device_unregister(). 8818c2ecf20Sopenharmony_ci * 8828c2ecf20Sopenharmony_ci * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_cistruct tee_device *tee_device_alloc(const struct tee_desc *teedesc, 8858c2ecf20Sopenharmony_ci struct device *dev, 8868c2ecf20Sopenharmony_ci struct tee_shm_pool *pool, 8878c2ecf20Sopenharmony_ci void *driver_data) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct tee_device *teedev; 8908c2ecf20Sopenharmony_ci void *ret; 8918c2ecf20Sopenharmony_ci int rc, max_id; 8928c2ecf20Sopenharmony_ci int offs = 0; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (!teedesc || !teedesc->name || !teedesc->ops || 8958c2ecf20Sopenharmony_ci !teedesc->ops->get_version || !teedesc->ops->open || 8968c2ecf20Sopenharmony_ci !teedesc->ops->release || !pool) 8978c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci teedev = kzalloc(sizeof(*teedev), GFP_KERNEL); 9008c2ecf20Sopenharmony_ci if (!teedev) { 9018c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 9028c2ecf20Sopenharmony_ci goto err; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci max_id = TEE_NUM_DEVICES / 2; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (teedesc->flags & TEE_DESC_PRIVILEGED) { 9088c2ecf20Sopenharmony_ci offs = TEE_NUM_DEVICES / 2; 9098c2ecf20Sopenharmony_ci max_id = TEE_NUM_DEVICES; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci spin_lock(&driver_lock); 9138c2ecf20Sopenharmony_ci teedev->id = find_next_zero_bit(dev_mask, max_id, offs); 9148c2ecf20Sopenharmony_ci if (teedev->id < max_id) 9158c2ecf20Sopenharmony_ci set_bit(teedev->id, dev_mask); 9168c2ecf20Sopenharmony_ci spin_unlock(&driver_lock); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (teedev->id >= max_id) { 9198c2ecf20Sopenharmony_ci ret = ERR_PTR(-ENOMEM); 9208c2ecf20Sopenharmony_ci goto err; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci snprintf(teedev->name, sizeof(teedev->name), "tee%s%d", 9248c2ecf20Sopenharmony_ci teedesc->flags & TEE_DESC_PRIVILEGED ? "priv" : "", 9258c2ecf20Sopenharmony_ci teedev->id - offs); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci teedev->dev.class = tee_class; 9288c2ecf20Sopenharmony_ci teedev->dev.release = tee_release_device; 9298c2ecf20Sopenharmony_ci teedev->dev.parent = dev; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci teedev->dev.devt = MKDEV(MAJOR(tee_devt), teedev->id); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci rc = dev_set_name(&teedev->dev, "%s", teedev->name); 9348c2ecf20Sopenharmony_ci if (rc) { 9358c2ecf20Sopenharmony_ci ret = ERR_PTR(rc); 9368c2ecf20Sopenharmony_ci goto err_devt; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci cdev_init(&teedev->cdev, &tee_fops); 9408c2ecf20Sopenharmony_ci teedev->cdev.owner = teedesc->owner; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci dev_set_drvdata(&teedev->dev, driver_data); 9438c2ecf20Sopenharmony_ci device_initialize(&teedev->dev); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* 1 as tee_device_unregister() does one final tee_device_put() */ 9468c2ecf20Sopenharmony_ci teedev->num_users = 1; 9478c2ecf20Sopenharmony_ci init_completion(&teedev->c_no_users); 9488c2ecf20Sopenharmony_ci mutex_init(&teedev->mutex); 9498c2ecf20Sopenharmony_ci idr_init(&teedev->idr); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci teedev->desc = teedesc; 9528c2ecf20Sopenharmony_ci teedev->pool = pool; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return teedev; 9558c2ecf20Sopenharmony_cierr_devt: 9568c2ecf20Sopenharmony_ci unregister_chrdev_region(teedev->dev.devt, 1); 9578c2ecf20Sopenharmony_cierr: 9588c2ecf20Sopenharmony_ci pr_err("could not register %s driver\n", 9598c2ecf20Sopenharmony_ci teedesc->flags & TEE_DESC_PRIVILEGED ? "privileged" : "client"); 9608c2ecf20Sopenharmony_ci if (teedev && teedev->id < TEE_NUM_DEVICES) { 9618c2ecf20Sopenharmony_ci spin_lock(&driver_lock); 9628c2ecf20Sopenharmony_ci clear_bit(teedev->id, dev_mask); 9638c2ecf20Sopenharmony_ci spin_unlock(&driver_lock); 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci kfree(teedev); 9668c2ecf20Sopenharmony_ci return ret; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_device_alloc); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic ssize_t implementation_id_show(struct device *dev, 9718c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct tee_device *teedev = container_of(dev, struct tee_device, dev); 9748c2ecf20Sopenharmony_ci struct tee_ioctl_version_data vers; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci teedev->desc->ops->get_version(teedev, &vers); 9778c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", vers.impl_id); 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(implementation_id); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic struct attribute *tee_dev_attrs[] = { 9828c2ecf20Sopenharmony_ci &dev_attr_implementation_id.attr, 9838c2ecf20Sopenharmony_ci NULL 9848c2ecf20Sopenharmony_ci}; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(tee_dev); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci/** 9898c2ecf20Sopenharmony_ci * tee_device_register() - Registers a TEE device 9908c2ecf20Sopenharmony_ci * @teedev: Device to register 9918c2ecf20Sopenharmony_ci * 9928c2ecf20Sopenharmony_ci * tee_device_unregister() need to be called to remove the @teedev if 9938c2ecf20Sopenharmony_ci * this function fails. 9948c2ecf20Sopenharmony_ci * 9958c2ecf20Sopenharmony_ci * @returns < 0 on failure 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ciint tee_device_register(struct tee_device *teedev) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci int rc; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) { 10028c2ecf20Sopenharmony_ci dev_err(&teedev->dev, "attempt to register twice\n"); 10038c2ecf20Sopenharmony_ci return -EINVAL; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci teedev->dev.groups = tee_dev_groups; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci rc = cdev_device_add(&teedev->cdev, &teedev->dev); 10098c2ecf20Sopenharmony_ci if (rc) { 10108c2ecf20Sopenharmony_ci dev_err(&teedev->dev, 10118c2ecf20Sopenharmony_ci "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", 10128c2ecf20Sopenharmony_ci teedev->name, MAJOR(teedev->dev.devt), 10138c2ecf20Sopenharmony_ci MINOR(teedev->dev.devt), rc); 10148c2ecf20Sopenharmony_ci return rc; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci teedev->flags |= TEE_DEVICE_FLAG_REGISTERED; 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_device_register); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_civoid tee_device_put(struct tee_device *teedev) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 10258c2ecf20Sopenharmony_ci /* Shouldn't put in this state */ 10268c2ecf20Sopenharmony_ci if (!WARN_ON(!teedev->desc)) { 10278c2ecf20Sopenharmony_ci teedev->num_users--; 10288c2ecf20Sopenharmony_ci if (!teedev->num_users) { 10298c2ecf20Sopenharmony_ci teedev->desc = NULL; 10308c2ecf20Sopenharmony_ci complete(&teedev->c_no_users); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cibool tee_device_get(struct tee_device *teedev) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci mutex_lock(&teedev->mutex); 10398c2ecf20Sopenharmony_ci if (!teedev->desc) { 10408c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 10418c2ecf20Sopenharmony_ci return false; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci teedev->num_users++; 10448c2ecf20Sopenharmony_ci mutex_unlock(&teedev->mutex); 10458c2ecf20Sopenharmony_ci return true; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/** 10498c2ecf20Sopenharmony_ci * tee_device_unregister() - Removes a TEE device 10508c2ecf20Sopenharmony_ci * @teedev: Device to unregister 10518c2ecf20Sopenharmony_ci * 10528c2ecf20Sopenharmony_ci * This function should be called to remove the @teedev even if 10538c2ecf20Sopenharmony_ci * tee_device_register() hasn't been called yet. Does nothing if 10548c2ecf20Sopenharmony_ci * @teedev is NULL. 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_civoid tee_device_unregister(struct tee_device *teedev) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci if (!teedev) 10598c2ecf20Sopenharmony_ci return; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) 10628c2ecf20Sopenharmony_ci cdev_device_del(&teedev->cdev, &teedev->dev); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci tee_device_put(teedev); 10658c2ecf20Sopenharmony_ci wait_for_completion(&teedev->c_no_users); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* 10688c2ecf20Sopenharmony_ci * No need to take a mutex any longer now since teedev->desc was 10698c2ecf20Sopenharmony_ci * set to NULL before teedev->c_no_users was completed. 10708c2ecf20Sopenharmony_ci */ 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci teedev->pool = NULL; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci put_device(&teedev->dev); 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_device_unregister); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/** 10798c2ecf20Sopenharmony_ci * tee_get_drvdata() - Return driver_data pointer 10808c2ecf20Sopenharmony_ci * @teedev: Device containing the driver_data pointer 10818c2ecf20Sopenharmony_ci * @returns the driver_data pointer supplied to tee_register(). 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_civoid *tee_get_drvdata(struct tee_device *teedev) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci return dev_get_drvdata(&teedev->dev); 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_get_drvdata); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistruct match_dev_data { 10908c2ecf20Sopenharmony_ci struct tee_ioctl_version_data *vers; 10918c2ecf20Sopenharmony_ci const void *data; 10928c2ecf20Sopenharmony_ci int (*match)(struct tee_ioctl_version_data *, const void *); 10938c2ecf20Sopenharmony_ci}; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int match_dev(struct device *dev, const void *data) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci const struct match_dev_data *match_data = data; 10988c2ecf20Sopenharmony_ci struct tee_device *teedev = container_of(dev, struct tee_device, dev); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci teedev->desc->ops->get_version(teedev, match_data->vers); 11018c2ecf20Sopenharmony_ci return match_data->match(match_data->vers, match_data->data); 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistruct tee_context * 11058c2ecf20Sopenharmony_citee_client_open_context(struct tee_context *start, 11068c2ecf20Sopenharmony_ci int (*match)(struct tee_ioctl_version_data *, 11078c2ecf20Sopenharmony_ci const void *), 11088c2ecf20Sopenharmony_ci const void *data, struct tee_ioctl_version_data *vers) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci struct device *dev = NULL; 11118c2ecf20Sopenharmony_ci struct device *put_dev = NULL; 11128c2ecf20Sopenharmony_ci struct tee_context *ctx = NULL; 11138c2ecf20Sopenharmony_ci struct tee_ioctl_version_data v; 11148c2ecf20Sopenharmony_ci struct match_dev_data match_data = { vers ? vers : &v, data, match }; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (start) 11178c2ecf20Sopenharmony_ci dev = &start->teedev->dev; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci do { 11208c2ecf20Sopenharmony_ci dev = class_find_device(tee_class, dev, &match_data, match_dev); 11218c2ecf20Sopenharmony_ci if (!dev) { 11228c2ecf20Sopenharmony_ci ctx = ERR_PTR(-ENOENT); 11238c2ecf20Sopenharmony_ci break; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci put_device(put_dev); 11278c2ecf20Sopenharmony_ci put_dev = dev; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci ctx = teedev_open(container_of(dev, struct tee_device, dev)); 11308c2ecf20Sopenharmony_ci } while (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci put_device(put_dev); 11338c2ecf20Sopenharmony_ci /* 11348c2ecf20Sopenharmony_ci * Default behaviour for in kernel client is to not wait for 11358c2ecf20Sopenharmony_ci * tee-supplicant if not present for any requests in this context. 11368c2ecf20Sopenharmony_ci * Also this flag could be configured again before call to 11378c2ecf20Sopenharmony_ci * tee_client_open_session() if any in kernel client requires 11388c2ecf20Sopenharmony_ci * different behaviour. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci if (!IS_ERR(ctx)) 11418c2ecf20Sopenharmony_ci ctx->supp_nowait = true; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return ctx; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_open_context); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_civoid tee_client_close_context(struct tee_context *ctx) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci teedev_close_context(ctx); 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_close_context); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_civoid tee_client_get_version(struct tee_context *ctx, 11548c2ecf20Sopenharmony_ci struct tee_ioctl_version_data *vers) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci ctx->teedev->desc->ops->get_version(ctx->teedev, vers); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_get_version); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ciint tee_client_open_session(struct tee_context *ctx, 11618c2ecf20Sopenharmony_ci struct tee_ioctl_open_session_arg *arg, 11628c2ecf20Sopenharmony_ci struct tee_param *param) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->open_session) 11658c2ecf20Sopenharmony_ci return -EINVAL; 11668c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->open_session(ctx, arg, param); 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_open_session); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ciint tee_client_close_session(struct tee_context *ctx, u32 session) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->close_session) 11738c2ecf20Sopenharmony_ci return -EINVAL; 11748c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->close_session(ctx, session); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_close_session); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ciint tee_client_invoke_func(struct tee_context *ctx, 11798c2ecf20Sopenharmony_ci struct tee_ioctl_invoke_arg *arg, 11808c2ecf20Sopenharmony_ci struct tee_param *param) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->invoke_func) 11838c2ecf20Sopenharmony_ci return -EINVAL; 11848c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->invoke_func(ctx, arg, param); 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_client_invoke_func); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ciint tee_client_cancel_req(struct tee_context *ctx, 11898c2ecf20Sopenharmony_ci struct tee_ioctl_cancel_arg *arg) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci if (!ctx->teedev->desc->ops->cancel_req) 11928c2ecf20Sopenharmony_ci return -EINVAL; 11938c2ecf20Sopenharmony_ci return ctx->teedev->desc->ops->cancel_req(ctx, arg->cancel_id, 11948c2ecf20Sopenharmony_ci arg->session); 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic int tee_client_device_match(struct device *dev, 11988c2ecf20Sopenharmony_ci struct device_driver *drv) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci const struct tee_client_device_id *id_table; 12018c2ecf20Sopenharmony_ci struct tee_client_device *tee_device; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci id_table = to_tee_client_driver(drv)->id_table; 12048c2ecf20Sopenharmony_ci tee_device = to_tee_client_device(dev); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci while (!uuid_is_null(&id_table->uuid)) { 12078c2ecf20Sopenharmony_ci if (uuid_equal(&tee_device->id.uuid, &id_table->uuid)) 12088c2ecf20Sopenharmony_ci return 1; 12098c2ecf20Sopenharmony_ci id_table++; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return 0; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic int tee_client_device_uevent(struct device *dev, 12168c2ecf20Sopenharmony_ci struct kobj_uevent_env *env) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci uuid_t *dev_id = &to_tee_client_device(dev)->id.uuid; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id); 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistruct bus_type tee_bus_type = { 12248c2ecf20Sopenharmony_ci .name = "tee", 12258c2ecf20Sopenharmony_ci .match = tee_client_device_match, 12268c2ecf20Sopenharmony_ci .uevent = tee_client_device_uevent, 12278c2ecf20Sopenharmony_ci}; 12288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tee_bus_type); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic int __init tee_init(void) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci int rc; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci tee_class = class_create(THIS_MODULE, "tee"); 12358c2ecf20Sopenharmony_ci if (IS_ERR(tee_class)) { 12368c2ecf20Sopenharmony_ci pr_err("couldn't create class\n"); 12378c2ecf20Sopenharmony_ci return PTR_ERR(tee_class); 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci rc = alloc_chrdev_region(&tee_devt, 0, TEE_NUM_DEVICES, "tee"); 12418c2ecf20Sopenharmony_ci if (rc) { 12428c2ecf20Sopenharmony_ci pr_err("failed to allocate char dev region\n"); 12438c2ecf20Sopenharmony_ci goto out_unreg_class; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci rc = bus_register(&tee_bus_type); 12478c2ecf20Sopenharmony_ci if (rc) { 12488c2ecf20Sopenharmony_ci pr_err("failed to register tee bus\n"); 12498c2ecf20Sopenharmony_ci goto out_unreg_chrdev; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ciout_unreg_chrdev: 12558c2ecf20Sopenharmony_ci unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); 12568c2ecf20Sopenharmony_ciout_unreg_class: 12578c2ecf20Sopenharmony_ci class_destroy(tee_class); 12588c2ecf20Sopenharmony_ci tee_class = NULL; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return rc; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic void __exit tee_exit(void) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci bus_unregister(&tee_bus_type); 12668c2ecf20Sopenharmony_ci unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); 12678c2ecf20Sopenharmony_ci class_destroy(tee_class); 12688c2ecf20Sopenharmony_ci tee_class = NULL; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cisubsys_initcall(tee_init); 12728c2ecf20Sopenharmony_cimodule_exit(tee_exit); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linaro"); 12758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TEE Driver"); 12768c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 12778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1278