summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Johnson <jeff.johnson@oss.qualcomm.com>2026-07-10 08:56:21 -0700
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>2026-07-10 09:57:25 -0700
commit8d0620d47dad641ab2874aa2e9b2803cd66f2118 (patch)
tree0d5e4830fcc583bbb82024c44a94cc003426c45b
parent913998f903fb1432c0046c33003db38a9e8bedb1 (diff)
parent6701259025d49139131a0eb2257659a066dcca22 (diff)
Merge qcom PAS TZ service series (20260702115835.167602-2-sumit.garg@kernel.org)
Bjorn Andersson says: > From: Sumit Garg <sumit.garg@oss.qualcomm.com> > > Qcom platforms has the legacy of using non-standard SCM calls > splintered over the various kernel drivers. These SCM calls aren't > compliant with the standard SMC calling conventions which is a > prerequisite to enable migration to the FF-A specifications from Arm. > > [...] Applied, thanks! [01/14] firmware: qcom: Add a generic PAS service commit: 08314e7c2c38b9ae6a5e01c58ed10a950859404d [02/14] firmware: qcom_scm: Migrate to generic PAS service commit: 5c1a2975d23c51c01aca51945d0f10a4ee4c9020 [03/14] firmware: qcom: Add a PAS TEE service commit: b6f7978da0c4d26fe465aa6634f5a0b48f900de0 [14/14] MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service commit: 6701259025d49139131a0eb2257659a066dcca22 This is available as an immutable branch, for other subsystems to pull at: https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git 20260702115835.167602-2-sumit.garg@kernel.org Link: https://lore.kernel.org/all/178362521364.2422497.1305957434056184382.b4-ty@kernel.org/
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/firmware/qcom/Kconfig22
-rw-r--r--drivers/firmware/qcom/Makefile2
-rw-r--r--drivers/firmware/qcom/qcom_pas.c298
-rw-r--r--drivers/firmware/qcom/qcom_pas.h50
-rw-r--r--drivers/firmware/qcom/qcom_pas_tee.c479
-rw-r--r--drivers/firmware/qcom/qcom_scm.c335
-rw-r--r--include/linux/firmware/qcom/qcom_pas.h43
8 files changed, 1056 insertions, 182 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..7847b2a98f90 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22315,6 +22315,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 <sumit.garg@oss.qualcomm.com>
+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 <casey.connolly@linaro.org>
L: linux-arm-msm@vger.kernel.org
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index b477d54b495a..c7f8413ab996 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 0be40a1abc13..48801d18f37b 100644
--- a/drivers/firmware/qcom/Makefile
+++ b/drivers/firmware/qcom/Makefile
@@ -8,3 +8,5 @@ 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
diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c
new file mode 100644
index 000000000000..24485dd0fa10
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas.c
@@ -0,0 +1,298 @@
+// 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 <linux/device/devres.h>
+#include <linux/firmware/qcom/qcom_pas.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#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");
diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h
new file mode 100644
index 000000000000..8643e2760602
--- /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 000000000000..ac33a00687aa
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas_tee.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/firmware/qcom/qcom_pas.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+
+#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-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 tee_client_device *pas_dev)
+{
+ struct device *dev = &pas_dev->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 void qcom_pas_tee_remove(struct tee_client_device *pas_dev)
+{
+ struct device *dev = &pas_dev->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);
+}
+
+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 = {
+ .probe = qcom_pas_tee_probe,
+ .remove = qcom_pas_tee_remove,
+ .id_table = qcom_pas_tee_id_table,
+ .driver = {
+ .name = "qcom-pas-tee",
+ },
+};
+
+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 6b601a4b89db..7933e55803dc 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -13,6 +13,7 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
+#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
#include <linux/init.h>
@@ -33,6 +34,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "qcom_pas.h"
#include "qcom_scm.h"
#include "qcom_tzmem.h"
@@ -479,25 +481,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;
@@ -570,26 +553,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)
@@ -600,11 +569,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 = {
@@ -626,7 +596,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:
@@ -635,7 +605,8 @@ disable_clk:
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;
@@ -650,7 +621,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
@@ -659,25 +630,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;
@@ -685,7 +640,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
@@ -699,16 +654,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;
@@ -717,36 +671,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 = {
@@ -768,7 +721,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:
@@ -776,9 +729,15 @@ disable_clk:
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)
{
@@ -813,7 +772,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;
@@ -830,51 +789,11 @@ free_output_rt:
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;
@@ -909,11 +828,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);
@@ -943,16 +863,20 @@ disable_clk:
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 = {
@@ -972,7 +896,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:
@@ -980,28 +904,15 @@ disable_clk:
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;
@@ -1012,7 +923,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
@@ -1022,20 +933,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 = {
@@ -1055,7 +991,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:
@@ -1063,16 +999,14 @@ disable_clk:
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 = {
@@ -1084,16 +1018,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_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 = {
@@ -2837,6 +2804,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
@@ -2876,6 +2848,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/include/linux/firmware/qcom/qcom_pas.h b/include/linux/firmware/qcom/qcom_pas.h
new file mode 100644
index 000000000000..65b1c9564458
--- /dev/null
+++ b/include/linux/firmware/qcom/qcom_pas.h
@@ -0,0 +1,43 @@
+/* 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 <linux/err.h>
+#include <linux/types.h>
+
+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;
+};
+
+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 */