Ejemplo n.º 1
0
int tee_session_close_and_destroy(struct tee_session *sess)
{
	int ret;
	struct tee *tee;
	struct tee_context *ctx;

	if (!sess || !sess->ctx || !sess->ctx->tee)
		return -EINVAL;

	ctx = sess->ctx;
	tee = ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sess=%p\n", __func__, sess);

	if (!tee_session_is_opened(sess))
		return -EINVAL;

	ret = tee_session_close_be(sess);

	mutex_lock(&tee->lock);
	tee_dec_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
	list_del(&sess->entry);

	devm_kfree(_DEV(tee), sess);
	tee_context_put(ctx);
	tee_put(tee);
	mutex_unlock(&tee->lock);

	dev_dbg(_DEV(tee), "%s: <\n", __func__);
	return ret;
}
Ejemplo n.º 2
0
static int tee_session_cancel_be(struct tee_session *sess,
				 struct tee_cmd_io *cmd_io)
{
	int ret = -EINVAL;
	struct tee *tee;
	struct tee_cmd cmd;

	BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);

	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
		sess->sessid, cmd_io->cmd);

	ret = _init_tee_cmd(sess, cmd_io, &cmd);
	if (ret)
		goto out;

	ret = tee->ops->cancel(sess, &cmd);

out:
	_release_tee_cmd(sess, &cmd);
	dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
	return ret;
}
Ejemplo n.º 3
0
static long tee_session_ioctl(struct file *filp, unsigned int cmd,
			      unsigned long arg)
{
	struct tee *tee;
	struct tee_session *sess = filp->private_data;
	int ret;

	BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);

	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));

	switch (cmd) {
	case TEE_INVOKE_COMMAND_IOC:
		ret =
		    tee_do_invoke_command(sess,
					  (struct tee_cmd_io __user *)arg);
		break;
	case TEE_REQUEST_CANCELLATION_IOC:
		ret = tee_do_cancel_cmd(sess, (struct tee_cmd_io __user *)arg);
		break;
	default:
		ret = -ENOSYS;
		break;
	}

	dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);

	return ret;
}
Ejemplo n.º 4
0
int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io)
{
	int ret = -EINVAL;
	struct tee *tee;
	struct tee_cmd cmd;

	BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);

	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
		sess->sessid, cmd_io->cmd);

	ret = _init_tee_cmd(sess, cmd_io, &cmd);
	if (ret)
		goto out;

	ret = tee->ops->invoke(sess, &cmd);
	if (!ret)
		_update_client_tee_cmd(sess, cmd_io, &cmd);
	else {
		/* propagate the reason of the error */
		cmd_io->origin = cmd.origin;
		cmd_io->err = cmd.err;
	}

out:
	_release_tee_cmd(sess, &cmd);
	dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
	return ret;
}
Ejemplo n.º 5
0
static struct sg_table *_tee_shm_dma_buf_map_dma_buf(
		struct dma_buf_attachment *attach, enum dma_data_direction dir)
{
	struct tee_shm_attach *tee_shm_attach = attach->priv;
	struct tee_shm *tee_shm = attach->dmabuf->priv;
	struct sg_table *sgt = NULL;
	struct scatterlist *rd, *wr;
	unsigned int i;
	int nents, ret;
	struct tee *tee;

	tee = tee_shm->tee;

	INMSG();

	/* just return current sgt if already requested. */
	if (tee_shm_attach->dir == dir && tee_shm_attach->is_mapped) {
		OUTMSGX(&tee_shm_attach->sgt);
		return &tee_shm_attach->sgt;
	}

	sgt = &tee_shm_attach->sgt;

	ret = sg_alloc_table(sgt, tee_shm->sgt.orig_nents, GFP_KERNEL);
	if (ret) {
		dev_err(_DEV(tee), "failed to alloc sgt.\n");
		return ERR_PTR(-ENOMEM);
	}

	rd = tee_shm->sgt.sgl;
	wr = sgt->sgl;
	for (i = 0; i < sgt->orig_nents; ++i) {
		sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
		rd = sg_next(rd);
		wr = sg_next(wr);
	}

	if (dir != DMA_NONE) {
		nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
		if (!nents) {
			dev_err(_DEV(tee), "failed to map sgl with iommu.\n");
			sg_free_table(sgt);
			sgt = ERR_PTR(-EIO);
			goto err_unlock;
		}
	}

