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) providedby 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.qualco...
Sure, I will use that instead.
-Sumit