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 <[email protected]>
> > 
> > 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 <[email protected]>
> > ---
> >  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/[email protected]/
> 

Sure, I will use that instead.

-Sumit

Reply via email to