	tee_shm_attach->is_mapped = true;
	tee_shm_attach->dir = dir;
	attach->priv = tee_shm_attach;

err_unlock:
	OUTMSGX(sgt);
	return sgt;
}
Ejemplo n.º 6
0
struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags)
{
	struct tee_shm *shm;
	unsigned long pfn;
	unsigned int nr_pages;
	struct page *page;
	int ret;

	INMSG();

	shm = tee->ops->alloc(tee, size, flags);
	if (IS_ERR_OR_NULL(shm)) {
		dev_err(_DEV(tee),
			"%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n",
			__func__, (int)size, flags, PTR_ERR(shm));
		goto exit;
	}

	shm->tee = tee;

	dev_dbg(_DEV(tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
		 __func__, shm, (void *)shm->paddr, (int)shm->size_req,
		 (int)shm->size_alloc, current->comm, current->pid);

	pfn = shm->paddr >> PAGE_SHIFT;
	page = pfn_to_page(pfn);
	if (IS_ERR_OR_NULL(page)) {
		dev_err(_DEV(tee), "%s: pfn_to_page(%lx) failed\n",
				__func__, pfn);
		tee->ops->free(shm);
		return (struct tee_shm *)page;
	}

	/* Only one page of contiguous physical memory */
	nr_pages = 1;

	ret = sg_alloc_table_from_pages(&shm->sgt, &page,
			nr_pages, 0, nr_pages * PAGE_SIZE, GFP_KERNEL);
	if (IS_ERR_VALUE(ret)) {
		dev_err(_DEV(tee), "%s: sg_alloc_table_from_pages() failed\n",
				__func__);
		tee->ops->free(shm);
		shm = ERR_PTR(ret);
	}
exit:
	OUTMSGX(shm);
	return shm;
}
Ejemplo n.º 7
0
static void _tee_shm_detach_dma_buf(struct dma_buf *dmabuf,
					struct dma_buf_attachment *attach)
{
	struct tee_shm_attach *tee_shm_attach = attach->priv;
	struct sg_table *sgt;
	struct tee_shm *shm;
	struct tee *tee;

	shm = dmabuf->priv;
	tee = shm->tee;

	INMSG();

	if (!tee_shm_attach) {
		OUTMSG(0);
		return;
	}

	sgt = &tee_shm_attach->sgt;

	if (tee_shm_attach->dir != DMA_NONE)
		dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
			tee_shm_attach->dir);

	sg_free_table(sgt);
	devm_kfree(_DEV(tee), tee_shm_attach);
	attach->priv = NULL;
	OUTMSG(0);
}
Ejemplo n.º 8
0
static int _tee_shm_dma_buf_mmap(struct dma_buf *dmabuf,
				struct vm_area_struct *vma)
{
	struct tee_shm *shm = dmabuf->priv;
	size_t size = vma->vm_end - vma->vm_start;
	struct tee *tee;
	int ret;
	pgprot_t prot;
	unsigned long pfn;

	tee = shm->ctx->tee;

	pfn = shm->paddr >> PAGE_SHIFT;

	INMSG();

	if (shm->flags & TEE_SHM_CACHED)
		prot = vma->vm_page_prot;
	else
		prot = pgprot_noncached(vma->vm_page_prot);

	ret =
	    remap_pfn_range(vma, vma->vm_start, pfn, size, prot);
	if (!ret)
		vma->vm_private_data = (void *)shm;

	dev_dbg(_DEV(shm->ctx->tee), "%s: map the shm (p@=%p,s=%dKiB) => %x\n",
		__func__, (void *)shm->paddr, (int)size / 1024,
		(unsigned int)vma->vm_start);

	OUTMSG(ret);
	return ret;
}
Ejemplo n.º 9
0
static int _tee_shm_attach_dma_buf(struct dma_buf *dmabuf,
					struct device *dev,
					struct dma_buf_attachment *attach)
{
	struct tee_shm_attach *tee_shm_attach;
	struct tee_shm *shm;
	struct tee *tee;

	shm = dmabuf->priv;
	tee = shm->tee;

	INMSG();

