823 lines · 22.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/slab.h>#include <linux/tee.h>#include <linux/platform_device.h>#include <linux/xarray.h> #include "qcomtee.h" static int find_qtee_object(struct qcomtee_object **object, unsigned long id,			    struct qcomtee_context_data *ctxdata){	int err = 0; 	guard(rcu)();	/* Object release is RCU protected. */	*object = idr_find(&ctxdata->qtee_objects_idr, id);	if (!qcomtee_object_get(*object))		err = -EINVAL; 	return err;} static void del_qtee_object(unsigned long id,			    struct qcomtee_context_data *ctxdata){	struct qcomtee_object *object; 	scoped_guard(mutex, &ctxdata->qtee_lock)		object = idr_remove(&ctxdata->qtee_objects_idr, id); 	qcomtee_object_put(object);} /** * qcomtee_context_add_qtee_object() - Add a QTEE object to the context. * @param: TEE parameter representing @object. * @object: QTEE object. * @ctx: context to add the object. * * It assumes @object is %QCOMTEE_OBJECT_TYPE_TEE and the caller has already * issued qcomtee_object_get() for @object. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_context_add_qtee_object(struct tee_param *param,				    struct qcomtee_object *object,				    struct tee_context *ctx){	int ret;	struct qcomtee_context_data *ctxdata = ctx->data; 	scoped_guard(mutex, &ctxdata->qtee_lock)		ret = idr_alloc(&ctxdata->qtee_objects_idr, object, 0, 0,				GFP_KERNEL);	if (ret < 0)		return ret; 	param->u.objref.id = ret;	/* QTEE Object: QCOMTEE_OBJREF_FLAG_TEE set. */	param->u.objref.flags = QCOMTEE_OBJREF_FLAG_TEE; 	return 0;} /* Retrieve the QTEE object added with qcomtee_context_add_qtee_object(). */int qcomtee_context_find_qtee_object(struct qcomtee_object **object,				     struct tee_param *param,				     struct tee_context *ctx){	struct qcomtee_context_data *ctxdata = ctx->data; 	return find_qtee_object(object, param->u.objref.id, ctxdata);} /** * qcomtee_context_del_qtee_object() - Delete a QTEE object from the context. * @param: TEE parameter representing @object. * @ctx: context for deleting the object. * * The @param has been initialized by qcomtee_context_add_qtee_object(). */void qcomtee_context_del_qtee_object(struct tee_param *param,				     struct tee_context *ctx){	struct qcomtee_context_data *ctxdata = ctx->data;	/* 'qtee_objects_idr' stores QTEE objects only. */	if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE)		del_qtee_object(param->u.objref.id, ctxdata);} /** * qcomtee_objref_to_arg() - Convert OBJREF parameter to QTEE argument. * @arg: QTEE argument. * @param: TEE parameter. * @ctx: context in which the conversion should happen. * * It assumes @param is an OBJREF. * It does not set @arg.type; the caller should initialize it to a correct * &enum qcomtee_arg_type value. It gets the object's refcount in @arg; * the caller should manage to put it afterward. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param,			  struct tee_context *ctx){	int err = -EINVAL; 	arg->o = NULL_QCOMTEE_OBJECT;	/* param is a NULL object: */	if (param->u.objref.id == TEE_OBJREF_NULL)		return 0; 	/* param is a callback object: */	if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_USER)		err =  qcomtee_user_param_to_object(&arg->o, param, ctx);	/* param is a QTEE object: */	else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE)		err = qcomtee_context_find_qtee_object(&arg->o, param, ctx);	/* param is a memory object: */	else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_MEM)		err = qcomtee_memobj_param_to_object(&arg->o, param, ctx); 	/*	 * For callback objects, call qcomtee_object_get() to keep a temporary	 * copy for the driver, as these objects are released asynchronously	 * and may disappear even before returning from QTEE.	 *	 *  - For direct object invocations, the matching put is called in	 *    qcomtee_object_invoke() when parsing the QTEE response.	 *  - For callback responses, put is called in qcomtee_user_object_notify()	 *    after QTEE has received its copies.	 */ 	if (!err && (typeof_qcomtee_object(arg->o) == QCOMTEE_OBJECT_TYPE_CB))		qcomtee_object_get(arg->o); 	return err;} /** * qcomtee_objref_from_arg() - Convert QTEE argument to OBJREF param. * @param: TEE parameter. * @arg: QTEE argument. * @ctx: context in which the conversion should happen. * * It assumes @arg is of %QCOMTEE_ARG_TYPE_IO or %QCOMTEE_ARG_TYPE_OO. * It does not set @param.attr; the caller should initialize it to a * correct type. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg,			    struct tee_context *ctx){	struct qcomtee_object *object = arg->o; 	switch (typeof_qcomtee_object(object)) {	case QCOMTEE_OBJECT_TYPE_NULL:		param->u.objref.id = TEE_OBJREF_NULL; 		return 0;	case QCOMTEE_OBJECT_TYPE_CB:		/* object is a callback object: */		if (is_qcomtee_user_object(object))			return qcomtee_user_param_from_object(param, object,							      ctx);		/* object is a memory object: */		else if (is_qcomtee_memobj_object(object))			return qcomtee_memobj_param_from_object(param, object,							       ctx); 		break;	case QCOMTEE_OBJECT_TYPE_TEE:		return qcomtee_context_add_qtee_object(param, object, ctx); 	case QCOMTEE_OBJECT_TYPE_ROOT:	default:		break;	} 	return -EINVAL;} /** * qcomtee_params_to_args() - Convert TEE parameters to QTEE arguments. * @u: QTEE arguments. * @params: TEE parameters. * @num_params: number of elements in the parameter array. * @ctx: context in which the conversion should happen. * * It assumes @u has at least @num_params + 1 entries and has been initialized * with %QCOMTEE_ARG_TYPE_INV as &struct qcomtee_arg.type. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_params_to_args(struct qcomtee_arg *u,				  struct tee_param *params, int num_params,				  struct tee_context *ctx){	int i; 	for (i = 0; i < num_params; i++) {		switch (params[i].attr) {		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:			u[i].flags = QCOMTEE_ARG_FLAGS_UADDR;			u[i].b.uaddr = params[i].u.ubuf.uaddr;			u[i].b.size = params[i].u.ubuf.size; 			if (params[i].attr ==			    TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT)				u[i].type = QCOMTEE_ARG_TYPE_IB;			else /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */				u[i].type = QCOMTEE_ARG_TYPE_OB; 			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:			u[i].type = QCOMTEE_ARG_TYPE_IO;			if (qcomtee_objref_to_arg(&u[i], &params[i], ctx))				goto out_failed; 			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:			u[i].type = QCOMTEE_ARG_TYPE_OO;			u[i].o = NULL_QCOMTEE_OBJECT;			break;		default:			goto out_failed;		}	} 	return 0; out_failed:	/* Undo qcomtee_objref_to_arg(). */	for (i--; i >= 0; i--) {		if (u[i].type != QCOMTEE_ARG_TYPE_IO)			continue; 		qcomtee_user_object_set_notify(u[i].o, false);		/* See docs for qcomtee_objref_to_arg() for double put. */		if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB)			qcomtee_object_put(u[i].o); 		qcomtee_object_put(u[i].o);	} 	return -EINVAL;} /** * qcomtee_params_from_args() - Convert QTEE arguments to TEE parameters. * @params: TEE parameters. * @u: QTEE arguments. * @num_params: number of elements in the parameter array. * @ctx: context in which the conversion should happen. * * @u should have already been initialized by qcomtee_params_to_args(). * This also represents the end of a QTEE invocation that started with * qcomtee_params_to_args() by releasing %QCOMTEE_ARG_TYPE_IO objects. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_params_from_args(struct tee_param *params,				    struct qcomtee_arg *u, int num_params,				    struct tee_context *ctx){	int i, np; 	qcomtee_arg_for_each(np, u) {		switch (u[np].type) {		case QCOMTEE_ARG_TYPE_OB:			/* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */			params[np].u.ubuf.size = u[np].b.size; 			break;		case QCOMTEE_ARG_TYPE_IO:			/* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */			qcomtee_object_put(u[np].o); 			break;		case QCOMTEE_ARG_TYPE_OO:			/* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */			if (qcomtee_objref_from_arg(&params[np], &u[np], ctx))				goto out_failed; 			break;		case QCOMTEE_ARG_TYPE_IB:		default:			break;		}	} 	return 0; out_failed:	/* Undo qcomtee_objref_from_arg(). */	for (i = 0; i < np; i++) {		if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT)			qcomtee_context_del_qtee_object(&params[i], ctx);	} 	/* Release any IO and OO objects not processed. */	for (; i < num_params && u[i].type; i++) {		if (u[i].type == QCOMTEE_ARG_TYPE_OO ||		    u[i].type == QCOMTEE_ARG_TYPE_IO)			qcomtee_object_put(u[i].o);	} 	return -EINVAL;} /* TEE Device Ops. */ static int qcomtee_params_check(struct tee_param *params, int num_params){	int io = 0, oo = 0, ib = 0, ob = 0;	int i; 	/* QTEE can accept 64 arguments. */	if (num_params > QCOMTEE_ARGS_MAX)		return -EINVAL; 	/* Supported parameter types. */	for (i = 0; i < num_params; i++) {		switch (params[i].attr) {		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT:			ib++;			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:			ob++;			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:			io++;			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:			oo++;			break;		default:			return -EINVAL;		}	} 	/*  QTEE can accept 16 arguments of each supported types. */	if (io > QCOMTEE_ARGS_PER_TYPE || oo > QCOMTEE_ARGS_PER_TYPE ||	    ib > QCOMTEE_ARGS_PER_TYPE || ob > QCOMTEE_ARGS_PER_TYPE)		return -EINVAL; 	return 0;} /* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */static int qcomtee_root_object_check(u32 op, struct tee_param *params,				     int num_params){	/* Some privileged operations recognized by QTEE. */	if (op == QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE ||	    op == QCOMTEE_ROOT_OP_ADCI_ACCEPT ||	    op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN)		return -EINVAL; 	/*	 * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE	 * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a	 * credential object represents a privileged client for QTEE and	 * is used by the kernel only.	 */	if (op == QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS && num_params == 2) {		if (params[0].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT &&		    params[1].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) {			if (params[0].u.objref.id == TEE_OBJREF_NULL)				return -EINVAL;		}	} 	return 0;} /** * qcomtee_object_invoke() - Invoke a QTEE object. * @ctx: TEE context. * @arg: ioctl arguments. * @params: parameters for the object. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_object_invoke(struct tee_context *ctx,				 struct tee_ioctl_object_invoke_arg *arg,				 struct tee_param *params){	struct qcomtee_context_data *ctxdata = ctx->data;	struct qcomtee_object *object;	int i, ret, result; 	if (qcomtee_params_check(params, arg->num_params))		return -EINVAL; 	/* First, handle reserved operations: */	if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) {		del_qtee_object(arg->id, ctxdata); 		return 0;	} 	/* Otherwise, invoke a QTEE object: */	struct qcomtee_object_invoke_ctx *oic __free(kfree) =		qcomtee_object_invoke_ctx_alloc(ctx);	if (!oic)		return -ENOMEM; 	/* +1 for ending QCOMTEE_ARG_TYPE_INV. */	struct qcomtee_arg *u __free(kfree) = kzalloc_objs(*u,							   arg->num_params + 1);	if (!u)		return -ENOMEM; 	/* Get an object to invoke. */	if (arg->id == TEE_OBJREF_NULL) {		/* Use ROOT if TEE_OBJREF_NULL is invoked. */		if (qcomtee_root_object_check(arg->op, params, arg->num_params))			return -EINVAL; 		object = ROOT_QCOMTEE_OBJECT;	} else if (find_qtee_object(&object, arg->id, ctxdata)) {		return -EINVAL;	} 	ret = qcomtee_params_to_args(u, params, arg->num_params, ctx);	if (ret)		goto out; 	ret = qcomtee_object_do_invoke(oic, object, arg->op, u, &result);	if (ret) {		qcomtee_arg_for_each_input_object(i, u) {			qcomtee_user_object_set_notify(u[i].o, false);			qcomtee_object_put(u[i].o);		} 		goto out;	} 	/* Prase QTEE response and put driver's object copies: */ 	if (!result) {		/* Assume service is UNAVAIL if unable to process the result. */		if (qcomtee_params_from_args(params, u, arg->num_params, ctx))			result = QCOMTEE_MSG_ERROR_UNAVAIL;	} else {		/*		 * qcomtee_params_to_args() gets a copy of IO for the driver to		 * make sure they do not get released while in the middle of		 * invocation. On success (!result), qcomtee_params_from_args()		 * puts them; Otherwise, put them here.		 */		qcomtee_arg_for_each_input_object(i, u)			qcomtee_object_put(u[i].o);	} 	arg->ret = result;out:	qcomtee_object_put(object); 	return ret;} /** * qcomtee_supp_recv() - Wait for a request for the supplicant. * @ctx: TEE context. * @op: requested operation on the object. * @num_params: number of elements in the parameter array. * @params: parameters for @op. * * The first parameter is a meta %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT. * On input, it provides a user buffer. This buffer is used for parameters of * type %TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT in qcomtee_cb_params_from_args(). * On output, the object ID and request ID are stored in the meta parameter. * * @num_params is updated to the number of parameters that actually exist * in @params on return. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_supp_recv(struct tee_context *ctx, u32 *op, u32 *num_params,			     struct tee_param *params){	struct qcomtee_user_object_request_data data;	void __user *uaddr;	size_t ubuf_size;	int i, ret; 	if (!*num_params)		return -EINVAL; 

Lines 1–500 of 823.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t