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