	tee_shm_attach = devm_kzalloc(_DEV(tee),
			sizeof(*tee_shm_attach), GFP_KERNEL);
	if (!tee_shm_attach) {
		OUTMSG(-ENOMEM);
		return -ENOMEM;
	}

	tee_shm_attach->dir = DMA_NONE;
	attach->priv = tee_shm_attach;

	OUTMSG(0);
	return 0;
}
Ejemplo n.º 10
0
static int tee_session_close_be(struct tee_session *sess)
{
	int ret = -EINVAL;
	struct tee *tee;

	BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);

	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sessid=%08x", __func__, sess->sessid);

	ret = tee->ops->close(sess);
	sess->sessid = 0;

	dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
	return ret;
}
Ejemplo n.º 11
0
static int tee_do_cancel_cmd(struct tee_session *sess,
			     struct tee_cmd_io __user *u_cmd)
{
	int ret = -EINVAL;
	struct tee *tee;
	struct tee_cmd_io k_cmd;
	struct tee_context *ctx;

	BUG_ON(!sess->ctx);
	BUG_ON(!sess->ctx->tee);
	ctx = sess->ctx;
	tee = sess->ctx->tee;

	dev_dbg(sess->ctx->tee->dev, "%s: > sessid=%08x\n", __func__,
		sess->sessid);

	BUG_ON(!sess->sessid);

	if (tee_copy_from_user
	    (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
		dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
		goto exit;
	}

	if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
	    (k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
		dev_err(_DEV(tee),
			"%s: op or/and data parameters are not valid\n",
			__func__);
		goto exit;
	}

	ret = tee_session_cancel_be(sess, &k_cmd);
	if (ret)
		dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);

	tee_put_user(ctx, k_cmd.err, &u_cmd->err);
	tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);

exit:
	dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
	return ret;
}
Ejemplo n.º 12
0
static void *_tee_shm_dma_buf_kmap(struct dma_buf *db, unsigned long pgnum)
{
	struct tee_shm *shm = db->priv;

	dev_dbg(_DEV(shm->ctx->tee), "%s: kmap the shm (p@=%p, v@=%p, s=%zdKiB)\n",
		__func__, (void *)shm->paddr, (void *)shm->kaddr,
		shm->size_alloc / 1024);
	/*
	 * A this stage, a shm allocated by the tee
	 * must be have a kernel address
	 */
	return shm->kaddr;
}
Ejemplo n.º 13
0
static int tee_session_open_be(struct tee_session *sess,
			       struct tee_cmd_io *cmd_io)
{
	int ret = -EINVAL;
	struct tee *tee;
	struct tee_cmd cmd;

	BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);

	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > open a new session", __func__);

	sess->sessid = 0;
	ret = _init_tee_cmd(sess, cmd_io, &cmd);
	if (ret)
		goto out;

	if (cmd.uuid) {
		dev_dbg(_DEV(tee), "%s: UUID=%s\n", __func__,
			_uuid_to_str((TEEC_UUID *) cmd.uuid->kaddr));
	}

	ret = tee->ops->open(sess, &cmd);
	if (ret == 0)
		_update_client_tee_cmd(sess, cmd_io, &cmd);
	else {
		/* propagate the reason of the error */
		cmd_io->origin = cmd.origin;
		cmd_io->err = cmd.err;
	}

