diff --git a/MAINTAINERS b/MAINTAINERS index 2431f27b1e093..ffcafb3672140 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21347,6 +21347,15 @@ F: Documentation/devicetree/bindings/media/*qcom* F: drivers/media/platform/qcom F: include/dt-bindings/media/*qcom* +QUALCOMM PAS TZ SERVICE +M: Sumit Garg +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom/qcom_pas.c +F: drivers/firmware/qcom/qcom_pas.h +F: drivers/firmware/qcom/qcom_pas_tee.c +F: include/linux/firmware/qcom/qcom_pas.h + QUALCOMM SMB CHARGER DRIVER M: Casey Connolly L: linux-arm-msm@vger.kernel.org diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index dd20fed163e3f..e18fb19ce5e32 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -6,9 +6,29 @@ menu "Qualcomm firmware drivers" +config QCOM_PAS + tristate "Qualcomm generic PAS interface driver" + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware. It acts as the common layer with different TZ + backends plugged in whether it's an SCM implementation or a proper + TEE bus based PAS service implementation. + +config QCOM_PAS_TEE + tristate "Qualcomm PAS TEE interface driver" + select QCOM_PAS + depends on TEE + depends on !CPU_BIG_ENDIAN + default m if ARCH_QCOM + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware TEE implementation as the backend. + config QCOM_SCM + tristate "Qualcomm PAS SCM interface driver" + select QCOM_PAS select QCOM_TZMEM - tristate + default y if ARCH_QCOM config QCOM_TZMEM tristate diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 01b293cae79ed..898ea06622fe8 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -8,5 +8,7 @@ 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_PAS) += qcom_pas.o +obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_MEMORY_DUMP_DEV) += memory_dump_dev.o diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c new file mode 100644 index 0000000000000..eb9e8ac858603 --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "qcom_pas.h" + +static struct qcom_pas_ops *ops_ptr; + +/** + * devm_qcom_pas_context_alloc() - Allocate peripheral authentication service + * context for a given peripheral + * + * PAS context is device-resource managed, so the caller does not need + * to worry about freeing the context memory. + * + * @dev: PAS firmware device + * @pas_id: peripheral authentication service id + * @mem_phys: Subsystem reserve memory start address + * @mem_size: Subsystem reserve memory size + * + * Return: The new PAS context, or ERR_PTR() on failure. + */ +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size) +{ + struct qcom_pas_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->pas_id = pas_id; + ctx->mem_phys = mem_phys; + ctx->mem_size = mem_size; + + return ctx; +} +EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc); + +/** + * qcom_pas_init_image() - Initialize peripheral authentication service state + * machine for a given peripheral, using the metadata + * @pas_id: peripheral authentication service id + * @metadata: pointer to memory containing ELF header, program header table + * and optional blob of data used for authenticating the metadata + * and the rest of the firmware + * @size: size of the metadata + * @ctx: optional pas context + * + * Return: 0 on success. + * + * Upon successful return, the PAS metadata context (@ctx) will be used to + * track the metadata allocation, this needs to be released by invoking + * qcom_pas_metadata_release() by the caller. + */ +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_init_image); + +/** + * qcom_pas_metadata_release() - release metadata context + * @ctx: pas context + */ +void qcom_pas_metadata_release(struct qcom_pas_context *ctx) +{ + if (!ops_ptr || !ctx || !ctx->ptr) + return; + + ops_ptr->metadata_release(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_metadata_release); + +/** + * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @pas_id: peripheral authentication service id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Return: 0 on success. + */ +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size); +} +EXPORT_SYMBOL_GPL(qcom_pas_mem_setup); + +/** + * qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer + * for a given peripheral. + * + * Qualcomm remote processor may rely on both static and dynamic resources for + * its functionality. Static resources typically refer to memory-mapped + * addresses required by the subsystem and are often embedded within the + * firmware binary and dynamic resources, such as shared memory in DDR etc., + * are determined at runtime during the boot process. + * + * On Qualcomm Technologies devices, it's possible that static resources are + * not embedded in the firmware binary and instead are provided by TrustZone. + * However, dynamic resources are always expected to come from TrustZone. This + * indicates that for Qualcomm devices, all resources (static and dynamic) will + * be provided by TrustZone PAS service. + * + * If the remote processor firmware binary does contain static resources, they + * should be passed in input_rt. These will be forwarded to TrustZone for + * authentication. TrustZone will then append the dynamic resources and return + * the complete resource table in output_rt_tzm. + * + * If the remote processor firmware binary does not include a resource table, + * the caller of this function should set input_rt as NULL and input_rt_size + * as zero respectively. + * + * More about documentation on resource table data structures can be found in + * include/linux/remoteproc.h + * + * @ctx: PAS context + * @input_rt: resource table buffer which is present in firmware binary + * @input_rt_size: size of the resource table present in firmware binary + * @output_rt_size: TrustZone expects caller should pass worst case size for + * the output_rt_tzm. + * + * Return: + * On success, returns a pointer to the allocated buffer containing the final + * resource table and output_rt_size will have actual resource table size from + * TrustZone. The caller is responsible for freeing the buffer. On failure, + * returns ERR_PTR(-errno). + */ +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + if (!ops_ptr) + return ERR_PTR(-ENODEV); + if (!ctx) + return ERR_PTR(-EINVAL); + + return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt, + input_rt_size, output_rt_size); +} +EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table); + +/** + * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_auth_and_reset(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset); + +/** + * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the + * remote processor + * + * @ctx: Context saved during call to devm_qcom_pas_context_alloc() + * + * This function performs the necessary steps to prepare a PAS subsystem, + * authenticate it using the provided metadata, and initiate a reset sequence. + * + * It should be used when Linux is in control setting up the IOMMU hardware + * for remote subsystem during secure firmware loading processes. The + * preparation step sets up a shmbridge over the firmware memory before + * TrustZone accesses the firmware memory region for authentication. The + * authentication step verifies the integrity and authenticity of the firmware + * or configuration using secure metadata. Finally, the reset step ensures the + * subsystem starts in a clean and sane state. + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + if (!ctx) + return -EINVAL; + + return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset); + +/** + * qcom_pas_set_remote_state() - Set the remote processor state + * @state: peripheral state + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_set_remote_state(u32 state, u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state); + +/** + * qcom_pas_shutdown() - Shut down the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_shutdown(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->shutdown(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_shutdown); + +/** + * qcom_pas_supported() - Check if the peripheral authentication service is + * supported for the given peripheral + * @pas_id: peripheral authentication service id + * + * Return: true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_pas_supported(u32 pas_id) +{ + if (!ops_ptr) + return false; + + return ops_ptr->supported(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_supported); + +const char *qcom_pas_get_backend(void) +{ + struct qcom_pas_ops *ops = ops_ptr; + + return ops ? ops->drv_name : NULL; +} +EXPORT_SYMBOL_GPL(qcom_pas_get_backend); + +/** + * qcom_pas_is_available() - Check if the peripheral authentication service is + * available. Note that it is mandatory for any PAS + * client to invoke this API. If it returns true then + * only any other PAS API can be invoked. + * + * Return: true if PAS is available, otherwise false. + */ +bool qcom_pas_is_available(void) +{ + /* + * The barrier for ops_ptr is intended to synchronize the data stores + * for the ops data structure when client drivers are in parallel + * checking for PAS service availability. + * + * Once the PAS backend becomes available, it is allowed for multiple + * threads to enter TZ for parallel bringup of co-processors during + * boot. + */ + return !!smp_load_acquire(&ops_ptr); +} +EXPORT_SYMBOL_GPL(qcom_pas_is_available); + +void qcom_pas_ops_register(struct qcom_pas_ops *ops) +{ + if (!qcom_pas_is_available()) + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, ops); + else + pr_err("qcom_pas: ops already registered by %s\n", + ops_ptr->drv_name); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_register); + +void qcom_pas_ops_unregister(void) +{ + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, NULL); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver"); diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h new file mode 100644 index 0000000000000..8643e2760602e --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_INT_H +#define __QCOM_PAS_INT_H + +struct device; + +/** + * struct qcom_pas_ops - Qcom Peripheral Authentication Service (PAS) ops + * @drv_name: PAS driver name. + * @dev: PAS device pointer. + * @supported: Peripheral supported callback. + * @init_image: Peripheral image initialization callback. + * @mem_setup: Peripheral memory setup callback. + * @get_rsc_table: Peripheral get resource table callback. + * @prepare_and_auth_reset: Peripheral prepare firmware authentication and + * reset callback. + * @auth_and_reset: Peripheral firmware authentication and reset + * callback. + * @set_remote_state: Peripheral set remote state callback. + * @shutdown: Peripheral shutdown callback. + * @metadata_release: Image metadata release callback. + */ +struct qcom_pas_ops { + const char *drv_name; + struct device *dev; + bool (*supported)(struct device *dev, u32 pas_id); + int (*init_image)(struct device *dev, u32 pas_id, const void *metadata, + size_t size, struct qcom_pas_context *ctx); + int (*mem_setup)(struct device *dev, u32 pas_id, phys_addr_t addr, + phys_addr_t size); + void *(*get_rsc_table)(struct device *dev, struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); + int (*prepare_and_auth_reset)(struct device *dev, + struct qcom_pas_context *ctx); + int (*auth_and_reset)(struct device *dev, u32 pas_id); + int (*set_remote_state)(struct device *dev, u32 state, u32 pas_id); + int (*shutdown)(struct device *dev, u32 pas_id); + void (*metadata_release)(struct device *dev, + struct qcom_pas_context *ctx); +}; + +void qcom_pas_ops_register(struct qcom_pas_ops *ops); +void qcom_pas_ops_unregister(void); + +#endif /* __QCOM_PAS_INT_H */ diff --git a/drivers/firmware/qcom/qcom_pas_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c new file mode 100644 index 0000000000000..d869076e0189c --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas_tee.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_pas.h" + +/* + * Peripheral Authentication Service (PAS) supported. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_IS_SUPPORTED 1 + +/* + * PAS capabilities. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [out] params[1].value.a: PAS capability flags + */ +#define TA_QCOM_PAS_CAPABILITIES 2 + +/* + * PAS image initialization. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Loadable firmware metadata + */ +#define TA_QCOM_PAS_INIT_IMAGE 3 + +/* + * PAS memory setup. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Relocatable firmware size + * [in] params[1].value.a: 32bit LSB relocatable firmware memory address + * [in] params[1].value.b: 32bit MSB relocatable firmware memory address + */ +#define TA_QCOM_PAS_MEM_SETUP 4 + +/* + * PAS get resource table. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [inout] params[1].memref: Resource table config + */ +#define TA_QCOM_PAS_GET_RESOURCE_TABLE 5 + +/* + * PAS image authentication and co-processor reset. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Firmware size + * [in] params[1].value.a: 32bit LSB firmware memory address + * [in] params[1].value.b: 32bit MSB firmware memory address + * [in] params[2].memref: Optional fw memory space shared/lent + */ +#define TA_QCOM_PAS_AUTH_AND_RESET 6 + +/* + * PAS co-processor set suspend/resume state. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Co-processor state identifier + */ +#define TA_QCOM_PAS_SET_REMOTE_STATE 7 + +/* + * PAS co-processor shutdown. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_SHUTDOWN 8 + +#define TEE_NUM_PARAMS 4 + +/** + * struct qcom_pas_tee_private - PAS service private data + * @dev: PAS service device. + * @ctx: TEE context handler. + * @session_id: PAS TA session identifier. + */ +struct qcom_pas_tee_private { + struct device *dev; + struct tee_context *ctx; + u32 session_id; +}; + +static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_IS_SUPPORTED, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return false; + } + + return true; +} + +static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_INIT_IMAGE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + struct tee_shm *mdata_shm; + u8 *mdata_buf = NULL; + int ret; + + mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size); + if (IS_ERR(mdata_shm)) { + dev_err(dev, "mdata_shm allocation failed\n"); + return PTR_ERR(mdata_shm); + } + + mdata_buf = tee_shm_get_va(mdata_shm, 0); + if (IS_ERR(mdata_buf)) { + dev_err(dev, "mdata_buf get VA failed\n"); + tee_shm_free(mdata_shm); + return PTR_ERR(mdata_buf); + } + memcpy(mdata_buf, metadata, size); + + param[1].u.memref.shm = mdata_shm; + param[1].u.memref.size = size; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + tee_shm_free(mdata_shm); + return ret ?: -EINVAL; + } + + if (ctx) + ctx->ptr = (void *)mdata_shm; + else + tee_shm_free(mdata_shm); + + return ret; +} + +static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_MEM_SETUP, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(addr), + .u.value.b = upper_32_bits(addr), + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T)) + +static void *qcom_pas_tee_get_rsc_table(struct device *dev, + struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_GET_RESOURCE_TABLE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = ctx->pas_id, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref.size = input_rt_size, + } + }; + void *rt_buf = NULL; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size >= input_rt_size) { + struct tee_shm *rt_shm __free(shm_free) = + tee_shm_alloc_kernel_buf(data->ctx, + param[1].u.memref.size); + void *rt_shm_va; + + if (IS_ERR_OR_NULL(rt_shm)) { + dev_err(dev, "rt_shm allocation failed\n"); + rt_shm = NULL; + return ERR_PTR(-ENOMEM); + } + + rt_shm_va = tee_shm_get_va(rt_shm, 0); + if (IS_ERR(rt_shm_va)) { + dev_err(dev, "rt_shm get VA failed\n"); + return ERR_CAST(rt_shm_va); + } + memcpy(rt_shm_va, input_rt, input_rt_size); + + param[1].u.memref.shm = rt_shm; + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size) { + *output_rt_size = param[1].u.memref.size; + rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL); + if (!rt_buf) + return ERR_PTR(-ENOMEM); + } + } else { + *output_rt_size = 0; + } + + return rt_buf; +} + +static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id, + phys_addr_t mem_phys, size_t mem_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_AUTH_AND_RESET, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = mem_size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(mem_phys), + .u.value.b = upper_32_bits(mem_phys), + }, + /* Reserved for fw memory space to be shared or lent */ + [2] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id) +{ + return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0); +} + +static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) +{ + return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys, + ctx->mem_size); +} + +static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SET_REMOTE_STATE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = state, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SHUTDOWN, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static void qcom_pas_tee_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) +{ + struct tee_shm *mdata_shm = ctx->ptr; + + tee_shm_free(mdata_shm); + ctx->ptr = NULL; +} + +static struct qcom_pas_ops qcom_pas_ops_tee = { + .drv_name = QCOM_PAS_BACKEND_TEE, + .supported = qcom_pas_tee_supported, + .init_image = qcom_pas_tee_init_image, + .mem_setup = qcom_pas_tee_mem_setup, + .get_rsc_table = qcom_pas_tee_get_rsc_table, + .auth_and_reset = qcom_pas_tee_auth_and_reset, + .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset, + .set_remote_state = qcom_pas_tee_set_remote_state, + .shutdown = qcom_pas_tee_shutdown, + .metadata_release = qcom_pas_tee_metadata_release, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE; +} + +static int qcom_pas_tee_probe(struct device *dev) +{ + struct tee_client_device *pas_dev = to_tee_client_device(dev); + struct qcom_pas_tee_private *data; + struct tee_ioctl_open_session_arg sess_arg = { + .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL + }; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(data->ctx)) + return -ENODEV; + + export_uuid(sess_arg.uuid, &pas_dev->id.uuid); + ret = tee_client_open_session(data->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n", + ret, sess_arg.ret); + tee_client_close_context(data->ctx); + return ret ?: -EINVAL; + } + + data->session_id = sess_arg.session; + dev_set_drvdata(dev, data); + qcom_pas_ops_tee.dev = dev; + qcom_pas_ops_register(&qcom_pas_ops_tee); + + return ret; +} + +static int qcom_pas_tee_remove(struct device *dev) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + + qcom_pas_ops_unregister(); + tee_client_close_session(data->ctx, data->session_id); + tee_client_close_context(data->ctx); + + return 0; +} + +static const struct tee_client_device_id qcom_pas_tee_id_table[] = { + {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784, + 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)}, + {} +}; +MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table); + +static struct tee_client_driver optee_pas_tee_driver = { + .id_table = qcom_pas_tee_id_table, + .driver = { + .name = "qcom-pas-tee", + .probe = qcom_pas_tee_probe, + .remove = qcom_pas_tee_remove, + }, +}; + +module_tee_client_driver(optee_pas_tee_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm PAS TEE driver"); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 18ca9a22ffe99..40adc49293b17 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include +#include "qcom_pas.h" #include "qcom_scm.h" #include "qcom_tzmem.h" @@ -470,25 +472,6 @@ void qcom_scm_cpu_power_down(u32 flags) } EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); -int qcom_scm_set_remote_state(u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = state, - .args[1] = id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); - static int qcom_scm_disable_sdi(void) { int ret; @@ -561,26 +544,12 @@ static void qcom_scm_set_download_mode(u32 dload_mode) dev_err(__scm->dev, "failed to set download mode: %d\n", ret); } -/** - * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service - * context for a given peripheral - * - * PAS context is device-resource managed, so the caller does not need - * to worry about freeing the context memory. - * - * @dev: PAS firmware device - * @pas_id: peripheral authentication service id - * @mem_phys: Subsystem reserve memory start address - * @mem_size: Subsystem reserve memory size - * - * Returns: The new PAS context, or ERR_PTR() on failure. - */ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, u32 pas_id, phys_addr_t mem_phys, size_t mem_size) { - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -591,11 +560,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, ctx->mem_phys = mem_phys; ctx->mem_size = mem_size; - return ctx; + return (struct qcom_scm_pas_context *)ctx; } EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); -static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, +static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id, + dma_addr_t mdata_phys, struct qcom_scm_res *res) { struct qcom_scm_desc desc = { @@ -617,7 +587,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, desc.args[1] = mdata_phys; - ret = qcom_scm_call(__scm->dev, &desc, res); + ret = qcom_scm_call(dev, &desc, res); qcom_scm_bw_disable(); disable_clk: @@ -626,7 +596,8 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, return ret; } -static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, +static int qcom_scm_pas_prep_and_init_image(struct device *dev, + struct qcom_pas_context *ctx, const void *metadata, size_t size) { struct qcom_scm_res res; @@ -641,7 +612,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, memcpy(mdata_buf, metadata, size); mdata_phys = qcom_tzmem_to_phys(mdata_buf); - ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res); if (ret < 0) qcom_tzmem_free(mdata_buf); else @@ -650,25 +621,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, return ret ? : res.result[0]; } -/** - * qcom_scm_pas_init_image() - Initialize peripheral authentication service - * state machine for a given peripheral, using the - * metadata - * @pas_id: peripheral authentication service id - * @metadata: pointer to memory containing ELF header, program header table - * and optional blob of data used for authenticating the metadata - * and the rest of the firmware - * @size: size of the metadata - * @ctx: optional pas context - * - * Return: 0 on success. - * - * Upon successful return, the PAS metadata context (@ctx) will be used to - * track the metadata allocation, this needs to be released by invoking - * qcom_scm_pas_metadata_release() by the caller. - */ -int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_context *ctx) +static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) { struct qcom_scm_res res; dma_addr_t mdata_phys; @@ -676,7 +631,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, int ret; if (ctx && ctx->use_tzmem) - return qcom_scm_pas_prep_and_init_image(ctx, metadata, size); + return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata, size); /* * During the scm call memory protection will be enabled for the meta @@ -690,16 +645,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, * If we pass a buffer that is already part of an SHM Bridge to this * call, it will fail. */ - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, - GFP_KERNEL); + mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL); if (!mdata_buf) return -ENOMEM; memcpy(mdata_buf, metadata, size); - ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res); if (ret < 0 || !ctx) { - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + dma_free_coherent(dev, size, mdata_buf, mdata_phys); } else if (ctx) { ctx->ptr = mdata_buf; ctx->phys = mdata_phys; @@ -708,36 +662,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, return ret ? : res.result[0]; } -EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); -/** - * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: pas context - */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_scm_pas_context *ctx) { - if (!ctx->ptr) - return; + return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size, + (struct qcom_pas_context *)ctx); +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); +static void __qcom_scm_pas_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) +{ if (ctx->use_tzmem) qcom_tzmem_free(ctx->ptr); else - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); + dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys); ctx->ptr = NULL; } + +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +{ + __qcom_scm_pas_metadata_release(__scm->dev, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); -/** - * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral - * for firmware loading - * @pas_id: peripheral authentication service id - * @addr: start address of memory area to prepare - * @size: size of the memory area to prepare - * - * Returns 0 on success. - */ -int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) { int ret; struct qcom_scm_desc desc = { @@ -759,7 +712,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -767,9 +720,15 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) return ret ? : res.result[0]; } + +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); -static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, +static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id, + void *input_rt_tzm, size_t input_rt_size, size_t *output_rt_size) { @@ -804,7 +763,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, * with output_rt_tzm buffer with res.result[2] size however, It should not * be of unresonable size. */ - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); if (!ret && res.result[2] > SZ_1G) { ret = -E2BIG; goto free_output_rt; @@ -821,51 +780,11 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, return ret ? ERR_PTR(ret) : output_rt_tzm; } -/** - * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer - * for a given peripheral. - * - * Qualcomm remote processor may rely on both static and dynamic resources for - * its functionality. Static resources typically refer to memory-mapped addresses - * required by the subsystem and are often embedded within the firmware binary - * and dynamic resources, such as shared memory in DDR etc., are determined at - * runtime during the boot process. - * - * On Qualcomm Technologies devices, it's possible that static resources are not - * embedded in the firmware binary and instead are provided by TrustZone However, - * dynamic resources are always expected to come from TrustZone. This indicates - * that for Qualcomm devices, all resources (static and dynamic) will be provided - * by TrustZone via the SMC call. - * - * If the remote processor firmware binary does contain static resources, they - * should be passed in input_rt. These will be forwarded to TrustZone for - * authentication. TrustZone will then append the dynamic resources and return - * the complete resource table in output_rt_tzm. - * - * If the remote processor firmware binary does not include a resource table, - * the caller of this function should set input_rt as NULL and input_rt_size - * as zero respectively. - * - * More about documentation on resource table data structures can be found in - * include/linux/remoteproc.h - * - * @ctx: PAS context - * @pas_id: peripheral authentication service id - * @input_rt: resource table buffer which is present in firmware binary - * @input_rt_size: size of the resource table present in firmware binary - * @output_rt_size: TrustZone expects caller should pass worst case size for - * the output_rt_tzm. - * - * Return: - * On success, returns a pointer to the allocated buffer containing the final - * resource table and output_rt_size will have actual resource table size from - * TrustZone. The caller is responsible for freeing the buffer. On failure, - * returns ERR_PTR(-errno). - */ -struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, - void *input_rt, - size_t input_rt_size, - size_t *output_rt_size) +static void *__qcom_scm_pas_get_rsc_table2(struct device *dev, + struct qcom_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) { struct resource_table empty_rsc = {}; size_t size = SZ_16K; @@ -900,11 +819,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c memcpy(input_rt_tzm, input_rt, input_rt_size); - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id, + input_rt_tzm, input_rt_size, &size); if (PTR_ERR(output_rt_tzm) == -EOVERFLOW) /* Try again with the size requested by the TZ */ - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id, input_rt_tzm, input_rt_size, &size); @@ -935,16 +855,20 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c return ret ? ERR_PTR(ret) : tbl_ptr; } + +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + return __qcom_scm_pas_get_rsc_table2(__scm->dev, + (struct qcom_pas_context *)ctx, + input_rt, input_rt_size, + output_rt_size); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table); -/** - * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware - * and reset the remote processor - * @pas_id: peripheral authentication service id - * - * Return 0 on success. - */ -int qcom_scm_pas_auth_and_reset(u32 pas_id) +static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -964,7 +888,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -972,28 +896,15 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id) return ret ? : res.result[0]; } + +int qcom_scm_pas_auth_and_reset(u32 pas_id) +{ + return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); -/** - * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the - * remote processor - * - * @ctx: Context saved during call to qcom_scm_pas_context_init() - * - * This function performs the necessary steps to prepare a PAS subsystem, - * authenticate it using the provided metadata, and initiate a reset sequence. - * - * It should be used when Linux is in control setting up the IOMMU hardware - * for remote subsystem during secure firmware loading processes. The preparation - * step sets up a shmbridge over the firmware memory before TrustZone accesses the - * firmware memory region for authentication. The authentication step verifies - * the integrity and authenticity of the firmware or configuration using secure - * metadata. Finally, the reset step ensures the subsystem starts in a clean and - * sane state. - * - * Return: 0 on success, negative errno on failure. - */ -int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) { u64 handle; int ret; @@ -1004,7 +915,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) * memory region and then invokes a call to TrustZone to authenticate. */ if (!ctx->use_tzmem) - return qcom_scm_pas_auth_and_reset(ctx->pas_id); + return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); /* * When Linux runs @ EL2 Linux must create the shmbridge itself and then @@ -1014,20 +925,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) if (ret) return ret; - ret = qcom_scm_pas_auth_and_reset(ctx->pas_id); + ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); qcom_tzmem_shm_bridge_delete(handle); return ret; } + +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +{ + return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset); -/** - * qcom_scm_pas_shutdown() - Shut down the remote processor - * @pas_id: peripheral authentication service id - * - * Returns 0 on success. - */ -int qcom_scm_pas_shutdown(u32 pas_id) +static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = state, + .args[1] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(dev, &desc, &res); + + return ret ? : res.result[0]; +} + +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + return __qcom_scm_pas_set_remote_state(__scm->dev, state, id); +} +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); + +static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1047,7 +983,7 @@ int qcom_scm_pas_shutdown(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -1055,16 +991,14 @@ int qcom_scm_pas_shutdown(u32 pas_id) return ret ? : res.result[0]; } + +int qcom_scm_pas_shutdown(u32 pas_id) +{ + return __qcom_scm_pas_shutdown(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); -/** - * qcom_scm_pas_supported() - Check if the peripheral authentication service is - * available for the given peripherial - * @pas_id: peripheral authentication service id - * - * Returns true if PAS is supported for this peripheral, otherwise false. - */ -bool qcom_scm_pas_supported(u32 pas_id) +static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1076,16 +1010,49 @@ bool qcom_scm_pas_supported(u32 pas_id) }; struct qcom_scm_res res; - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PIL_PAS_IS_SUPPORTED)) return false; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); return ret ? false : !!res.result[0]; } + +bool qcom_scm_pas_supported(u32 pas_id) +{ + return __qcom_scm_pas_supported(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); +static struct qcom_pas_ops qcom_pas_ops_scm = { + .drv_name = QCOM_PAS_BACKEND_SCM, + .supported = __qcom_scm_pas_supported, + .init_image = __qcom_scm_pas_init_image2, + .mem_setup = __qcom_scm_pas_mem_setup, + .get_rsc_table = __qcom_scm_pas_get_rsc_table2, + .auth_and_reset = __qcom_scm_pas_auth_and_reset, + .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset, + .set_remote_state = __qcom_scm_pas_set_remote_state, + .shutdown = __qcom_scm_pas_shutdown, + .metadata_release = __qcom_scm_pas_metadata_release, +}; + +/** + * qcom_scm_is_pas_available() - Check if the peripheral authentication service + * is available via SCM or not + * + * Returns true if PAS is available, otherwise false. + */ +static bool qcom_scm_is_pas_available(void) +{ + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PIL_PAS_AUTH_AND_RESET)) + return false; + + return true; +} + static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) { struct qcom_scm_desc desc = { @@ -2803,6 +2770,11 @@ static int qcom_scm_probe(struct platform_device *pdev) __get_convention(); + if (qcom_scm_is_pas_available()) { + qcom_pas_ops_scm.dev = scm->dev; + qcom_pas_ops_register(&qcom_pas_ops_scm); + } + /* * If "download mode" is requested, from this point on warmboot * will cause the boot stages to enter download mode, unless @@ -2842,6 +2814,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev) { /* Clean shutdown, disable download mode to allow normal restart */ qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP); + qcom_pas_ops_unregister(); } static const struct of_device_id qcom_scm_dt_match[] = { diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 250246f81ea94..09469d56513b0 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -21,6 +21,7 @@ config DRM_MSM select SHMEM select TMPFS select QCOM_SCM + select QCOM_PAS select QCOM_UBWC_CONFIG select WANT_DEV_COREDUMP select SND_SOC_HDMI_CODEC if SND_SOC diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 0c9700cd33516..ae287137dcda6 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -653,7 +653,7 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu) if (adreno_is_a506(adreno_gpu)) return 0; - ret = qcom_scm_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID); + ret = qcom_pas_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID); if (ret) DRM_ERROR("%s: zap-shader resume failed: %d\n", gpu->name, ret); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 0873c14642bca..68987081d1b8b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -51,6 +52,12 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname, return -ENODEV; } + /* We need PAS to be able to load the firmware */ + if (!qcom_pas_is_available()) { + DRM_DEV_ERROR(dev, "PAS is not available\n"); + return -EPROBE_DEFER; + } + ret = of_reserved_mem_region_to_resource(np, 0, &r); if (ret) { zap_available = false; @@ -146,10 +153,10 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname, goto out; /* Send the image to the secure world */ - ret = qcom_scm_pas_auth_and_reset(pasid); + ret = qcom_pas_auth_and_reset(pasid); /* - * If the scm call returns -EOPNOTSUPP we assume that this target + * If the pas call returns -EOPNOTSUPP we assume that this target * doesn't need/support the zap shader so quietly fail */ if (ret == -EOPNOTSUPP) @@ -169,18 +176,11 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname, int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - struct platform_device *pdev = gpu->pdev; /* Short cut if we determine the zap shader isn't available/needed */ if (!zap_available) return -ENODEV; - /* We need SCM to be able to load the firmware */ - if (!qcom_scm_is_available()) { - DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n"); - return -EPROBE_DEFER; - } - return zap_shader_load_mdt(gpu, adreno_gpu->info->zapfw, pasid); } diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index af78a1775937b..388c9bbc81365 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -1,14 +1,15 @@ config VIDEO_QCOM_IRIS - tristate "Qualcomm iris V4L2 decoder driver" - depends on VIDEO_DEV - depends on ARCH_QCOM || COMPILE_TEST - select V4L2_MEM2MEM_DEV - select QCOM_MDT_LOADER - select QCOM_SCM - select QCOM_UBWC_CONFIG - select VIDEOBUF2_DMA_CONTIG - help - This is a V4L2 driver for Qualcomm iris video accelerator - hardware. It accelerates decoding operations on various - Qualcomm SoCs. - To compile this driver as a module choose m here. + tristate "Qualcomm iris V4L2 decoder driver" + depends on VIDEO_DEV + depends on ARCH_QCOM || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select QCOM_MDT_LOADER + select QCOM_SCM + select QCOM_PAS + select QCOM_UBWC_CONFIG + select VIDEOBUF2_DMA_CONTIG + help + This is a V4L2 driver for Qualcomm iris video accelerator + hardware. It accelerates decoding operations on various + Qualcomm SoCs. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index 4e0f16c565d3e..9c0f371dbb297 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -94,7 +94,7 @@ struct iris_core { struct video_device *vdev_enc; struct video_firmware { struct device *dev; - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; struct iommu_domain *iommu_domain; } fw; const struct v4l2_file_operations *iris_v4l2_file_ops; diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 06fbf8db1bb6a..d0cf7e7f12718 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include "iris_core.h" #include "iris_firmware.h" @@ -100,7 +102,7 @@ static const struct firmware *iris_detect_firmware(struct iris_core *core, static int iris_load_fw_to_memory(struct iris_core *core) { - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; const struct firmware *firmware = NULL; struct device *dev = core->dev; struct resource res; @@ -119,7 +121,7 @@ static int iris_load_fw_to_memory(struct iris_core *core) dev = core->fw.dev ? : core->dev; - ctx = devm_qcom_scm_pas_context_alloc(dev, IRIS_PAS_ID, mem_phys, res_size); + ctx = devm_qcom_pas_context_alloc(dev, IRIS_PAS_ID, mem_phys, res_size); if (!ctx) return -ENOMEM; @@ -138,7 +140,7 @@ static int iris_load_fw_to_memory(struct iris_core *core) } ret = qcom_mdt_pas_load(ctx, firmware, fw_name, NULL); - qcom_scm_pas_metadata_release(ctx); + qcom_pas_metadata_release(ctx); if (ret) goto err_release_fw; @@ -149,7 +151,7 @@ static int iris_load_fw_to_memory(struct iris_core *core) goto err_release_fw; } - ret = qcom_scm_pas_prepare_and_auth_reset(ctx); + ret = qcom_pas_prepare_and_auth_reset(ctx); if (ret) goto err_iommu_unmap; @@ -168,6 +170,7 @@ static int iris_load_fw_to_memory(struct iris_core *core) int iris_fw_load(struct iris_core *core) { const struct tz_cp_config *cp_config; + const char *pas_backend; int i, ret; ret = iris_load_fw_to_memory(core); @@ -176,6 +179,15 @@ int iris_fw_load(struct iris_core *core) return ret; } + /* + * qcom_scm_mem_protect_video_var() only applies to the SCM backend; + * other backends (e.g. OP-TEE) own secure memory protection and do + * not service this call. + */ + pas_backend = qcom_pas_get_backend(); + if (!pas_backend || strcmp(pas_backend, QCOM_PAS_BACKEND_SCM)) + return 0; + for (i = 0; i < core->iris_platform_data->tz_cp_config_data_size; i++) { cp_config = &core->iris_platform_data->tz_cp_config_data[i]; ret = qcom_scm_mem_protect_video_var(cp_config->cp_start, @@ -194,13 +206,13 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - struct qcom_scm_pas_context *ctx = core->fw.ctx; + struct qcom_pas_context *ctx = core->fw.ctx; int ret; if (!ctx) return -EINVAL; - ret = qcom_scm_pas_shutdown(ctx->pas_id); + ret = qcom_pas_shutdown(ctx->pas_id); if (core->fw.iommu_domain) iommu_unmap(core->fw.iommu_domain, 0, ctx->mem_size); @@ -210,7 +222,7 @@ int iris_fw_unload(struct iris_core *core) int iris_set_hw_state(struct iris_core *core, bool resume) { - return qcom_scm_set_remote_state(resume, 0); + return qcom_pas_set_remote_state(resume, 0); } int iris_fw_init(struct iris_core *core) diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index 63ee8c78dc6d7..7997b8aa427a0 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -6,6 +6,7 @@ config VIDEO_QCOM_VENUS select OF_DYNAMIC if ARCH_QCOM select QCOM_MDT_LOADER select QCOM_SCM + select QCOM_PAS select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index c46f62c9f3f3c..c7b3d1ba991b5 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -218,7 +218,7 @@ struct venus_core { size_t mapped_mem_size; phys_addr_t mem_phys; size_t mem_size; - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; bool iommu_domain_owned; } fw; struct mutex lock; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index dbfb322a99978..e8a5d757adaa9 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include #include "core.h" @@ -59,7 +61,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume) int ret; if (core->use_tz) { - ret = qcom_scm_set_remote_state(resume, 0); + ret = qcom_pas_set_remote_state(resume, 0); if (resume && ret == -EINVAL) ret = 0; return ret; @@ -124,12 +126,12 @@ static int venus_load_fw(struct venus_core *core, const struct firmware *mdt, const char *fwname, phys_addr_t mem_phys, size_t res_size) { - struct qcom_scm_pas_context *ctx; + struct qcom_pas_context *ctx; struct device *dev; int ret; dev = core->fw.dev ? core->fw.dev : core->dev; - ctx = devm_qcom_scm_pas_context_alloc(dev, VENUS_PAS_ID, mem_phys, res_size); + ctx = devm_qcom_pas_context_alloc(dev, VENUS_PAS_ID, mem_phys, res_size); if (!ctx) { dev_err(core->dev, "%s: ctx is null\n", __func__); return -ENOMEM; @@ -138,7 +140,7 @@ static int venus_load_fw(struct venus_core *core, ctx->use_tzmem = !!core->fw.dev; ret = qcom_mdt_pas_load(ctx, mdt, fwname, NULL); - qcom_scm_pas_metadata_release(ctx); + qcom_pas_metadata_release(ctx); if (ret) { dev_err(core->dev, "%s: qcom_mdt_pas_load: %d\n", __func__, ret); return ret; @@ -155,9 +157,9 @@ static int venus_load_fw(struct venus_core *core, core->fw.mapped_mem_size = res_size; - ret = qcom_scm_pas_prepare_and_auth_reset(ctx); + ret = qcom_pas_prepare_and_auth_reset(ctx); if (ret) { - dev_err(core->dev, "%s: qcom_scm_pas_prepare_and_auth_reset: %d\n", __func__, ret); + dev_err(core->dev, "%s: qcom_pas_prepare_and_auth_reset: %d\n", __func__, ret); if (core->fw.iommu_domain) iommu_unmap(core->fw.iommu_domain, 0, res_size); core->fw.mapped_mem_size = 0; @@ -253,6 +255,7 @@ int venus_boot(struct venus_core *core) const struct venus_resources *res = core->res; const struct firmware *mdt; const char *fwpath = NULL; + const char *pas_backend; phys_addr_t mem_phys; size_t res_size; int ret; @@ -282,6 +285,14 @@ int venus_boot(struct venus_core *core) } if (core->use_tz && res->cp_size) { + /* + * qcom_scm_mem_protect_video_var() only applies to the SCM + * backend; other backends (e.g. OP-TEE) own secure memory + * protection and do not service this call. + */ + pas_backend = qcom_pas_get_backend(); + if (!pas_backend || strcmp(pas_backend, QCOM_PAS_BACKEND_SCM)) + return ret; /* * Clues for porting using downstream data: * cp_start = 0 @@ -311,7 +322,7 @@ int venus_shutdown(struct venus_core *core) int ret; if (core->use_tz) { - ret = qcom_scm_pas_shutdown(VENUS_PAS_ID); + ret = qcom_pas_shutdown(VENUS_PAS_ID); if (core->fw.iommu_domain && core->fw.mapped_mem_size) { iommu_unmap(core->fw.iommu_domain, 0, core->fw.mapped_mem_size); core->fw.mapped_mem_size = 0; @@ -410,8 +421,8 @@ static int venus_firmware_init_auto_detect(struct venus_core *core) int ret; core->use_tz = false; - if (qcom_scm_is_available()) { - if (qcom_scm_pas_supported(VENUS_PAS_ID)) + if (qcom_pas_is_available()) { + if (qcom_pas_supported(VENUS_PAS_ID)) core->use_tz = true; } else { ret = driver_deferred_probe_check_state(core->dev); diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index ca4ee864f5ca5..ed0169f72ff7d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1909,14 +1909,14 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, return 0; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); + dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; + return 0; } - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); - msm_host->ice = ice; /* Initialize the blk_crypto_profile */ diff --git a/drivers/net/ipa/Kconfig b/drivers/net/ipa/Kconfig index 01d219d3760c8..a9aff1b7977de 100644 --- a/drivers/net/ipa/Kconfig +++ b/drivers/net/ipa/Kconfig @@ -6,7 +6,7 @@ config QCOM_IPA depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST) depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n select QCOM_MDT_LOADER - select QCOM_SCM + select QCOM_PAS select QCOM_QMI_HELPERS help Choose Y or M here to include support for the Qualcomm diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 30fe12e3582b2..a7f4a6cceacac 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include "ipa.h" @@ -624,10 +624,13 @@ static int ipa_firmware_load(struct device *dev) } ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL); - if (ret) + if (ret) { dev_err(dev, "error %d loading \"%s\"\n", ret, path); - else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID))) - dev_err(dev, "error %d authenticating \"%s\"\n", ret, path); + } else { + ret = qcom_pas_auth_and_reset(IPA_PAS_ID); + if (ret) + dev_err(dev, "error %d authenticating \"%s\"\n", ret, path); + } memunmap(virt); out_release_firmware: @@ -754,7 +757,7 @@ static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) return IPA_LOADER_INVALID; out_self: /* We need Trust Zone to load firmware; make sure it's available */ - if (qcom_scm_is_available()) + if (qcom_pas_is_available()) return IPA_LOADER_SELF; return IPA_LOADER_DEFER; diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48a0d3a69ed08..6c745f31bdb9d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -208,6 +208,7 @@ config QCOM_Q6V5_MSS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the Qualcomm self-authenticating modem subsystem based on Hexagon V5. The TrustZone based system is @@ -228,6 +229,7 @@ config QCOM_Q6V5_PAS select QCOM_Q6V5_COMMON select QCOM_RPROC_COMMON select QCOM_SCM + select QCOM_PAS help Say y here to support the TrustZone based Peripheral Image Loader for the Qualcomm remote processors. This is commonly used to control @@ -280,7 +282,7 @@ config QCOM_WCNSS_PIL select QCOM_MDT_LOADER select QCOM_PIL_INFO select QCOM_RPROC_COMMON - select QCOM_SCM + select QCOM_PAS help Say y here to support the Peripheral Image Loader for loading WCNSS firmware and boot the core on e.g. MSM8974, MSM8916. The firmware is diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index e4f25be4f6438..0feef66964779 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -34,6 +34,7 @@ #include "qcom_pil_info.h" #include "qcom_q6v5.h" +#include #include #define MPSS_CRASH_REASON_SMEM 421 @@ -1441,7 +1442,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } if (qproc->version == MSS_MSM8953) { - ret = qcom_scm_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); + ret = qcom_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); if (ret) { dev_err(qproc->dev, "setting up mpss memory failed: %d\n", ret); @@ -2059,9 +2060,18 @@ static int q6v5_probe(struct platform_device *pdev) if (!desc) return -EINVAL; + /* + * Memory protection is done through qcom_scm_assign_mem(), which needs + * SCM but not PAS. Only the memory setup path issues PAS calls, so + * requiring PAS for every need_mem_protection platform prevents the + * modem from probing at all on TZ firmware that offers no PAS. + */ if (desc->need_mem_protection && !qcom_scm_is_available()) return -EPROBE_DEFER; + if (desc->version == MSS_MSM8953 && !qcom_pas_is_available()) + return -EPROBE_DEFER; + mba_image = desc->hexagon_mba_image; ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", 0, &mba_image); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index ec56e6638a442..222c792161f1a 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -121,8 +122,8 @@ struct qcom_pas { struct qcom_sysmon *sysmon; struct platform_device *bam_dmux; - struct qcom_scm_pas_context *pas_ctx; - struct qcom_scm_pas_context *dtb_pas_ctx; + struct qcom_pas_context *pas_ctx; + struct qcom_pas_context *dtb_pas_ctx; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -220,7 +221,7 @@ static int qcom_pas_shutdown_poll_decrypt(struct qcom_pas *pas) do { msleep(QCOM_PAS_DECRYPT_SHUTDOWN_DELAY_MS); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); } while (ret == -EINVAL && --retry_num); return ret; @@ -236,9 +237,9 @@ static int qcom_pas_unprepare(struct rproc *rproc) * auth_and_reset() was successful, but in other cases clean it up * here. */ - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); return 0; } @@ -252,9 +253,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) pas->firmware = fw; if (pas->lite_pas_id) - qcom_scm_pas_shutdown(pas->lite_pas_id); + qcom_pas_shutdown(pas->lite_pas_id); if (pas->lite_dtb_pas_id) - qcom_scm_pas_shutdown(pas->lite_dtb_pas_id); + qcom_pas_shutdown(pas->lite_dtb_pas_id); if (pas->dtb_pas_id) { ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev); @@ -275,7 +276,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) release_dtb_metadata: if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); release_firmware(pas->dtb_firmware); @@ -336,7 +337,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto disable_px_supply; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->dtb_pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate dtb image and release reset\n"); @@ -355,7 +356,7 @@ static int qcom_pas_start(struct rproc *rproc) if (ret) goto release_pas_metadata; - ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx); + ret = qcom_pas_prepare_and_auth_reset(pas->pas_ctx); if (ret) { dev_err(pas->dev, "failed to authenticate image and release reset\n"); @@ -365,13 +366,13 @@ static int qcom_pas_start(struct rproc *rproc) ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(pas->dev, "start timed out\n"); - qcom_scm_pas_shutdown(pas->pas_id); + qcom_pas_shutdown(pas->pas_id); goto unmap_carveout; } - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); /* firmware is used to pass reference from qcom_pas_start(), drop it now */ pas->firmware = NULL; @@ -381,9 +382,9 @@ static int qcom_pas_start(struct rproc *rproc) unmap_carveout: qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); release_pas_metadata: - qcom_scm_pas_metadata_release(pas->pas_ctx); + qcom_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + qcom_pas_metadata_release(pas->dtb_pas_ctx); unmap_dtb_carveout: if (pas->dtb_pas_id) @@ -432,7 +433,7 @@ static int qcom_pas_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id); if (ret && pas->decrypt_shutdown) ret = qcom_pas_shutdown_poll_decrypt(pas); @@ -440,7 +441,7 @@ static int qcom_pas_stop(struct rproc *rproc) dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + ret = qcom_pas_shutdown(pas->dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); @@ -510,11 +511,11 @@ static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *f * * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources * or not and if it is not having then we pass NULL and zero as input resource - * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table() + * table pointer and size respectively to the argument of qcom_pas_get_rsc_table() * and this is even true for Qualcomm remote processor who does follow remoteproc * framework. */ - output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); + output_rt = qcom_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size); ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0; if (ret) { dev_err(pas->dev, "Error in getting resource table: %d\n", ret); @@ -685,8 +686,8 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas) pas->mem_phys = pas->mem_reloc = rmem->base; pas->mem_size = rmem->size; - pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id, - pas->mem_phys, pas->mem_size); + pas->pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->pas_id, + pas->mem_phys, pas->mem_size); if (IS_ERR(pas->pas_ctx)) return PTR_ERR(pas->pas_ctx); @@ -709,9 +710,9 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas) pas->dtb_mem_phys = pas->dtb_mem_reloc = rmem->base; pas->dtb_mem_size = rmem->size; - pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id, - pas->dtb_mem_phys, - pas->dtb_mem_size); + pas->dtb_pas_ctx = devm_qcom_pas_context_alloc(pas->dev, pas->dtb_pas_id, + pas->dtb_mem_phys, + pas->dtb_mem_size); if (IS_ERR(pas->dtb_pas_ctx)) return PTR_ERR(pas->dtb_pas_ctx); @@ -810,7 +811,7 @@ static int qcom_pas_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; fw_name = desc->firmware_name; diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 2c7e519a2254b..1a98ddb62dcc7 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -257,7 +257,7 @@ static int wcnss_start(struct rproc *rproc) wcnss_indicate_nv_download(wcnss); wcnss_configure_iris(wcnss); - ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); + ret = qcom_pas_auth_and_reset(WCNSS_PAS_ID); if (ret) { dev_err(wcnss->dev, "failed to authenticate image and release reset\n"); @@ -269,7 +269,7 @@ static int wcnss_start(struct rproc *rproc) if (wcnss->ready_irq > 0 && ret == 0) { /* We have a ready_irq, but it didn't fire in time. */ dev_err(wcnss->dev, "start timed out\n"); - qcom_scm_pas_shutdown(WCNSS_PAS_ID); + qcom_pas_shutdown(WCNSS_PAS_ID); ret = -ETIMEDOUT; goto disable_iris; } @@ -311,7 +311,7 @@ static int wcnss_stop(struct rproc *rproc) 0); } - ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); + ret = qcom_pas_shutdown(WCNSS_PAS_ID); if (ret) dev_err(wcnss->dev, "failed to shutdown: %d\n", ret); @@ -562,10 +562,10 @@ static int wcnss_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); - if (!qcom_scm_is_available()) + if (!qcom_pas_is_available()) return -EPROBE_DEFER; - if (!qcom_scm_pas_supported(WCNSS_PAS_ID)) { + if (!qcom_pas_supported(WCNSS_PAS_ID)) { dev_err(&pdev->dev, "PAS is not available for WCNSS\n"); return -ENXIO; } diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 9683f59b61e4b..f75e5e6b2ccaf 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -626,7 +626,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!qcom_scm_ice_available()) { dev_warn(dev, "ICE SCM interface not found\n"); - return NULL; + return ERR_PTR(-EOPNOTSUPP); } engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); @@ -714,7 +714,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return NULL; + return ERR_PTR(-EOPNOTSUPP); pdev = of_find_device_by_node(node); if (!pdev) { @@ -767,8 +767,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res) * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * - * Return: ICE pointer on success, NULL if there is no ICE data provided by the - * consumer or ERR_PTR() on error. + * Return: ICE pointer on success, ERR_PTR() on error. */ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) { @@ -779,7 +778,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) return ERR_PTR(-ENOMEM); ice = of_qcom_ice_get(dev); - if (!IS_ERR_OR_NULL(ice)) { + if (!IS_ERR(ice)) { *dr = ice; devres_add(dev, dr); } else { diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 3936839da329e..24da9f8914538 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *ctx) + struct qcom_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -272,7 +272,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, goto out; } - ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx); + ret = qcom_pas_init_image(pas_id, metadata, metadata_len, ctx); kfree(metadata); if (ret) { /* Invalid firmware metadata */ @@ -281,7 +281,7 @@ static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, } if (relocate) { - ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); + ret = qcom_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); if (ret) { /* Unable to set up relocation */ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); @@ -481,7 +481,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); * firmware segments (e.g., .bXX files). Authentication of the segments done * by a separate call. * - * The PAS context must be initialized using qcom_scm_pas_context_init() + * The PAS context must be initialized using devm_qcom_pas_context_alloc() * prior to invoking this function. * * @ctx: Pointer to the PAS (Peripheral Authentication Service) context @@ -491,7 +491,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); * * Return: 0 on success or a negative error code on failure. */ -int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, +int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, phys_addr_t *reloc_base) { void *mem_region; diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d65d47cc154e3..51379f7fc5d54 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1405,6 +1405,22 @@ const struct bus_type tee_bus_type = { }; EXPORT_SYMBOL_GPL(tee_bus_type); +int __tee_client_driver_register(struct tee_client_driver *tee_driver, + struct module *owner) +{ + tee_driver->driver.owner = owner; + tee_driver->driver.bus = &tee_bus_type; + + return driver_register(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(__tee_client_driver_register); + +void tee_client_driver_unregister(struct tee_client_driver *tee_driver) +{ + driver_unregister(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(tee_client_driver_unregister); + static int __init tee_init(void) { int rc; diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index fde11f4dc11d1..420776f0e5afe 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -176,14 +176,14 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host) int i; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); + dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; + return 0; } - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); - host->ice = ice; /* Initialize the blk_crypto_profile */ diff --git a/include/linux/firmware/qcom/qcom_pas.h b/include/linux/firmware/qcom/qcom_pas.h new file mode 100644 index 0000000000000..65122c81e2915 --- /dev/null +++ b/include/linux/firmware/qcom/qcom_pas.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_H +#define __QCOM_PAS_H + +#include +#include + +/* Names reported by qcom_pas_get_backend() for the active PAS backend. */ +#define QCOM_PAS_BACKEND_SCM "qcom_scm" +#define QCOM_PAS_BACKEND_TEE "qcom-pas-tee" + +struct qcom_pas_context { + struct device *dev; + u32 pas_id; + phys_addr_t mem_phys; + size_t mem_size; + void *ptr; + dma_addr_t phys; + ssize_t size; + bool use_tzmem; +}; + +const char *qcom_pas_get_backend(void); +bool qcom_pas_is_available(void); +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size); +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_pas_context *ctx); +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); +int qcom_pas_auth_and_reset(u32 pas_id); +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx); +int qcom_pas_set_remote_state(u32 state, u32 pas_id); +int qcom_pas_shutdown(u32 pas_id); +bool qcom_pas_supported(u32 pas_id); +void qcom_pas_metadata_release(struct qcom_pas_context *ctx); + +#endif /* __QCOM_PAS_H */ diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 7c551b98e1822..74886f772d46d 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -10,7 +10,7 @@ struct device; struct firmware; -struct qcom_scm_pas_context; +struct qcom_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) @@ -20,7 +20,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); -int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, +int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, phys_addr_t *reloc_base); int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, @@ -45,7 +45,7 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, return -ENODEV; } -static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, +static inline int qcom_mdt_pas_load(struct qcom_pas_context *ctx, const struct firmware *fw, const char *firmware, phys_addr_t *reloc_base) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 88a6f9697c890..850c03b2cdeac 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -322,4 +322,13 @@ struct tee_client_driver { #define to_tee_client_driver(d) \ container_of_const(d, struct tee_client_driver, driver) +#define tee_client_driver_register(drv) \ + __tee_client_driver_register(drv, THIS_MODULE) +int __tee_client_driver_register(struct tee_client_driver *, struct module *); +void tee_client_driver_unregister(struct tee_client_driver *); + +#define module_tee_client_driver(__tee_client_driver) \ + module_driver(__tee_client_driver, tee_client_driver_register, \ + tee_client_driver_unregister) + #endif /*__TEE_DRV_H*/