From: Sumit Garg <sumit.garg@oss.qualcomm.com>
Add support for Peripheral Authentication Service (PAS) driver based
on TEE bus with OP-TEE providing the backend PAS service implementation.
The TEE PAS service ABI is designed to be extensible with additional API
as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
extensions of the PAS service needed while still maintaining backwards
compatibility.
Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
drivers/firmware/qcom/Kconfig | 9 +
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_pas_tee.c | 477 +++++++++++++++++++++++++++
3 files changed, 487 insertions(+)
create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index 9a12ae2b639d..fff47abdaafd 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -14,6 +14,15 @@ config QCOM_PAS
backends plugged in whether it's an SCM implementation or a proper
TEE bus based PAS service implementation.
+config QCOM_PAS_TEE
+ tristate
+ select QCOM_PAS
+ depends on TEE
+ depends on !CPU_BIG_ENDIAN
+ help
+ Enable the generic Peripheral Authentication Service (PAS) provided
+ by the firmware TEE implementation as the backend.
+
config QCOM_SCM
select QCOM_PAS
select QCOM_TZMEM
diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
index dc5ab45f906a..48801d18f37b 100644
--- a/drivers/firmware/qcom/Makefile
+++ b/drivers/firmware/qcom/Makefile
@@ -9,3 +9,4 @@ 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_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c
new file mode 100644
index 000000000000..7db9fd736369
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_pas_tee.c
@@ -0,0 +1,477 @@
+// 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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_QCOM_PAS_SET_REMOTE_STATE 7
+
+/*
+ * PAS co-processor shutdown.
+ *
+ * [in] params[0].value.a: Unique 32bit remote processor identifier
+ */
+#define PTA_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 = PTA_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, err: %x\n",
+ pas_id, 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 = PTA_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, err: %x\n",
+ pas_id, inv_arg.ret);
+ tee_shm_free(mdata_shm);
+ return -EINVAL;
+ }
+ ctx->ptr = (void *)mdata_shm;
+
+ return 0;
+}
+
+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 = PTA_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, err: %x\n",
+ pas_id, inv_arg.ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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 = PTA_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, err: %x\n",
+ ctx->pas_id, inv_arg.ret);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (param[1].u.memref.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(rt_shm)) {
+ dev_err(dev, "rt_shm allocation failed\n");
+ return rt_shm;
+ }
+
+ rt_shm_va = tee_shm_get_va(rt_shm, 0);
+ if (IS_ERR_OR_NULL(rt_shm_va)) {
+ dev_err(dev, "rt_shm get VA failed\n");
+ return ERR_PTR(-EINVAL);
+ }
+ 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, err: %x\n",
+ ctx->pas_id, inv_arg.ret);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (param[1].u.memref.size) {
+ *output_rt_size = param[1].u.memref.size;
+ rt_buf = kmalloc(param[1].u.memref.size, GFP_KERNEL);
+ if (!rt_buf)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(rt_buf, rt_shm_va, *output_rt_size);
+ }
+ }
+
+ 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 = PTA_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, err: %x\n",
+ pas_id, inv_arg.ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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 = PTA_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 shutdown failed, pas_id: %d, err: %x\n",
+ pas_id, inv_arg.ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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 = PTA_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 = 0;
+
+ 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, err: %x\n",
+ pas_id, inv_arg.ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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);
+}
+
+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, err = -ENODEV;
+
+ 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, err: %x\n",
+ sess_arg.ret);
+ err = -EINVAL;
+ goto out_ctx;
+ }
+
+ 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 0;
+out_ctx:
+ tee_client_close_context(data->ctx);
+
+ return err;
+}
+
+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("TEE bus based Qualcomm PAS driver");
--
2.51.0
On Thu, Mar 12, 2026 at 11:57:45AM +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> Add support for Peripheral Authentication Service (PAS) driver based
> on TEE bus with OP-TEE providing the backend PAS service implementation.
>
> The TEE PAS service ABI is designed to be extensible with additional API
> as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
> extensions of the PAS service needed while still maintaining backwards
> compatibility.
>
> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> drivers/firmware/qcom/Kconfig | 9 +
> drivers/firmware/qcom/Makefile | 1 +
> drivers/firmware/qcom/qcom_pas_tee.c | 477 +++++++++++++++++++++++++++
> 3 files changed, 487 insertions(+)
> create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
>
> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> index 9a12ae2b639d..fff47abdaafd 100644
> --- a/drivers/firmware/qcom/Kconfig
> +++ b/drivers/firmware/qcom/Kconfig
> @@ -14,6 +14,15 @@ config QCOM_PAS
> backends plugged in whether it's an SCM implementation or a proper
> TEE bus based PAS service implementation.
>
> +config QCOM_PAS_TEE
> + tristate
> + select QCOM_PAS
> + depends on TEE
> + depends on !CPU_BIG_ENDIAN
> + help
> + Enable the generic Peripheral Authentication Service (PAS) provided
> + by the firmware TEE implementation as the backend.
> +
> config QCOM_SCM
> select QCOM_PAS
> select QCOM_TZMEM
> diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
> index dc5ab45f906a..48801d18f37b 100644
> --- a/drivers/firmware/qcom/Makefile
> +++ b/drivers/firmware/qcom/Makefile
> @@ -9,3 +9,4 @@ 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_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c
> new file mode 100644
> index 000000000000..7db9fd736369
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas_tee.c
> @@ -0,0 +1,477 @@
> +// 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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_QCOM_PAS_SET_REMOTE_STATE 7
> +
> +/*
> + * PAS co-processor shutdown.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + */
> +#define PTA_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 = PTA_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, err: %x\n",
> + pas_id, 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 = PTA_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, err: %x\n",
> + pas_id, inv_arg.ret);
> + tee_shm_free(mdata_shm);
> + return -EINVAL;
> + }
> + ctx->ptr = (void *)mdata_shm;
> +
> + return 0;
> +}
> +
> +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 = PTA_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, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +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 = PTA_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);
What is the purpose of this function ? looks like, this is for, how
much Linux need to allocate for output buffer ?
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS get RT failed, pas_id: %d, err: %x\n",
> + ctx->pas_id, inv_arg.ret);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (param[1].u.memref.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(rt_shm)) {
> + dev_err(dev, "rt_shm allocation failed\n");
> + return rt_shm;
> + }
> +
> + rt_shm_va = tee_shm_get_va(rt_shm, 0);
> + if (IS_ERR_OR_NULL(rt_shm_va)) {
> + dev_err(dev, "rt_shm get VA failed\n");
> + return ERR_PTR(-EINVAL);
> + }
> + memcpy(rt_shm_va, input_rt, input_rt_size);
> +
> + param[1].u.memref.shm = rt_shm;
Here, you are passing only one buffer for both input and output ?
Like, you are allocating of buffer of size returned from qtee which I
assume includes both input + output rt size and copying the input_rt
and calling invoke and in return you will get combine table in return ?
> + 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, err: %x\n",
> + ctx->pas_id, inv_arg.ret);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (param[1].u.memref.size) {
> + *output_rt_size = param[1].u.memref.size;
> + rt_buf = kmalloc(param[1].u.memref.size, GFP_KERNEL);
> + if (!rt_buf)
> + return ERR_PTR(-ENOMEM);
> +
> + memcpy(rt_buf, rt_shm_va, *output_rt_size);
rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
https://lore.kernel.org/lkml/20260310140255.2520230-1-mukesh.ojha@oss.qualcomm.com/
> + }
> + }
> +
> + 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 = PTA_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, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +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 = PTA_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 shutdown failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +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 = PTA_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 = 0;
> +
> + 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, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +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);
> +}
> +
> +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, err = -ENODEV;
> +
> + 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, err: %x\n",
> + sess_arg.ret);
> + err = -EINVAL;
> + goto out_ctx;
> + }
> +
> + 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 0;
> +out_ctx:
> + tee_client_close_context(data->ctx);
> +
> + return err;
> +}
> +
> +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("TEE bus based Qualcomm PAS driver");
> --
> 2.51.0
>
--
-Mukesh Ojha
On Fri, Mar 13, 2026 at 04:37:47PM +0530, Mukesh Ojha wrote:
> On Thu, Mar 12, 2026 at 11:57:45AM +0530, Sumit Garg wrote:
> > From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> >
> > Add support for Peripheral Authentication Service (PAS) driver based
> > on TEE bus with OP-TEE providing the backend PAS service implementation.
> >
> > The TEE PAS service ABI is designed to be extensible with additional API
> > as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
> > extensions of the PAS service needed while still maintaining backwards
> > compatibility.
> >
> > Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > ---
> > drivers/firmware/qcom/Kconfig | 9 +
> > drivers/firmware/qcom/Makefile | 1 +
> > drivers/firmware/qcom/qcom_pas_tee.c | 477 +++++++++++++++++++++++++++
> > 3 files changed, 487 insertions(+)
> > create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
> >
> > diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> > index 9a12ae2b639d..fff47abdaafd 100644
> > --- a/drivers/firmware/qcom/Kconfig
> > +++ b/drivers/firmware/qcom/Kconfig
> > @@ -14,6 +14,15 @@ config QCOM_PAS
> > backends plugged in whether it's an SCM implementation or a proper
> > TEE bus based PAS service implementation.
> >
> > +config QCOM_PAS_TEE
> > + tristate
> > + select QCOM_PAS
> > + depends on TEE
> > + depends on !CPU_BIG_ENDIAN
> > + help
> > + Enable the generic Peripheral Authentication Service (PAS) provided
> > + by the firmware TEE implementation as the backend.
> > +
> > config QCOM_SCM
> > select QCOM_PAS
> > select QCOM_TZMEM
> > diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
> > index dc5ab45f906a..48801d18f37b 100644
> > --- a/drivers/firmware/qcom/Makefile
> > +++ b/drivers/firmware/qcom/Makefile
> > @@ -9,3 +9,4 @@ 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_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c
> > new file mode 100644
> > index 000000000000..7db9fd736369
> > --- /dev/null
> > +++ b/drivers/firmware/qcom/qcom_pas_tee.c
> > @@ -0,0 +1,477 @@
> > +// 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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_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 PTA_QCOM_PAS_SET_REMOTE_STATE 7
> > +
> > +/*
> > + * PAS co-processor shutdown.
> > + *
> > + * [in] params[0].value.a: Unique 32bit remote processor identifier
> > + */
> > +#define PTA_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 = PTA_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, err: %x\n",
> > + pas_id, 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 = PTA_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, err: %x\n",
> > + pas_id, inv_arg.ret);
> > + tee_shm_free(mdata_shm);
> > + return -EINVAL;
> > + }
> > + ctx->ptr = (void *)mdata_shm;
> > +
> > + return 0;
> > +}
> > +
> > +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 = PTA_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, err: %x\n",
> > + pas_id, inv_arg.ret);
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +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 = PTA_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);
>
> What is the purpose of this function ? looks like, this is for, how
> much Linux need to allocate for output buffer ?
That's right.
>
> > + if (ret < 0 || inv_arg.ret != 0) {
> > + dev_err(dev, "PAS get RT failed, pas_id: %d, err: %x\n",
> > + ctx->pas_id, inv_arg.ret);
> > + return ERR_PTR(-EINVAL);
> > + }
> > +
> > + if (param[1].u.memref.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(rt_shm)) {
> > + dev_err(dev, "rt_shm allocation failed\n");
> > + return rt_shm;
> > + }
> > +
> > + rt_shm_va = tee_shm_get_va(rt_shm, 0);
> > + if (IS_ERR_OR_NULL(rt_shm_va)) {
> > + dev_err(dev, "rt_shm get VA failed\n");
> > + return ERR_PTR(-EINVAL);
> > + }
> > + memcpy(rt_shm_va, input_rt, input_rt_size);
> > +
> > + param[1].u.memref.shm = rt_shm;
>
> Here, you are passing only one buffer for both input and output ?
>
> Like, you are allocating of buffer of size returned from qtee which I
s/qtee/optee/
> assume includes both input + output rt size and copying the input_rt
> and calling invoke and in return you will get combine table in return ?
That's right.
>
> > + 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, err: %x\n",
> > + ctx->pas_id, inv_arg.ret);
> > + return ERR_PTR(-EINVAL);
> > + }
> > +
> > + if (param[1].u.memref.size) {
> > + *output_rt_size = param[1].u.memref.size;
> > + rt_buf = kmalloc(param[1].u.memref.size, GFP_KERNEL);
> > + if (!rt_buf)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + memcpy(rt_buf, rt_shm_va, *output_rt_size);
>
> rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
>
> https://lore.kernel.org/lkml/20260310140255.2520230-1-mukesh.ojha@oss.qualcomm.com/
>
Sure, I will use that instead.
-Sumit
© 2016 - 2026 Red Hat, Inc.