out:
	_release_tee_cmd(sess, &cmd);
	dev_dbg(_DEV(tee), "%s: < ret=%d, sessid=%08x", __func__, ret,
		sess->sessid);
	return ret;
}
Ejemplo n.º 14
0
int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io)
{
	int ret;
	struct tee_session *sess;
	struct tee *tee = ctx->tee;

	BUG_ON(cmd_io->fd_sess > 0);

	dev_dbg(_DEV(tee), "%s: >\n", __func__);

	sess = tee_session_create_and_open(ctx, cmd_io);
	if (IS_ERR_OR_NULL(sess)) {
		ret = PTR_ERR(sess);
		dev_dbg(_DEV(tee), "%s: ERROR can't create the session (ret=%d, err=0x%08x, org=%d)\n",
			__func__, ret, cmd_io->err, cmd_io->origin);
		cmd_io->fd_sess = -1;
		goto out;
	}

	/* Retrieve a fd */
	cmd_io->fd_sess = -1;
	ret =
	    anon_inode_getfd("tee_session", &tee_session_fops, sess, O_CLOEXEC);
	if (ret < 0) {
		dev_err(_DEV(tee), "%s: ERROR can't get a fd (ret=%d)\n",
			__func__, ret);
		tee_session_close_and_destroy(sess);
		goto out;
	}
	cmd_io->fd_sess = ret;
	ret = 0;

out:
	dev_dbg(_DEV(tee), "%s: < ret=%d, sess=%p, fd=%d\n", __func__,
		ret, sess, cmd_io->fd_sess);
	return ret;
}
Ejemplo n.º 15
0
void tee_shm_free(struct tee_shm *shm)
{
	struct tee *tee;

	if (IS_ERR_OR_NULL(shm))
		return;
	tee = shm->tee;
	if (tee == NULL)
		pr_warn("invalid call to tee_shm_free(%p): NULL tee\n", shm);
	else if (shm->tee == NULL)
		dev_warn(_DEV(tee), "tee_shm_free(%p): NULL tee\n", shm);
	else {
		sg_free_table(&shm->sgt);
		shm->tee->ops->free(shm);
	}
}
Ejemplo n.º 16
0
static void _tee_shm_dma_buf_release(struct dma_buf *dmabuf)
{
	struct tee_shm *shm = dmabuf->priv;
	struct tee_context *ctx;
	struct tee *tee;

	tee = shm->ctx->tee;

	INMSG();

	ctx = shm->ctx;
	dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
		 __func__, shm, (void *)shm->paddr, (int)shm->size_req,
		 (int)shm->size_alloc, current->comm, current->pid);

	tee_shm_free_io(shm);

	OUTMSG(0);
}
Ejemplo n.º 17
0
struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
						struct tee_cmd_io *cmd_io)
{
	int ret = 0;
	struct tee_session *sess;
	struct tee *tee;

	BUG_ON(!ctx->tee);

	tee = ctx->tee;

	dev_dbg(_DEV(tee), "%s: >\n", __func__);
	ret = tee_get(tee);
	if (ret)
		return ERR_PTR(-EBUSY);

	sess = devm_kzalloc(_DEV(tee), sizeof(struct tee_session), GFP_KERNEL);
	if (!sess) {
		dev_err(_DEV(tee), "%s: tee_session allocation() failed\n",
			__func__);
		tee_put(tee);
		return ERR_PTR(-ENOMEM);
	}

	tee_context_get(ctx);
	sess->ctx = ctx;

	ret = tee_session_open_be(sess, cmd_io);
	mutex_lock(&tee->lock);
	if (ret || !sess->sessid || cmd_io->err) {
		dev_err(_DEV(tee), "%s: ERROR ret=%d (err=0x%08x, org=%d,  sessid=0x%08x)\n",
				__func__, ret, cmd_io->err,
				cmd_io->origin, sess->sessid);
		tee_put(tee);
		tee_context_put(ctx);
		devm_kfree(_DEV(tee), sess);
		mutex_unlock(&tee->lock);
		if (ret)
			return ERR_PTR(ret);
		else
			return NULL;
	}

	tee_inc_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
	list_add_tail(&sess->entry, &ctx->list_sess);
	mutex_unlock(&tee->lock);

	dev_dbg(_DEV(tee), "%s: < sess=%p\n", __func__, sess);
	return sess;
}
Ejemplo n.º 18
0
struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size)
{
	struct tee_shm *shm;

	INMSG();

	shm = tee_shm_alloc(tee, size, TEE_SHM_TEMP | TEE_SHM_FROM_RPC);
	if (IS_ERR_OR_NULL(shm)) {
		dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
			__func__, PTR_ERR(shm));
		goto out;
	}

	mutex_lock(&tee->lock);
	tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
	list_add_tail(&shm->entry, &tee->list_rpc_shm);
	mutex_unlock(&tee->lock);
	shm->ctx = NULL;

out:
	OUTMSGX(shm);
	return shm;
}
Ejemplo n.º 19
0
static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
			 struct tee_cmd *cmd)
{
	int ret = -EINVAL;
	int idx;
	TEEC_Operation op;
	struct tee_data *param = &cmd->param;
	struct tee *tee;
	struct tee_context *ctx;

