// 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); /** * 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");