diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db5..8013895622ccc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22068,6 +22068,13 @@ L: linux-arm-msm@vger.kernel.org S: Maintained F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c +QUALCOMM TEE UEFISECAPP DRIVER +M: Harshal Dev +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom/qcom_tee_uefisecapp.c +F: drivers/firmware/qcom/qcom_tee_uefisecapp.h + QUALCOMM RMNET DRIVER M: Subash Abhinov Kasiviswanathan M: Sean Tranchetti diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index b477d54b495a6..20ce8b58e4905 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -74,4 +74,28 @@ config QCOM_QSEECOM_UEFISECAPP Select Y here to provide access to EFI variables on the aforementioned platforms. +config QCOM_TEE_UEFISECAPP + tristate "Qualcomm TEE UEFI Secure App client driver" + depends on QCOMTEE + depends on EFI + depends on !QCOM_QSEECOM_UEFISECAPP + help + On Qualcomm SoC based platforms without emulated RPMB support, + specifically platforms where RPMB is not present within SPI-NOR storage + and instead located on UFS/EMMC storage, non-volatile EFI variables can + only be set via a callback request from the UEFI Secure Application to + the RPMB service running in user-space (within the QTEE supplicant: + github.com/qualcomm/minkipc). Unlike the QCOMTEE driver, the QSEECOM + driver used by the QSEECOM based uefisecapp does not support callback + requests. And so on these platforms, the TEE based uefisecapp client + driver must be used to ensure persistence of non-volatile EFI variables + via writes through the RPMB service hosted in the QTEE supplicant. + + This module provides a TEE client driver for uefisecapp, installing efivar + operations to allow the kernel and user-space access to EFI variables. + + Select m here to provide access to EFI variables on the aforementioned + platforms if your Linux distribution has QTEE supplicant installed and + running. + endmenu diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 0be40a1abc13c..d780490b2865b 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o +obj-$(CONFIG_QCOM_TEE_UEFISECAPP) += qcom_tee_uefisecapp.o diff --git a/drivers/firmware/qcom/qcom_tee_uefisecapp.c b/drivers/firmware/qcom/qcom_tee_uefisecapp.c new file mode 100644 index 0000000000000..9a5a6f145a9ff --- /dev/null +++ b/drivers/firmware/qcom/qcom_tee_uefisecapp.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include "qcom_tee_uefisecapp.h" + +static struct qcomtee_uefisec_app uefisec_app; + +static int qcuefi_get_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + struct tee_param_ubuf in_attributes, + struct tee_param_ubuf *data, + u32 *out_attributes, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 5; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 in_data_size; + } in_cong = { 0 }; + + struct { + u32 out_data_size; + u32 attributes; + u32 errno; + } out_cong = { 0 }; + + in_cong.guid = *guid; + in_cong.in_data_size = data->size; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_GET_VAR, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_INPUT, in_attributes); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + SET_TEE_PARAM_UBUF(param[4], UBUF_OUTPUT, *data); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_GET_VAR invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + data->size = out_cong.out_data_size; + *out_attributes = out_cong.attributes; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, + u32 *attr, unsigned long *data_size, + void *data) +{ + int ret; + u32 in_attr, out_attributes, out_errno; + struct tee_param_ubuf in_data, in_var, in_attributes; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + /* 'attr' can be NULL, however an input attribute is always expected + * by UefiSecApp TA + */ + in_attr = 0; + if (attr) + in_attr = *attr; + + in_data = (struct tee_param_ubuf){ .addr = data, *data_size }; + in_var = (struct tee_param_ubuf){ .addr = name, + (ucs2_strlen(name) + 1) * sizeof(*name) }; + in_attributes = (struct tee_param_ubuf){ .addr = &in_attr, sizeof(u32) }; + + /* On SUCCESS, 'data' member of 'in_data' has already been updated. */ + ret = qcuefi_get_variable(in_var, guid, in_attributes, &in_data, + &out_attributes, &out_errno); + + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno || out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT) { + /* If 'attr' is NULL 'out_attributes' is not updated. */ + if (attr) + *attr = out_attributes; + + *data_size = in_data.size; + } + + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_set_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + u32 attributes, struct tee_param_ubuf data, + u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 4; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 attributes; + u32 in_data_size; + } in_cong = { 0 }; + + in_cong.guid = *guid; + in_cong.attributes = attributes; + in_cong.in_data_size = data.size; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_SET_VAR, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_INPUT, data); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, TEE_PARAM_UBUF(*out_errno)); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_SET_VAR invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + return ret; +} + +static efi_status_t qcomtee_uefi_set_variable(efi_char16_t *name, efi_guid_t *guid, + u32 attr, unsigned long data_size, + void *data) +{ + int ret; + u32 out_errno; + struct tee_param_ubuf in_data, in_var; + + if (!name || !guid) + return EFI_INVALID_PARAMETER; + + in_data = (struct tee_param_ubuf){ .addr = data, data_size }; + in_var = (struct tee_param_ubuf){ .addr = name, + (ucs2_strlen(name) + 1) * sizeof(*name) }; + + ret = qcuefi_set_variable(in_var, guid, attr, in_data, &out_errno); + if (ret) + return EFI_DEVICE_ERROR; + + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_get_next_variable(struct tee_param_ubuf in_variable, efi_guid_t *guid, + struct tee_param_ubuf *out_variable, + efi_guid_t *out_vendor_guid, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 4; + struct tee_param param[nparams]; + + struct { + efi_guid_t guid; + u32 in_data_size; + } in_cong = { 0 }; + + struct { + efi_guid_t guid; + u32 out_data_size; + u32 errno; + } out_cong = { 0 }; + + /* Pass size of available buffer */ + in_cong.in_data_size = out_variable->size; + in_cong.guid = *guid; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(in_cong)); + SET_TEE_PARAM_UBUF(param[1], UBUF_INPUT, in_variable); + SET_TEE_PARAM_UBUF(param[2], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + SET_TEE_PARAM_UBUF(param[3], UBUF_OUTPUT, *out_variable); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + /* UefiSecApp TA does not touch 'out_variable.size'. Update it here. + * On SUCCESS (!out_errno), 'out_data_size' is length of name in 'out_variable.addr'. + * On failure (out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT), 'out_data_size' is + * actual name length. + * Otherwise, it's undefined. + */ + out_variable->size = out_cong.out_data_size; + *out_vendor_guid = out_cong.guid; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *guid) +{ + int ret; + u32 out_errno; + efi_guid_t out_guid; + struct tee_param_ubuf in_var, out_var; + + if (!name_size || !name || !guid) + return EFI_INVALID_PARAMETER; + + if (*name_size == 0) + return EFI_INVALID_PARAMETER; + + /* For 'in_var', 'name_size' is not necessarily size of 'name'; + * could be size of buffer where 'name' has been stored. TA expects a + * NULL-terminated string in 'name' and ignores the size. + * For 'out_var', 'name_size' is size of buffer pointed by 'name'. + */ + in_var = (struct tee_param_ubuf){ .addr = name, *name_size }; + out_var = (struct tee_param_ubuf){ .addr = name, *name_size }; + + ret = qcuefi_get_next_variable(in_var, guid, &out_var, &out_guid, + &out_errno); + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno) + *guid = out_guid; + + if (!out_errno || out_errno == QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT) + *name_size = out_var.size; + + /* On SUCCESS, 'name' stores the next variable name. */ + return uefisecapp_err_to_efi_status(out_errno); +} + +static int qcuefi_query_variable_info(u32 attributes, + u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size, u32 *out_errno) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = uefisec_app.uefisec_svc_obj.id; + u32 nparams = 2; + struct tee_param param[nparams]; + + struct { + u64 max_var_storage_size; + u64 remaining_var_storage_size; + u64 maximum_var_size; + u32 errno; + } out_cong = { 0 }; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(attributes)); + SET_TEE_PARAM_UBUF(param[1], UBUF_OUTPUT, TEE_PARAM_UBUF(out_cong)); + + ret = tee_client_object_invoke_func(uefisec_app.ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *maximum_variable_storage_size = out_cong.max_var_storage_size; + *remaining_variable_storage_size = out_cong.remaining_var_storage_size; + *maximum_variable_size = out_cong.maximum_var_size; + *out_errno = out_cong.errno; + + return ret; +} + +static efi_status_t qcomtee_uefi_query_variable_info(u32 attr, u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + int ret; + u32 out_errno; + u64 maximum_variable_storage_size; + u64 remaining_variable_storage_size; + u64 maximum_variable_size; + + if (!storage_space || !remaining_space || !max_variable_size) + return EFI_INVALID_PARAMETER; + + ret = qcuefi_query_variable_info(attr, + &maximum_variable_storage_size, + &remaining_variable_storage_size, + &maximum_variable_size, + &out_errno); + + if (ret) + return EFI_DEVICE_ERROR; + + if (!out_errno) { + *storage_space = maximum_variable_storage_size; + *remaining_space = remaining_variable_storage_size; + *max_variable_size = maximum_variable_size; + } + + return uefisecapp_err_to_efi_status(out_errno); +} + +/** + * qcomtee_release_object() - Release an object returned by QTEE. + * + * Each object returned by QTEE repesents a secure service exposed to the + * client. Whenever an secure service is opened, QTEE may allocate resources + * on the client's behalf. Therefore, once the client is done accessing the + * secure service, the object representing it should be explicitly released + * so that QTEE can release the associated resources as well. + * + * @ctx: TEE context. + * @object: The object to release. + */ +static void qcomtee_release_object(struct tee_context *ctx, + struct tee_param_objref object) +{ + struct tee_ioctl_object_invoke_arg inv_arg; + + memset(&inv_arg, 0, sizeof(inv_arg)); + SET_INVOKE_ARG(inv_arg, object.id, QCOMTEE_MSG_OBJECT_OP_RELEASE, 0); + tee_client_object_invoke_func(ctx, &inv_arg, NULL); +} + +/** + * qcomtee_get_uefisec_svc_obj() - Get a UEFI Secure App service object to + * begin communication with the service. + * @ctx: TEE context. + * @client_env_obj: The client environment object returned earlier by QTEE. + * @uefisec_svc_obj: The UEFI Secure App service object. + * + * Returns 0 on success. + * Returns < 0 if client environment object invocation failed. + * Returns > 0 if client environment invocation was success but UEFI Secure App + * service object could not be returned for some other reason (represented by the + * returned value) + */ +static int qcomtee_get_uefisec_svc_obj(struct tee_context *ctx, + struct tee_param_objref client_env_obj, + struct tee_param_objref *uefisec_svc_obj) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u64 obj_id = client_env_obj.id; + u32 nparams = 2; + struct tee_param param[nparams]; + u32 uefisec_uid = QCOMTEE_UEFI_SEC_UID; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, obj_id, QCOMTEE_OP_CLIENT_ENV_OPEN, nparams); + SET_TEE_PARAM_UBUF(param[0], UBUF_INPUT, TEE_PARAM_UBUF(uefisec_uid)); + SET_TEE_PARAM_OBJREF(param[1], OBJREF_OUTPUT, 0, 0); + + ret = tee_client_object_invoke_func(ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_CLIENT_ENV_OPEN invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *uefisec_svc_obj = param[1].u.objref; + return ret; +} + +/** + * qcomtee_get_client_env_obj() - Get a client environment object to begin + * object exchange with QTEE. + * @ctx: TEE context. + * @client_env_obj: The client environment object returned by QTEE. + * + * Returns 0 on success. + * Returns < 0 if root object invocation failed. + * Returns > 0 if root object invocation was success but client environment + * object could not be returned for some other reason (represented by the + * returned value) + */ +static int qcomtee_get_client_env_obj(struct tee_context *ctx, + struct tee_param_objref *client_env_obj) +{ + int ret; + struct tee_ioctl_object_invoke_arg inv_arg; + u32 nparams = 2; + struct tee_param param[nparams]; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + SET_INVOKE_ARG(inv_arg, TEE_OBJREF_NULL, + QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS, nparams); + SET_TEE_PARAM_OBJREF(param[0], OBJREF_INPUT, TEE_OBJREF_NULL, 0); + SET_TEE_PARAM_OBJREF(param[1], OBJREF_OUTPUT, 0, 0); + + ret = tee_client_object_invoke_func(ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(uefisec_app.dev, "QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS invoke ret: %d, err: 0x%x\n", + ret, inv_arg.ret); + return ret ?: inv_arg.ret; + } + + *client_env_obj = param[1].u.objref; + return ret; +} + +static const struct efivar_operations qcom_efivar_ops = { + .get_variable = qcomtee_uefi_get_variable, + .set_variable = qcomtee_uefi_set_variable, + .get_next_variable = qcomtee_uefi_get_next_variable, + .query_variable_info = qcomtee_uefi_query_variable_info, +}; + +static int qcomtee_ctx_match(struct tee_ioctl_version_data *ver, + const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_QTEE); +} + +static int qcomtee_uefisecapp_probe(struct tee_client_device *tee_dev) +{ + int ret, err; + struct tee_param_objref client_env_obj; + struct tee_param_objref uefisec_svc_obj; + + uefisec_app.dev = &tee_dev->dev; + /* Open context with QCOMTEE driver */ + uefisec_app.ctx = tee_client_open_context(NULL, qcomtee_ctx_match, NULL, + NULL); + if (IS_ERR(uefisec_app.ctx)) + return -ENODEV; + + /* Obtain a reference to client_env object to begin object exchange + * with QTEE + */ + ret = qcomtee_get_client_env_obj(uefisec_app.ctx, &client_env_obj); + if (ret) { + err = -EINVAL; + goto err_get_client_env; + } + + /* Obtain a reference to the uefisec_svc object which provides access to + * the EFI var storage. + */ + ret = qcomtee_get_uefisec_svc_obj(uefisec_app.ctx, client_env_obj, + &uefisec_svc_obj); + if (ret) { + err = -EINVAL; + goto err_get_uefisec_svc; + } + uefisec_app.uefisec_svc_obj = uefisec_svc_obj; + + ret = efivars_register(&uefisec_app.efivars, &qcom_efivar_ops); + if (ret) { + err = ret; + goto err_efi_vars_reg; + } + + /* We don't need to keep a reference to this object anymore, we only + * needed it to obtain the uefisec_svc object. + */ + qcomtee_release_object(uefisec_app.ctx, client_env_obj); + return 0; + +err_efi_vars_reg: + qcomtee_release_object(uefisec_app.ctx, uefisec_svc_obj); +err_get_uefisec_svc: + qcomtee_release_object(uefisec_app.ctx, client_env_obj); +err_get_client_env: + tee_client_close_context(uefisec_app.ctx); + + return err; +} + +static void qcomtee_uefisecapp_remove(struct tee_client_device *tee_dev) +{ + efivars_unregister(&uefisec_app.efivars); + qcomtee_release_object(uefisec_app.ctx, uefisec_app.uefisec_svc_obj); + tee_client_close_context(uefisec_app.ctx); +} + +static const struct tee_client_device_id qcomtee_uefisecapp_id_table[] = { + {UEFISECAPP_UUID}, + {} +}; +MODULE_DEVICE_TABLE(tee, qcomtee_uefisecapp_id_table); + +static struct tee_client_driver qcomtee_uefisecapp_driver = { + .id_table = qcomtee_uefisecapp_id_table, + .probe = qcomtee_uefisecapp_probe, + .remove = qcomtee_uefisecapp_remove, + .driver = { + .name = "qcom-tee-uefisecapp", + }, +}; + +module_tee_client_driver(qcomtee_uefisecapp_driver); + +MODULE_AUTHOR("Qualcomm"); +MODULE_DESCRIPTION("TEE client driver for Qualcomm TEE UEFI Secure App"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom/qcom_tee_uefisecapp.h b/drivers/firmware/qcom/qcom_tee_uefisecapp.h new file mode 100644 index 0000000000000..a014c18cfed08 --- /dev/null +++ b/drivers/firmware/qcom/qcom_tee_uefisecapp.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_TEE_UEFISECAPP_H +#define QCOM_TEE_UEFISECAPP_H + +#define QCOMTEE_OP_CLIENT_ENV_OPEN 0 +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5 + +/* Each service exposed by QTEE is identified by a 32-bit UID */ +#define QCOMTEE_UEFI_SEC_UID 413 + +/* Operations supported by the UEFI Sec App service */ +#define QCOMTEE_UEFI_SEC_OP_GET_VAR 0 +#define QCOMTEE_UEFI_SEC_OP_SET_VAR 1 +#define QCOMTEE_UEFI_SEC_OP_QUERY_VAR_INFO 2 +#define QCOMTEE_UEFI_SEC_OP_GET_NEXT_VAR_NAME 3 + +/* Error codes returned by the UEFI Sec App service */ +#define QCOMTEE_UEFI_SEC_SUCCESS 0 +#define QCOMTEE_UEFI_SEC_ERROR_INVALID_PARAMETER 10 +#define QCOMTEE_UEFI_SEC_ERROR_UNSUPPORTED 11 +#define QCOMTEE_UEFI_SEC_ERROR_WRITE_PROTECTED 12 +#define QCOMTEE_UEFI_SEC_ERROR_SECURITY_VIOLATION 13 +#define QCOMTEE_UEFI_SEC_ERROR_DEVICE_ERROR 14 +#define QCOMTEE_UEFI_SEC_ERROR_OUT_OF_RESOURCES 15 +#define QCOMTEE_UEFI_SEC_ERROR_VOLUME_CORRUPTED 16 +#define QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT 17 +#define QCOMTEE_UEFI_SEC_ERROR_NOT_FOUND 18 +#define QCOMTEE_UEFI_SEC_ERROR_ALREADY_STARTED 19 + +/* Operations for objects are 32-bit. QCOMTEE transport uses the upper 16 bits. */ +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0) +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0) + +/** + * struct qcomtee_uefisec_app - An instance of UEFI Secure Application. + * @dev: TEE client device on the TEE bus which represents uefisecapp. + * @ctx: The context opened with the TEE subsystem by the uefisecapp client. + * @uefisec_svc_obj: A TEE object representing the uefisecapp service. + * @efivars: EFI variables registered with the EFI subsystem. + */ +struct qcomtee_uefisec_app { + struct device *dev; + struct tee_context *ctx; + struct tee_param_objref uefisec_svc_obj; + struct efivars efivars; +}; + +#define UEFISECAPP_UUID \ + UUID_INIT(0x01f95dcd, 0x2d7e, 0x58be, \ + 0xa1, 0x43, 0x81, 0x32, 0xa1, 0x72, 0xdb, 0x7d) + +/* Short-hands for these long attribute names */ +#define UBUF_INPUT TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT +#define UBUF_OUTPUT TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT +#define OBJREF_INPUT TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT +#define OBJREF_OUTPUT TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT + +/* Init instance of 'struct tee_param_objref'. */ +#define SET_TEE_PARAM_OBJREF(param, attri, obj_id, obj_flag) do { \ + (param).attr = (attri); \ + (param).u.objref.id = (obj_id); \ + (param).u.objref.flags = (obj_flag); \ + } while (0) + +/* Init instance of 'struct tee_param_ubuf'. */ +#define SET_TEE_PARAM_UBUF(param, attri, ubuff) do { \ + (param).attr = (attri); \ + (param).u.ubuf = (ubuff); \ + } while (0) + +#define TEE_PARAM_UBUF(x) ((struct tee_param_ubuf){ .addr = &(x), sizeof(x) }) + +#define SET_INVOKE_ARG(arg, object_id, opp, nparam) do { \ + (arg).id = (object_id); \ + (arg).op = (opp); \ + (arg).num_params = (nparam); \ + } while (0) + +static inline efi_status_t uefisecapp_err_to_efi_status(u32 err) +{ + switch (err) { + case QCOMTEE_UEFI_SEC_SUCCESS: + return EFI_SUCCESS; + + case QCOMTEE_UEFI_SEC_ERROR_INVALID_PARAMETER: + return EFI_INVALID_PARAMETER; + + case QCOMTEE_UEFI_SEC_ERROR_UNSUPPORTED: + return EFI_UNSUPPORTED; + + case QCOMTEE_UEFI_SEC_ERROR_WRITE_PROTECTED: + return EFI_WRITE_PROTECTED; + + case QCOMTEE_UEFI_SEC_ERROR_SECURITY_VIOLATION: + return EFI_SECURITY_VIOLATION; + + case QCOMTEE_UEFI_SEC_ERROR_DEVICE_ERROR: + return EFI_DEVICE_ERROR; + + case QCOMTEE_UEFI_SEC_ERROR_OUT_OF_RESOURCES: + return EFI_OUT_OF_RESOURCES; + + case QCOMTEE_UEFI_SEC_ERROR_SIZE_OUT: + return EFI_BUFFER_TOO_SMALL; + + case QCOMTEE_UEFI_SEC_ERROR_NOT_FOUND: + return EFI_NOT_FOUND; + + /* No matching on EFI_* list. */ + case QCOMTEE_UEFI_SEC_ERROR_ALREADY_STARTED: /* EFI_ALREADY_STARTED. */ + case QCOMTEE_UEFI_SEC_ERROR_VOLUME_CORRUPTED: /* EFI_VOLUME_CORRUPTED. */ + default: + return EFI_DEVICE_ERROR; + } +} +#endif /* QCOM_TEE_UEFISECAPP_H */ diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index 0efc5646242af..4373fcac2f5f3 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -202,7 +202,7 @@ int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, */ static int qcomtee_params_to_args(struct qcomtee_arg *u, struct tee_param *params, int num_params, - struct tee_context *ctx) + struct qcomtee_object_invoke_ctx *oic) { int i; @@ -210,8 +210,14 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, 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].flags = oic->kernel_ctx ? 0 : + QCOMTEE_ARG_FLAGS_UADDR; + + if (u[i].flags && QCOMTEE_ARG_FLAGS_UADDR) + u[i].b.uaddr = params[i].u.ubuf.uaddr; + else + u[i].b.addr = params[i].u.ubuf.addr; + u[i].b.size = params[i].u.ubuf.size; if (params[i].attr == @@ -223,7 +229,7 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: u[i].type = QCOMTEE_ARG_TYPE_IO; - if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) + if (qcomtee_objref_to_arg(&u[i], ¶ms[i], oic->ctx)) goto out_failed; break; @@ -270,7 +276,7 @@ static int qcomtee_params_to_args(struct qcomtee_arg *u, */ static int qcomtee_params_from_args(struct tee_param *params, struct qcomtee_arg *u, int num_params, - struct tee_context *ctx) + struct qcomtee_object_invoke_ctx *oic) { int i, np; @@ -288,7 +294,8 @@ static int qcomtee_params_from_args(struct tee_param *params, break; case QCOMTEE_ARG_TYPE_OO: /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ - if (qcomtee_objref_from_arg(¶ms[np], &u[np], ctx)) + if (qcomtee_objref_from_arg(¶ms[np], &u[np], + oic->ctx)) goto out_failed; break; @@ -304,7 +311,7 @@ static int qcomtee_params_from_args(struct tee_param *params, /* 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(¶ms[i], ctx); + qcomtee_context_del_qtee_object(¶ms[i], oic->ctx); } /* Release any IO and OO objects not processed. */ @@ -357,7 +364,8 @@ static int qcomtee_params_check(struct tee_param *params, int num_params) } /* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */ -static int qcomtee_root_object_check(u32 op, struct tee_param *params, +static int qcomtee_root_object_check(struct qcomtee_object_invoke_ctx *oic, + u32 op, struct tee_param *params, int num_params) { /* Some privileged operations recognized by QTEE. */ @@ -366,6 +374,9 @@ static int qcomtee_root_object_check(u32 op, struct tee_param *params, op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN) return -EINVAL; + if (oic->kernel_ctx) + return 0; + /* * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a @@ -397,11 +408,31 @@ static int qcomtee_object_invoke(struct tee_context *ctx, { struct qcomtee_context_data *ctxdata = ctx->data; struct qcomtee_object *object; + bool kernel_ctx = false; int i, ret, result; if (qcomtee_params_check(params, arg->num_params)) return -EINVAL; + /* Obtain the invocation context information from the MSB of the object + * `id` field. + */ + kernel_ctx = QCOMTEE_GET_CLIENT_CTX(arg->id); + /* User-space identifies a NULL object via a 32-bit TEE_OBJREF_NULL id, whereas + * the kernel uses as 64-bit object-id. Hence, we check for a NULL object by + * sign-extending the object-id to 64 bits. If user-space is indeed invoking a + * NULL object we must extend the object-id to 64-bits from here on so that + * QCOMTEE can recognize it. + */ + if (!kernel_ctx && ((s64)(s32)arg->id) == TEE_OBJREF_NULL) + arg->id = TEE_OBJREF_NULL; + + /* If the object being invoked is not NULL, drop the MSB from the `id` field to + * obtain the actual object-id. + */ + if (arg->id != TEE_OBJREF_NULL) + arg->id = QCOMTEE_SANITIZE_OBJ_ID(arg->id); + /* First, handle reserved operations: */ if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { del_qtee_object(arg->id, ctxdata); @@ -411,7 +442,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, /* Otherwise, invoke a QTEE object: */ struct qcomtee_object_invoke_ctx *oic __free(kfree) = - qcomtee_object_invoke_ctx_alloc(ctx); + qcomtee_object_invoke_ctx_alloc(ctx, kernel_ctx); if (!oic) return -ENOMEM; @@ -424,7 +455,8 @@ static int qcomtee_object_invoke(struct tee_context *ctx, /* 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)) + if (qcomtee_root_object_check(oic, arg->op, params, + arg->num_params)) return -EINVAL; object = ROOT_QCOMTEE_OBJECT; @@ -432,7 +464,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, return -EINVAL; } - ret = qcomtee_params_to_args(u, params, arg->num_params, ctx); + ret = qcomtee_params_to_args(u, params, arg->num_params, oic); if (ret) goto out; @@ -450,7 +482,7 @@ static int qcomtee_object_invoke(struct tee_context *ctx, if (!result) { /* Assume service is UNAVAIL if unable to process the result. */ - if (qcomtee_params_from_args(params, u, arg->num_params, ctx)) + if (qcomtee_params_from_args(params, u, arg->num_params, oic)) result = QCOMTEE_MSG_ERROR_UNAVAIL; } else { /* @@ -645,10 +677,10 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, { struct qcomtee_object *client_env, *service; struct qcomtee_arg u[3] = { 0 }; - int result; + int result, error = 0; struct qcomtee_object_invoke_ctx *oic __free(kfree) = - qcomtee_object_invoke_ctx_alloc(ctx); + qcomtee_object_invoke_ctx_alloc(ctx, true); if (!oic) return; @@ -658,9 +690,13 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, /* Get ''FeatureVersions Service'' object. */ service = qcomtee_object_get_service(oic, client_env, - QCOMTEE_FEATURE_VER_UID); - if (service == NULL_QCOMTEE_OBJECT) + QCOMTEE_FEATURE_VER_UID, + &error); + if (service == NULL_QCOMTEE_OBJECT) { + if (error) + pr_err("Failed to get service! error: %d\n", error); goto out_failed; + } /* IB: Feature to query. */ u[0].b.addr = &id; @@ -680,6 +716,153 @@ static void qcomtee_get_qtee_feature_list(struct tee_context *ctx, u32 id, qcomtee_object_put(client_env); } +/** + * is_qcomtee_service_available() - Check if the QTEE service identified by the UID + * is available + * @ctx: TEE context. + * @uid: 32-bit UID of the service. + * + * Returns true if the service exists and is available. + * Returns false if a service is not exposed by QTEE. + */ +static bool is_qcomtee_service_available(struct tee_context *ctx, u32 uid) +{ + struct qcomtee_object *client_env; + struct qcomtee_object *service; + int error = 0; + bool ret = false; + + struct qcomtee_object_invoke_ctx *oic __free(kfree) = + qcomtee_object_invoke_ctx_alloc(ctx, true); + if (!oic) + return ret; + + client_env = qcomtee_object_get_client_env(oic); + if (client_env == NULL_QCOMTEE_OBJECT) + return ret; + + /* Get service object corresponding to the uid. */ + service = qcomtee_object_get_service(oic, client_env, uid, &error); + if (service != NULL_QCOMTEE_OBJECT) { + qcomtee_object_put(service); + ret = true; + } + + /* When we fail to get the service, QTEE provides the reason. */ + if (error) + pr_err("Failed to get service! error: %d\n", error); + + qcomtee_object_put(client_env); + return ret; +} + +/* + * QTEE Service UUID name space identifier + * + * A random UUID that is allocated as a name space identifier for forming UUID's + * representing secure services exposed by QTEE. + */ +static const uuid_t qtee_service_uuid_ns = UUID_INIT(0xe1b48857, 0x6154, 0x49f9, + 0x93, 0x4e, 0xa2, 0xf2, + 0x0a, 0xba, 0x98, 0x42); + +static const struct qtee_service qtee_services[] = { + { "qcom.tz.uefisecapp", + QCOMTEE_UEFI_SEC_UID } +}; + +static void qtee_release_service(struct device *dev) +{ + struct tee_client_device *qtee_service = to_tee_client_device(dev); + + kfree(qtee_service); +} + +/** + * qtee_enumerate_service() - Enumerate a given QTEE service and register + * it on the TEE bus as a TEE client device + * @ctx: TEE context. + * @service_uuid: UUID of the service to be registered on the TEE bus. + * @uid: 32-bit UID used by QTEE to identify the service. + * + * Returns 0 on success and < 0 on failure. + */ +static int qtee_enumerate_service(struct tee_context *ctx, const char *service_name, + const u32 uid) +{ + struct tee_client_device *qtee_service; + uuid_t service_uuid; + int rc; + + if (!is_qcomtee_service_available(ctx, uid)) + return -ENXIO; + + tee_generate_uuid_v5(&service_uuid, &qtee_service_uuid_ns, service_name, + strlen(service_name)); + + qtee_service = kzalloc_obj(*qtee_service); + if (!qtee_service) + return -ENOMEM; + + qtee_service->dev.bus = &tee_bus_type; + qtee_service->dev.release = qtee_release_service; + if (dev_set_name(&qtee_service->dev, "qtee-svc-%pUb", &service_uuid)) { + kfree(qtee_service); + return -ENOMEM; + } + uuid_copy(&qtee_service->id.uuid, &service_uuid); + + rc = device_register(&qtee_service->dev); + if (rc) { + pr_err("QTEE service registration failed, err: %d\n", rc); + put_device(&qtee_service->dev); + kfree(qtee_service); + return rc; + } + + return 0; +} + +/** + * qtee_enumerate_services() - Enumerate all the secure services exposed by QTEE + * from the static 'qtee_services' list and register them on the TEE bus as + * TEE client devices. + * + * Not all versions of QTEE support a given service. Hence, we try to + * enumerate as many services from the 'qtee_services' list as possible. + * Not being able to enumerate a service shouldn't cause the driver probe + * to fail since none of the services in the list are mandatory for + * establishing communication with QTEE. + * @ctx: TEE context. + */ +static void qtee_enumerate_services(struct tee_context *ctx) +{ + int rc; + u32 idx; + + for (idx = 0; idx < ARRAY_SIZE(qtee_services); idx++) { + rc = qtee_enumerate_service(ctx, qtee_services[idx].name, + qtee_services[idx].uid); + if (rc == -ENXIO) + pr_err("QTEE does not implement service %d.\n", + qtee_services[idx].uid); + } +} + +static int qtee_unregister_service(struct device *dev, void *data) +{ + if (!strncmp(dev_name(dev), "qtee-svc", strlen("qtee-svc"))) + device_unregister(dev); + + return 0; +} + +static void qtee_unregister_services(void) +{ + bus_for_each_dev(&tee_bus_type, NULL, NULL, + qtee_unregister_service); +} + static const struct tee_driver_ops qcomtee_ops = { .get_version = qcomtee_get_version, .open = qcomtee_open, @@ -761,6 +944,8 @@ static int qcomtee_probe(struct platform_device *pdev) QTEE_VERSION_GET_MINOR(qcomtee->qtee_version), QTEE_VERSION_GET_PATCH(qcomtee->qtee_version)); + qtee_enumerate_services(qcomtee->ctx); + return 0; err_dest_wq: @@ -790,6 +975,7 @@ static void qcomtee_remove(struct platform_device *pdev) { struct qcomtee *qcomtee = platform_get_drvdata(pdev); + qtee_unregister_services(); teedev_close_context(qcomtee->ctx); /* Wait for RELEASE operations to be processed for QTEE objects. */ tee_device_unregister(qcomtee->teedev); diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index b1cb50e434f00..4e39e867c3e95 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -896,19 +896,20 @@ qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic) struct qcomtee_object * qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, - struct qcomtee_object *client_env, u32 uid) + struct qcomtee_object *client_env, u32 uid, + int *result) { struct qcomtee_arg u[3] = { 0 }; - int ret, result; + int ret; u[0].b.addr = &uid; u[0].b.size = sizeof(uid); u[0].type = QCOMTEE_ARG_TYPE_IB; u[1].type = QCOMTEE_ARG_TYPE_OO; ret = qcomtee_object_do_invoke(oic, client_env, QCOMTEE_CLIENT_ENV_OPEN, - u, &result); + u, result); - if (ret || result) + if (ret || *result) return NULL_QCOMTEE_OBJECT; return u[1].o; diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h index f39bf63fd1c2b..44fbed6868cae 100644 --- a/drivers/tee/qcomtee/qcomtee.h +++ b/drivers/tee/qcomtee/qcomtee.h @@ -17,6 +17,14 @@ #define QCOMTEE_OBJREF_FLAG_USER BIT(1) #define QCOMTEE_OBJREF_FLAG_MEM BIT(2) +#define QTEE_UUID_NS_NAME_SIZE 128 + +/* The MSB of the object_id field indicates whether the client is invoking the + * object from user context or kernel context. + */ +#define QCOMTEE_GET_CLIENT_CTX(x) (((x) >> 63) & 1U) +#define QCOMTEE_SANITIZE_OBJ_ID(x) ((x) & (BIT(63) - 1)) + /** * struct qcomtee - Main service struct. * @teedev: client device. @@ -39,6 +47,16 @@ struct qcomtee { u32 qtee_version; }; +/** + * struct qtee_service - A secure service exposed by QTEE identified by a 32-bit UID. + * @name: Name of the QTEE service. + * @uid: 32-bit UID used by QTEE to identify the service. + */ +struct qtee_service { + const char *name; + const u32 uid; +}; + void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic); struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, u32 idx); diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h index 878f70178a5b6..ecaf8db67d452 100644 --- a/drivers/tee/qcomtee/qcomtee_msg.h +++ b/drivers/tee/qcomtee/qcomtee_msg.h @@ -105,6 +105,7 @@ union qcomtee_msg_arg { #define QTEE_VERSION_GET_MINOR(x) (((x) >> 12) & 0xffU) #define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU) +#define QCOMTEE_UEFI_SEC_UID 413 /* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */ /* The message contains a callback request. */ diff --git a/drivers/tee/qcomtee/qcomtee_object.h b/drivers/tee/qcomtee/qcomtee_object.h index 8b4401ecad48c..f4cb9b8fcbd44 100644 --- a/drivers/tee/qcomtee/qcomtee_object.h +++ b/drivers/tee/qcomtee/qcomtee_object.h @@ -112,8 +112,9 @@ struct qcomtee_buffer { * @b: address and size if the type of argument is a buffer. * @o: object instance if the type of argument is an object. * - * &qcomtee_arg.flags only accepts %QCOMTEE_ARG_FLAGS_UADDR for now, which - * states that &qcomtee_arg.b contains a userspace address in uaddr. ++ * If %QCOMTEE_ARG_FLAGS_UADDR is set in &qcomtee_arg.flags then it implies ++ * that &qcomtee_arg.b contains a userspace address in uaddr. ++ * Otherwise, &qcomtee_arg.b contains a kernel address in addr. */ struct qcomtee_arg { enum qcomtee_arg_type type; @@ -146,6 +147,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args) * struct qcomtee_object_invoke_ctx - QTEE context for object invocation. * @ctx: TEE context for this invocation. * @flags: flags for the invocation context. + * @kernel_ctx: flag that indicates this context is owned by a kernel client. * @errno: error code for the invocation. * @object: current object invoked in this callback context. * @u: array of arguments for the current invocation (+1 for ending arg). @@ -158,6 +160,7 @@ static inline int qcomtee_args_len(struct qcomtee_arg *args) struct qcomtee_object_invoke_ctx { struct tee_context *ctx; unsigned long flags; + bool kernel_ctx; int errno; struct qcomtee_object *object; @@ -172,13 +175,15 @@ struct qcomtee_object_invoke_ctx { }; static inline struct qcomtee_object_invoke_ctx * -qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx) +qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx, bool kernel_ctx) { struct qcomtee_object_invoke_ctx *oic; oic = kzalloc_obj(*oic); - if (oic) + if (oic) { oic->ctx = ctx; + oic->kernel_ctx = kernel_ctx; + } return oic; } @@ -311,6 +316,7 @@ qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic); struct qcomtee_object * qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic, - struct qcomtee_object *client_env, u32 uid); + struct qcomtee_object *client_env, u32 uid, + int *result); #endif /* QCOMTEE_OBJECT_H */ diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d726728..61dd99bbf5f64 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -135,7 +135,7 @@ static int tee_release(struct inode *inode, struct file *filp) } /** - * uuid_v5() - Calculate UUIDv5 + * tee_generate_uuid_v5() - Calculate UUIDv5 * @uuid: Resulting UUID * @ns: Name space ID for UUIDv5 function * @name: Name for UUIDv5 function @@ -146,8 +146,8 @@ static int tee_release(struct inode *inode, struct file *filp) * 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) +void tee_generate_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; @@ -163,6 +163,7 @@ static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, uuid->b[6] = (hash[6] & 0x0F) | 0x50; uuid->b[8] = (hash[8] & 0x3F) | 0x80; } +EXPORT_SYMBOL_GPL(tee_generate_uuid_v5); int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]) @@ -228,7 +229,7 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, goto out_free_name; } - uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); + tee_generate_uuid_v5(uuid, &tee_client_uuid_ns, name, name_len); out_free_name: kfree(name); @@ -706,6 +707,10 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, goto out; } + /* Userspace object-ids are restricted to 32-bits. */ + if (arg.id > U32_MAX) + return -EINVAL; + rc = ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params); if (rc) goto out; @@ -1409,6 +1414,19 @@ int tee_client_invoke_func(struct tee_context *ctx, } EXPORT_SYMBOL_GPL(tee_client_invoke_func); +int tee_client_object_invoke_func(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->object_invoke_func) + return -EINVAL; + + /* Indicate that this object is being invoked from a kernel context. */ + arg->id = arg->id | BIT(63); + return ctx->teedev->desc->ops->object_invoke_func(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_object_invoke_func); + int tee_client_cancel_req(struct tee_context *ctx, struct tee_ioctl_cancel_arg *arg) { diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index f993d5118edda..13807e795880f 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -266,6 +266,21 @@ void tee_device_set_dev_groups(struct tee_device *teedev, int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]); +/** + * tee_generate_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 + */ +void tee_generate_uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name, + size_t size); + /** * struct tee_shm_pool - shared memory pool * @ops: operations diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index e561a26f537ae..71d0536db60eb 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -83,7 +83,10 @@ struct tee_param_memref { }; struct tee_param_ubuf { - void __user *uaddr; + union { + void *addr; + void __user *uaddr; + }; size_t size; }; @@ -283,6 +286,19 @@ int tee_client_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); +/** + * tee_client_object_invoke_func() - Invoke a TEE object from kernel space + * @ctx: TEE Context + * @arg: Invoke arguments, see description of + * struct tee_ioctl_object_invoke_arg + * @param: Parameters for the object invocation + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int tee_client_object_invoke_func(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param); + /** * tee_client_cancel_req() - Request cancellation of the previous open-session * or invoke-command operations in a Trusted Application