	BUG_ON(!sess->ctx);
	BUG_ON(!sess->ctx->tee);
	ctx = sess->ctx;
	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);

	memset(cmd, 0, sizeof(struct tee_cmd));

	cmd->cmd = cmd_io->cmd;
	cmd->origin = TEEC_ORIGIN_TEE;
	cmd->err = TEEC_ERROR_BAD_PARAMETERS;
	cmd_io->origin = cmd->origin;
	cmd_io->err = cmd->err;

	if (tee_context_copy_from_client(ctx, &op, cmd_io->op, sizeof(op)))
		goto out;

	cmd->param.type_original = op.paramTypes;

	for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
		uint32_t offset = 0;
		uint32_t size = 0;
		int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx);

		switch (type) {
		case TEEC_NONE:
			break;

		case TEEC_VALUE_INPUT:
		case TEEC_VALUE_OUTPUT:
		case TEEC_VALUE_INOUT:
			param->params[idx].value = op.params[idx].value;
			dev_dbg(_DEV_TEE,
				"%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n",
				__func__, idx, type, param->params[idx].value.a,
				param->params[idx].value.b);
			break;

		case TEEC_MEMREF_TEMP_INPUT:
		case TEEC_MEMREF_TEMP_OUTPUT:
		case TEEC_MEMREF_TEMP_INOUT:
			dev_dbg(_DEV_TEE,
				"> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n",
				idx, type, op.params[idx].tmpref.buffer,
				op.params[idx].tmpref.size);

			param->params[idx].shm =
			    tee_context_create_tmpref_buffer(ctx,
					     op.params[idx].tmpref.size,
					     op.params[idx].tmpref.buffer,
					     type);
			if (IS_ERR_OR_NULL(param->params[idx].shm))
				goto out;

			dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
					(void *)param->params[idx].shm->paddr,
					param->params[idx].shm->size_alloc);
			break;

		case TEEC_MEMREF_PARTIAL_INPUT:
		case TEEC_MEMREF_PARTIAL_OUTPUT:
		case TEEC_MEMREF_PARTIAL_INOUT:
		case TEEC_MEMREF_WHOLE:
			if (tee_copy_from_user(ctx, &param->c_shm[idx],
						op.params[idx].memref.parent,
						sizeof(param->c_shm[idx]))) {
				goto out;
			}

			if (type == TEEC_MEMREF_WHOLE) {
				offset = 0;
				size = param->c_shm[idx].size;
			} else { /* for PARTIAL, check the size */
				offset = op.params[idx].memref.offset;
				size = op.params[idx].memref.size;
				if (param->c_shm[idx].size < size + offset) {
					dev_err(_DEV(tee), "A PARTIAL parameter is bigger than the parent %zd < %d + %d\n",
						param->c_shm[idx].size, size,
						offset);
					goto out;
				}
			}

			dev_dbg(_DEV_TEE, "> param[%d]:type=%d,buffer=%p, offset=%d size=%d\n",
					idx, type, param->c_shm[idx].buffer,
					offset, size);

			type = to_memref_type(param->c_shm[idx].flags);
			if (type == 0)
				goto out;

			param->params[idx].shm = tee_shm_get(ctx,
					&param->c_shm[idx], size, offset);

			if (IS_ERR_OR_NULL(param->params[idx].shm)) {
				param->params[idx].shm =
				    tee_context_create_tmpref_buffer(ctx, size,
				       param->c_shm[idx].buffer + offset, type);

				if (IS_ERR_OR_NULL(param->params[idx].shm))
					goto out;
			}

			dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
				(void *)param->params[idx].shm->paddr,
				param->params[idx].shm->size_req);
			break;

		default:
			BUG_ON(1);
		}

		param->type |= (type << (idx * 4));
	}

	if (cmd_io->uuid != NULL) {
		dev_dbg(_DEV_TEE, "%s: copy UUID value...\n", __func__);
		cmd->uuid = tee_context_alloc_shm_tmp(sess->ctx,
			sizeof(*cmd_io->uuid), cmd_io->uuid, TEEC_MEM_INPUT);
		if (IS_ERR_OR_NULL(cmd->uuid)) {
			ret = -EINVAL;
			goto out;
		}
	}

	ret = 0;

