// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2015-2016, Linaro Limited */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/cdev.h>#include <linux/cred.h>#include <linux/fs.h>#include <linux/idr.h>#include <linux/module.h>#include <linux/overflow.h>#include <linux/slab.h>#include <linux/tee_core.h>#include <linux/uaccess.h>#include <crypto/sha1.h>#include "tee_private.h" #define TEE_NUM_DEVICES 32 #define TEE_IOCTL_PARAM_SIZE(x) (size_mul(sizeof(struct tee_param), (x))) #define TEE_UUID_NS_NAME_SIZE 128 /* * TEE Client UUID name space identifier (UUIDv4) * * Value here is random UUID that is allocated as name space identifier for * forming Client UUID's for TEE environment using UUIDv5 scheme. */static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683, 0xa1, 0xb8, 0xec, 0x4b, 0xc0, 0x8e, 0x01, 0xb6); /* * Unprivileged devices in the lower half range and privileged devices in * the upper half range. */static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES);static DEFINE_SPINLOCK(driver_lock); static const struct class tee_class;static dev_t tee_devt; struct tee_context *teedev_open(struct tee_device *teedev){ int rc; struct tee_context *ctx; if (!tee_device_get(teedev)) return ERR_PTR(-EINVAL); ctx = kzalloc_obj(*ctx); if (!ctx) { rc = -ENOMEM; goto err; } kref_init(&ctx->refcount); ctx->teedev = teedev; rc = teedev->desc->ops->open(ctx); if (rc) goto err; return ctx;err: kfree(ctx); tee_device_put(teedev); return ERR_PTR(rc); }EXPORT_SYMBOL_GPL(teedev_open); void teedev_ctx_get(struct tee_context *ctx){ if (ctx->releasing) return; kref_get(&ctx->refcount);}EXPORT_SYMBOL_GPL(teedev_ctx_get); static void teedev_ctx_release(struct kref *ref){ struct tee_context *ctx = container_of(ref, struct tee_context, refcount); ctx->releasing = true; ctx->teedev->desc->ops->release(ctx); kfree(ctx);} void teedev_ctx_put(struct tee_context *ctx){ if (ctx->releasing) return; kref_put(&ctx->refcount, teedev_ctx_release);}EXPORT_SYMBOL_GPL(teedev_ctx_put); void teedev_close_context(struct tee_context *ctx){ struct tee_device *teedev = ctx->teedev; if (teedev->desc->ops->close_context) teedev->desc->ops->close_context(ctx); teedev_ctx_put(ctx); tee_device_put(teedev);}EXPORT_SYMBOL_GPL(teedev_close_context); static int tee_open(struct inode *inode, struct file *filp){ struct tee_context *ctx; ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev)); if (IS_ERR(ctx)) return PTR_ERR(ctx); /* * Default user-space behaviour is to wait for tee-supplicant * if not present for any requests in this context. */ ctx->supp_nowait = false; filp->private_data = ctx; return 0;} static int tee_release(struct inode *inode, struct file *filp){ teedev_close_context(filp->private_data); return 0;} /** * uuid_v5() - Calculate UUIDv5 * @uuid: Resulting UUID * @ns: Name space ID for UUIDv5 function * @name: Name for UUIDv5 function * @size: Size of name * * UUIDv5 is specific in RFC 4122. * * This implements section (for SHA-1): * 4.3. Algorithm for Creating a Name-Based UUID */static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, size_t size){ unsigned char hash[SHA1_DIGEST_SIZE]; struct sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, (const u8 *)ns, sizeof(*ns)); sha1_update(&ctx, (const u8 *)name, size); sha1_final(&ctx, hash); memcpy(uuid->b, hash, UUID_SIZE); /* Tag for version 5 */ uuid->b[6] = (hash[6] & 0x0F) | 0x50; uuid->b[8] = (hash[8] & 0x3F) | 0x80;} int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]){ gid_t ns_grp = (gid_t)-1; kgid_t grp = INVALID_GID; char *name = NULL; int name_len; int rc = 0; if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { /* Nil UUID to be passed to TEE environment */ uuid_copy(uuid, &uuid_null); return 0; } /* * In Linux environment client UUID is based on UUIDv5. * * Determine client UUID with following semantics for 'name': * * For TEEC_LOGIN_USER: * uid=<uid> * * For TEEC_LOGIN_GROUP: * gid=<gid> * */ name = kzalloc(TEE_UUID_NS_NAME_SIZE, GFP_KERNEL); if (!name) return -ENOMEM; switch (connection_method) { case TEE_IOCTL_LOGIN_USER: name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "uid=%x", current_euid().val); if (name_len >= TEE_UUID_NS_NAME_SIZE) { rc = -E2BIG; goto out_free_name; } break; case TEE_IOCTL_LOGIN_GROUP: memcpy(&ns_grp, connection_data, sizeof(gid_t)); grp = make_kgid(current_user_ns(), ns_grp); if (!gid_valid(grp) || !in_egroup_p(grp)) { rc = -EPERM; goto out_free_name; } name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "gid=%x", grp.val); if (name_len >= TEE_UUID_NS_NAME_SIZE) { rc = -E2BIG; goto out_free_name; } break; default: rc = -EINVAL; goto out_free_name; } uuid_v5(uuid, &tee_client_uuid_ns, name, name_len);out_free_name: kfree(name); return rc;}EXPORT_SYMBOL_GPL(tee_session_calc_client_uuid); static int tee_ioctl_version(struct tee_context *ctx, struct tee_ioctl_version_data __user *uvers){ struct tee_ioctl_version_data vers; ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; if (copy_to_user(uvers, &vers, sizeof(vers))) return -EFAULT; return 0;} static int tee_ioctl_shm_alloc(struct tee_context *ctx, struct tee_ioctl_shm_alloc_data __user *udata){ long ret; struct tee_ioctl_shm_alloc_data data; struct tee_shm *shm; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_alloc_user_buf(ctx, data.size); if (IS_ERR(shm)) return PTR_ERR(shm); data.id = shm->id; data.size = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret;} static inttee_ioctl_shm_register(struct tee_context *ctx, struct tee_ioctl_shm_register_data __user *udata){ long ret; struct tee_ioctl_shm_register_data data; struct tee_shm *shm; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_register_user_buf(ctx, data.addr, data.length); if (IS_ERR(shm)) return PTR_ERR(shm); data.id = shm->id; data.length = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret;} static inttee_ioctl_shm_register_fd(struct tee_context *ctx, struct tee_ioctl_shm_register_fd_data __user *udata){ struct tee_ioctl_shm_register_fd_data data; struct tee_shm *shm; long ret; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_register_fd(ctx, data.fd); if (IS_ERR(shm)) return -EINVAL; data.id = shm->id; data.flags = shm->flags; data.size = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret;} static int param_from_user_memref(struct tee_context *ctx, struct tee_param_memref *memref, struct tee_ioctl_param *ip){ struct tee_shm *shm; size_t offs = 0; /* * If a NULL pointer is passed to a TA in the TEE, * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL * indicating a NULL memory reference. */ if (ip->c != TEE_MEMREF_NULL) { /* * If we fail to get a pointer to a shared * memory object (and increase the ref count) * from an identifier we return an error. All * pointers that has been added in params have * an increased ref count. It's the callers * responibility to do tee_shm_put() on all * resolved pointers. */ shm = tee_shm_get_from_id(ctx, ip->c); if (IS_ERR(shm)) return PTR_ERR(shm); /* * Ensure offset + size does not overflow * offset and does not overflow the size of * the referred shared memory object. */ if ((ip->a + ip->b) < ip->a || (ip->a + ip->b) > shm->size) { tee_shm_put(shm); return -EINVAL; } if (shm->flags & TEE_SHM_DMA_BUF) { struct tee_shm_dmabuf_ref *ref; ref = container_of(shm, struct tee_shm_dmabuf_ref, shm); if (ref->parent_shm) { /* * The shm already has one reference to * ref->parent_shm so we are clear of 0. * We're getting another reference since * this shm will be used in the parameter * list instead of the shm we got with * tee_shm_get_from_id() above. */ refcount_inc(&ref->parent_shm->refcount); tee_shm_put(shm); shm = ref->parent_shm; offs = ref->offset; } } } else if (ctx->cap_memref_null) { /* Pass NULL pointer to OP-TEE */ shm = NULL; } else { return -EINVAL; } memref->shm_offs = ip->a + offs; memref->size = ip->b; memref->shm = shm; return 0;} static int params_from_user(struct tee_context *ctx, struct tee_param *params, size_t num_params, struct tee_ioctl_param __user *uparams){ size_t n; for (n = 0; n < num_params; n++) { struct tee_ioctl_param ip; int rc; if (copy_from_user(&ip, uparams + n, sizeof(ip))) return -EFAULT; /* All unused attribute bits has to be zero */ if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) return -EINVAL; params[n].attr = ip.attr; switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: break; case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: params[n].u.value.a = ip.a; params[n].u.value.b = ip.b; params[n].u.value.c = ip.c; break; case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: params[n].u.ubuf.uaddr = u64_to_user_ptr(ip.a); params[n].u.ubuf.size = ip.b; if (!access_ok(params[n].u.ubuf.uaddr, params[n].u.ubuf.size)) return -EFAULT; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: params[n].u.objref.id = ip.a; params[n].u.objref.flags = ip.b; break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: rc = param_from_user_memref(ctx, ¶ms[n].u.memref, &ip); if (rc) return rc; break; default: /* Unknown attribute */ return -EINVAL; } } return 0;} static int params_to_user(struct tee_ioctl_param __user *uparams, size_t num_params, struct tee_param *params){ size_t n; for (n = 0; n < num_params; n++) { struct tee_ioctl_param __user *up = uparams + n; struct tee_param *p = params + n;Lines 1–500 of 1579. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2016, Linaro Limited */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/cdev.h> #include <linux/cred.h> #include <linux/fs.h> #include <linux/idr.h> #include <linux/module.h> #include <linux/overflow.h> #include <linux/slab.h> #include <linux/tee_core.h> #include <linux/uaccess.h> #include <crypto/sha1.h> #include "tee_private.h" #define TEE_NUM_DEVICES 32 #define TEE_IOCTL_PARAM_SIZE(x) (size_mul(sizeof(struct tee_param), (x))) #define TEE_UUID_NS_NAME_SIZE 128 /* * TEE Client UUID name space identifier (UUIDv4) * * Value here is random UUID that is allocated as name space identifier for * forming Client UUID's for TEE environment using UUIDv5 scheme. */ static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683, 0xa1, 0xb8, 0xec, 0x4b, 0xc0, 0x8e, 0x01, 0xb6); /* * Unprivileged devices in the lower half range and privileged devices in * the upper half range. */ static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); static DEFINE_SPINLOCK(driver_lock); static const struct class tee_class; static dev_t tee_devt; struct tee_context *teedev_open(struct tee_device *teedev) { int rc; struct tee_context *ctx; if (!tee_device_get(teedev)) return ERR_PTR(-EINVAL); ctx = kzalloc_obj(*ctx); if (!ctx) { rc = -ENOMEM; goto err; } kref_init(&ctx->refcount); ctx->teedev = teedev; rc = teedev->desc->ops->open(ctx); if (rc) goto err; return ctx; err: kfree(ctx); tee_device_put(teedev); return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(teedev_open); void teedev_ctx_get(struct tee_context *ctx) { if (ctx->releasing) return; kref_get(&ctx->refcount); } EXPORT_SYMBOL_GPL(teedev_ctx_get); static void teedev_ctx_release(struct kref *ref) { struct tee_context *ctx = container_of(ref, struct tee_context, refcount); ctx->releasing = true; ctx->teedev->desc->ops->release(ctx); kfree(ctx); } void teedev_ctx_put(struct tee_context *ctx) { if (ctx->releasing) return; kref_put(&ctx->refcount, teedev_ctx_release); } EXPORT_SYMBOL_GPL(teedev_ctx_put); void teedev_close_context(struct tee_context *ctx) { struct tee_device *teedev = ctx->teedev; if (teedev->desc->ops->close_context) teedev->desc->ops->close_context(ctx); teedev_ctx_put(ctx); tee_device_put(teedev); } EXPORT_SYMBOL_GPL(teedev_close_context); static int tee_open(struct inode *inode, struct file *filp) { struct tee_context *ctx; ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev)); if (IS_ERR(ctx)) return PTR_ERR(ctx); /* * Default user-space behaviour is to wait for tee-supplicant * if not present for any requests in this context. */ ctx->supp_nowait = false; filp->private_data = ctx; return 0; } static int tee_release(struct inode *inode, struct file *filp) { teedev_close_context(filp->private_data); return 0; } /** * uuid_v5() - Calculate UUIDv5 * @uuid: Resulting UUID * @ns: Name space ID for UUIDv5 function * @name: Name for UUIDv5 function * @size: Size of name * * UUIDv5 is specific in RFC 4122. * * This implements section (for SHA-1): * 4.3. Algorithm for Creating a Name-Based UUID */ static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, size_t size) { unsigned char hash[SHA1_DIGEST_SIZE]; struct sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, (const u8 *)ns, sizeof(*ns)); sha1_update(&ctx, (const u8 *)name, size); sha1_final(&ctx, hash); memcpy(uuid->b, hash, UUID_SIZE); /* Tag for version 5 */ uuid->b[6] = (hash[6] & 0x0F) | 0x50; uuid->b[8] = (hash[8] & 0x3F) | 0x80; } int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]) { gid_t ns_grp = (gid_t)-1; kgid_t grp = INVALID_GID; char *name = NULL; int name_len; int rc = 0; if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { /* Nil UUID to be passed to TEE environment */ uuid_copy(uuid, &uuid_null); return 0; } /* * In Linux environment client UUID is based on UUIDv5. * * Determine client UUID with following semantics for 'name': * * For TEEC_LOGIN_USER: * uid=<uid> * * For TEEC_LOGIN_GROUP: * gid=<gid> * */ name = kzalloc(TEE_UUID_NS_NAME_SIZE, GFP_KERNEL); if (!name) return -ENOMEM; switch (connection_method) { case TEE_IOCTL_LOGIN_USER: name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "uid=%x", current_euid().val); if (name_len >= TEE_UUID_NS_NAME_SIZE) { rc = -E2BIG; goto out_free_name; } break; case TEE_IOCTL_LOGIN_GROUP: memcpy(&ns_grp, connection_data, sizeof(gid_t)); grp = make_kgid(current_user_ns(), ns_grp); if (!gid_valid(grp) || !in_egroup_p(grp)) { rc = -EPERM; goto out_free_name; } name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "gid=%x", grp.val); if (name_len >= TEE_UUID_NS_NAME_SIZE) { rc = -E2BIG; goto out_free_name; } break; default: rc = -EINVAL; goto out_free_name; } uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); out_free_name: kfree(name); return rc; } EXPORT_SYMBOL_GPL(tee_session_calc_client_uuid); static int tee_ioctl_version(struct tee_context *ctx, struct tee_ioctl_version_data __user *uvers) { struct tee_ioctl_version_data vers; ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; if (copy_to_user(uvers, &vers, sizeof(vers))) return -EFAULT; return 0; } static int tee_ioctl_shm_alloc(struct tee_context *ctx, struct tee_ioctl_shm_alloc_data __user *udata) { long ret; struct tee_ioctl_shm_alloc_data data; struct tee_shm *shm; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_alloc_user_buf(ctx, data.size); if (IS_ERR(shm)) return PTR_ERR(shm); data.id = shm->id; data.size = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret; } static int tee_ioctl_shm_register(struct tee_context *ctx, struct tee_ioctl_shm_register_data __user *udata) { long ret; struct tee_ioctl_shm_register_data data; struct tee_shm *shm; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_register_user_buf(ctx, data.addr, data.length); if (IS_ERR(shm)) return PTR_ERR(shm); data.id = shm->id; data.length = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret; } static int tee_ioctl_shm_register_fd(struct tee_context *ctx, struct tee_ioctl_shm_register_fd_data __user *udata) { struct tee_ioctl_shm_register_fd_data data; struct tee_shm *shm; long ret; if (copy_from_user(&data, udata, sizeof(data))) return -EFAULT; /* Currently no input flags are supported */ if (data.flags) return -EINVAL; shm = tee_shm_register_fd(ctx, data.fd); if (IS_ERR(shm)) return -EINVAL; data.id = shm->id; data.flags = shm->flags; data.size = shm->size; if (copy_to_user(udata, &data, sizeof(data))) ret = -EFAULT; else ret = tee_shm_get_fd(shm); /* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */ tee_shm_put(shm); return ret; } static int param_from_user_memref(struct tee_context *ctx, struct tee_param_memref *memref, struct tee_ioctl_param *ip) { struct tee_shm *shm; size_t offs = 0; /* * If a NULL pointer is passed to a TA in the TEE, * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL * indicating a NULL memory reference. */ if (ip->c != TEE_MEMREF_NULL) { /* * If we fail to get a pointer to a shared * memory object (and increase the ref count) * from an identifier we return an error. All * pointers that has been added in params have * an increased ref count. It's the callers * responibility to do tee_shm_put() on all * resolved pointers. */ shm = tee_shm_get_from_id(ctx, ip->c); if (IS_ERR(shm)) return PTR_ERR(shm); /* * Ensure offset + size does not overflow * offset and does not overflow the size of * the referred shared memory object. */ if ((ip->a + ip->b) < ip->a || (ip->a + ip->b) > shm->size) { tee_shm_put(shm); return -EINVAL; } if (shm->flags & TEE_SHM_DMA_BUF) { struct tee_shm_dmabuf_ref *ref; ref = container_of(shm, struct tee_shm_dmabuf_ref, shm); if (ref->parent_shm) { /* * The shm already has one reference to * ref->parent_shm so we are clear of 0. * We're getting another reference since * this shm will be used in the parameter * list instead of the shm we got with * tee_shm_get_from_id() above. */ refcount_inc(&ref->parent_shm->refcount); tee_shm_put(shm); shm = ref->parent_shm; offs = ref->offset; } } } else if (ctx->cap_memref_null) { /* Pass NULL pointer to OP-TEE */ shm = NULL; } else { return -EINVAL; } memref->shm_offs = ip->a + offs; memref->size = ip->b; memref->shm = shm; return 0; } static int params_from_user(struct tee_context *ctx, struct tee_param *params, size_t num_params, struct tee_ioctl_param __user *uparams) { size_t n; for (n = 0; n < num_params; n++) { struct tee_ioctl_param ip; int rc; if (copy_from_user(&ip, uparams + n, sizeof(ip))) return -EFAULT; /* All unused attribute bits has to be zero */ if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) return -EINVAL; params[n].attr = ip.attr; switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: break; case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: params[n].u.value.a = ip.a; params[n].u.value.b = ip.b; params[n].u.value.c = ip.c; break; case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT: params[n].u.ubuf.uaddr = u64_to_user_ptr(ip.a); params[n].u.ubuf.size = ip.b; if (!access_ok(params[n].u.ubuf.uaddr, params[n].u.ubuf.size)) return -EFAULT; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: params[n].u.objref.id = ip.a; params[n].u.objref.flags = ip.b; break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: rc = param_from_user_memref(ctx, ¶ms[n].u.memref, &ip); if (rc) return rc; break; default: /* Unknown attribute */ return -EINVAL; } } return 0; } static int params_to_user(struct tee_ioctl_param __user *uparams, size_t num_params, struct tee_param *params) { size_t n; for (n = 0; n < num_params; n++) { struct tee_ioctl_param __user *up = uparams + n; struct tee_param *p = params + n;