out:
	if (ret)
		_release_tee_cmd(sess, cmd);

	dev_dbg(_DEV_TEE, "%s: < ret=%d\n", __func__, ret);
	return ret;
}
Ejemplo n.º 20
0
static int _copy_op(struct tee_session *sess, struct tee_cmd_io *cmd_io,
		    struct tee_cmd *cmd)
{
	int res = -EINVAL;
	int idx;
	TEEC_Operation op;
	struct tee_data *param = &cmd->param;
	struct tee *tee;
	struct tee_context *ctx;

	BUG_ON(!sess->ctx);
	BUG_ON(!sess->ctx->tee);
	ctx = sess->ctx;
	tee = sess->ctx->tee;

	dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);

	if (tee_context_copy_from_client
	    (sess->ctx, &op, cmd_io->op, sizeof(TEEC_Operation)))
		goto out;

	cmd->param.type_original = op.paramTypes;

	if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE,
			TEEC_NONE, TEEC_NONE, TEEC_NONE)) {
		param->type = cmd->param.type_original;
		res = 0;
		goto out;
	}

	for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
		int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx);
		switch (type) {
		case TEEC_NONE:
			break;
		case TEEC_VALUE_INPUT:
		case TEEC_VALUE_OUTPUT:
		case TEEC_VALUE_INOUT:
			param->params[idx].value = op.params[idx].value;
			dev_dbg(_DEV_TEE,
				"%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n",
				__func__, idx, type, param->params[idx].value.a,
				param->params[idx].value.b);
			break;

		case TEEC_MEMREF_TEMP_INPUT:
		case TEEC_MEMREF_TEMP_OUTPUT:
		case TEEC_MEMREF_TEMP_INOUT:
			dev_dbg(_DEV_TEE,
				"> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n",
				idx, type, op.params[idx].tmpref.buffer,
				op.params[idx].tmpref.size);
			param->params[idx].shm =
			    tee_context_create_tmpref_buffer(sess->ctx,
							     op.params[idx].
							     tmpref.size,
							     op.params[idx].
							     tmpref.buffer,
							     type);
			if (IS_ERR_OR_NULL(param->params[idx].shm))
				return -ENOMEM;
			dev_dbg(_DEV_TEE, "< %d %p:%zu (TMPREF)\n",
				idx, (void *)param->params[idx].shm->paddr,
				param->params[idx].shm->size_req);
			break;

		case TEEC_MEMREF_WHOLE:
			if (sess->ctx->usr_client) {
				if (tee_copy_from_user(ctx, &param->c_shm[idx],
						       op.params[idx].memref.
						       parent,
						       sizeof
						       (TEEC_SharedMemory))) {
					res = TEEC_ERROR_BAD_PARAMETERS;
					goto out;
				}
			} else
				param->c_shm[idx] =
				    *op.params[idx].memref.parent;

			BUG_ON(!param->c_shm[idx].buffer);
			BUG_ON(!param->c_shm[idx].size);

			if (param->c_shm[idx].flags == TEEC_MEM_INPUT)
				type = TEEC_MEMREF_TEMP_INPUT;
			else if (param->c_shm[idx].flags == TEEC_MEM_OUTPUT)
				type = TEEC_MEMREF_TEMP_OUTPUT;
			else if (param->c_shm[idx].flags ==
				 (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))
				type = TEEC_MEMREF_TEMP_INOUT;

			if (check_shm
			    (tee, (struct tee_shm_io *)&param->c_shm[idx])) {
				dev_dbg(_DEV_TEE,
					"> param[%d]:type=%d,buffer=%p, s=%zu (WHOLE)\n",
					idx, type, op.params[idx].tmpref.buffer,
					param->c_shm[idx].size);

				param->params[idx].shm =
				    tee_context_create_tmpref_buffer(sess->ctx,
						param->c_shm[idx].size,
						param->c_shm[idx].buffer,
						type);
				if (IS_ERR_OR_NULL(param->params[idx].shm))
					return -ENOMEM;

				dev_dbg(_DEV_TEE, "< %d %p:%zu (WHOLE)\n",
					idx,
					(void *)param->params[idx].shm->paddr,
					param->params[idx].shm->size_req);
			} else {
				struct tee_shm *shm;
				/* The buffer is already allocated by the tee
				 * get a reference on it
				 */
				shm =
				    tee_shm_get(sess->ctx,
						(struct tee_shm_io *)&param->
						c_shm[idx]);
				if (!shm)
					/* not allocated by us,
					 * is it a use case ? */
					BUG_ON(1);

				param->params[idx].shm =
				    devm_kzalloc(tee->dev,
						 sizeof(struct tee_shm),
						 GFP_KERNEL);
				if (!param->params[idx].shm)
					return -ENOMEM;

				param->params[idx].shm->parent = shm;
				param->params[idx].shm->ctx = sess->ctx;
				param->params[idx].shm->tee = tee;
				param->params[idx].shm->dev = tee->dev;
				param->params[idx].shm->size_req =
				    param->c_shm[idx].size;
				param->params[idx].shm->size_alloc = 0;
				param->params[idx].shm->kaddr = shm->kaddr;
				param->params[idx].shm->paddr = shm->paddr;
				param->params[idx].shm->flags =
				    shm->flags | TEE_SHM_PARENT;
			}
			break;

		case TEEC_MEMREF_PARTIAL_INPUT:
		case TEEC_MEMREF_PARTIAL_OUTPUT:
		case TEEC_MEMREF_PARTIAL_INOUT:{
				uint32_t offset = op.params[idx].memref.offset;
				uint32_t size = op.params[idx].memref.size;

				if (sess->ctx->usr_client) {
					if (tee_copy_from_user
					    (ctx, &param->c_shm[idx],
					     op.params[idx].memref.parent,
					     sizeof(TEEC_SharedMemory))) {
						res = TEEC_ERROR_BAD_PARAMETERS;
						goto out;
					}
				} else
					param->c_shm[idx] =
					    *op.params[idx].memref.parent;

				dev_dbg(_DEV_TEE,
					"> param[%d]:type=%d,buffer=%p, offset=%x s=%d (PARTIAL)\n",
					idx, type, param->c_shm[idx].buffer,
					offset, size);

				if (type == TEEC_MEMREF_PARTIAL_INPUT)
					type = TEEC_MEMREF_TEMP_INPUT;
				else if (type == TEEC_MEMREF_PARTIAL_OUTPUT)
					type = TEEC_MEMREF_TEMP_OUTPUT;
				else if (type == TEEC_MEMREF_PARTIAL_INOUT)
					type = TEEC_MEMREF_TEMP_INOUT;


				if (check_shm
				    (tee,
				     (struct tee_shm_io *)&param->c_shm[idx])) {

					param->params[idx].shm =
					    tee_context_create_tmpref_buffer
					    (sess->ctx, size,
					     param->c_shm[idx].buffer + offset,
					     type);
					if (IS_ERR_OR_NULL(
							param->params[idx].shm))
						return -ENOMEM;

				} else {
					struct tee_shm *shm;
					/* The buffer is already allocated by
					 * the tee
					 * get a reference on it
					 */

					shm =
					    tee_shm_get(sess->ctx,
							(struct tee_shm_io *)
							&param->c_shm[idx]);
					if (!shm)
						/* not allocated by us,
						 * is it a use case ? */
						BUG_ON(1);

					param->params[idx].shm =
					    devm_kzalloc(tee->dev,
							 sizeof(struct tee_shm),
							 GFP_KERNEL);
					if (!param->params[idx].shm)
						return -ENOMEM;

					param->params[idx].shm->parent = shm;
					param->params[idx].shm->ctx = sess->ctx;
					param->params[idx].shm->tee = tee;
					param->params[idx].shm->dev = tee->dev;
					param->params[idx].shm->size_req = size;
					param->params[idx].shm->size_alloc = 0;
					param->params[idx].shm->kaddr =
					    shm->kaddr + offset;
					param->params[idx].shm->paddr =
					    shm->paddr + offset;
					param->params[idx].shm->flags =
					    shm->flags | TEE_SHM_PARENT;


				}
				dev_dbg(_DEV_TEE, "< %d %p:%zu (PARTIAL)\n",
					idx,
					(void *)param->params[idx].shm->paddr,
					param->params[idx].shm->size_req);
				break;
			}
		default:
			BUG_ON(1);
		}

		param->type |= (type << (idx * 4));
	}
	res = 0;

out:
	dev_dbg(_DEV(tee), "%s: < fd=%d\n", __func__, res);
	return res;
}