* [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
@ 2026-09-07 9:28 Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
` (4 more replies)
0 siblings, 5 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-07 9:28 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity, Kuldeep Singh
Qualcomm platforms with a discrete TPM (dTPM) talked to it directly over
a non-secure SPI channel from the kernel. Arm's Base Boot Security
Requirements (BBSR) v1.4 require that access to go through TrustZone
instead, so on affected Qualcomm platforms the TPM 2.0 instance is now
fronted by a Trusted Application (TA) running inside Qualcomm's Trusted
Execution Environment (QTEE), which talks to the dTPM (or implements an
fTPM) on the kernel's behalf.
This series adds a kernel driver for that TA, built on the QCOMTEE
object-IPC transport (drivers/tee/qcomtee/) already used to reach other
QTEE services.
This patch series functionally depends on below(patch 5/6 specifically)
for qtee service discovery.
- https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
Tested on Glymur-crd target with tpm2-tools utility.
Validations:
- Get capabilities
- Random number generator
- RSA key creation, encryption and decryption.
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
Changes in v2:
- Use QCOMTEE_TPM_UID as 81 for service discovery in patch 1.
- Use FIELD_GET, zero initialised array and log improvement (Konrad)
- Improve commit title and other fixes (Jarkko)
- Split MAINTAINERS entry as separate patch.
- Link to v1: https://patch.msgid.link/20260831-tpm_qcom_driver-v1-0-6f16fa6924fa@oss.qualcomm.com
To: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
To: Jens Wiklander <jenswi@kernel.org>
To: Sumit Garg <sumit.garg@kernel.org>
To: Peter Huewe <peterhuewe@gmx.de>
To: Jarkko Sakkinen <jarkko@kernel.org>
To: Jason Gunthorpe <jgg@ziepe.ca>
To: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: op-tee@lists.trustedfirmware.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-integrity@vger.kernel.org
---
Kuldeep Singh (3):
tee: qcomtee: Register qcom.tz.tpm service for discovery
tpm: Introduce Qualcomm TPM driver
MAINTAINERS: Add Qualcomm TPM driver entry
MAINTAINERS | 7 +
drivers/char/tpm/Kconfig | 9 +
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm_qcom.h | 83 +++++++++
drivers/tee/qcomtee/call.c | 4 +-
drivers/tee/qcomtee/qcomtee_msg.h | 2 +
7 files changed, 459 insertions(+), 1 deletion(-)
---
base-commit: f3e6330d7fe42b204af05a2dbc68b379e0ad179e
change-id: 20260831-tpm_qcom_driver-d21c720e73b2
prerequisite-change-id: 20260408-qcom_uefisecapp_migrate_qcomtee-13869d45e014:v2
prerequisite-patch-id: 4dc81445c9baf36f420da8c2e2bed96e71b31a5b
prerequisite-patch-id: b487dfe2fbc076f4815dc6c73b9e68b0b78c961f
prerequisite-patch-id: c5df2b3696520a96f95b2d3535ed84cdc21cc315
prerequisite-patch-id: bbdd5327c15aeaa99ce9b74bab324a98f084ed48
prerequisite-patch-id: 07d9c4e9fe9fd61f60e3f35b30b9d81716f0734c
prerequisite-patch-id: 10ff88d87586f21f3cff3f72dbd21c27adbfbbcc
Best regards,
--
Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
@ 2026-09-07 9:28 ` Kuldeep Singh
2026-09-18 1:46 ` Jarkko Sakkinen
2026-09-07 9:28 ` [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver Kuldeep Singh
` (3 subsequent siblings)
4 siblings, 1 reply; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-07 9:28 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity, Kuldeep Singh
Add "qcom.tz.tpm" (QCOMTEE_TPM_UID) to qtee_services[] so the TPM TA
is enumerated as a TEE-bus device, allowing a client driver to bind to
it via its generated UUID.
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
drivers/tee/qcomtee/call.c | 4 +++-
drivers/tee/qcomtee/qcomtee_msg.h | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c
index 4212572ff54b..aca4e8c6e37b 100644
--- a/drivers/tee/qcomtee/call.c
+++ b/drivers/tee/qcomtee/call.c
@@ -753,7 +753,9 @@ static const uuid_t qtee_service_uuid_ns = UUID_INIT(0xe1b48857, 0x6154, 0x49f9,
static const struct qtee_service qtee_services[] = {
{ "qcom.tz.uefisecapp",
- QCOMTEE_UEFI_SEC_UID }
+ QCOMTEE_UEFI_SEC_UID },
+ { "qcom.tz.tpm",
+ QCOMTEE_TPM_UID }
};
static void qtee_release_service(struct device *dev)
diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h
index ecaf8db67d45..87f1c257a877 100644
--- a/drivers/tee/qcomtee/qcomtee_msg.h
+++ b/drivers/tee/qcomtee/qcomtee_msg.h
@@ -106,6 +106,8 @@ union qcomtee_msg_arg {
#define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU)
#define QCOMTEE_UEFI_SEC_UID 413
+#define QCOMTEE_TPM_UID 81
+
/* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */
/* The message contains a callback request. */
--
2.34.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
@ 2026-09-07 9:28 ` Kuldeep Singh
2026-09-18 1:48 ` Jarkko Sakkinen
2026-09-28 23:29 ` Amirreza Zarrabi
2026-09-07 9:28 ` [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry Kuldeep Singh
` (2 subsequent siblings)
4 siblings, 2 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-07 9:28 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity, Kuldeep Singh
Add a TPM chip driver for platforms where a TPM 2.0 instance is
implemented by a Trusted Application (TA) running in Qualcomm's Trusted
Execution Environment (QTEE), reachable over the QCOMTEE object-IPC
transport.
The driver discovers the qcom.tz.tpm TEE-bus device, opens a session
with the TPM TA, and register with tpm interface. This exposes the TA
through the standard /dev/tpm interface and the existing tpm2 command
layer. OS need not be aware underlying TPM instance is dTPM or fTPM.
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
drivers/char/tpm/Kconfig | 9 ++
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm_qcom.h | 83 +++++++++++
4 files changed, 447 insertions(+)
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 5f672f2c01b0..05d704ed3632 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -243,6 +243,15 @@ config TCG_FTPM_TEE
help
This driver proxies for firmware TPM running in TEE.
+config TCG_QCOM
+ tristate "Qualcomm TEE based TPM Interface"
+ depends on QCOMTEE
+ help
+ This driver provides interface to run TPM instances with Trustzone
+ having Qualcomm TPM TA running in Qualcomm TEE.
+ The mechanism uses the object-IPC based transport provided by
+ QCOMTEE.
+
config TCG_SVSM
tristate "SNP SVSM vTPM interface"
depends on AMD_MEM_ENCRYPT
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 5b5cdc0d32e4..471cbf49afd2 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -45,5 +45,6 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o
obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
+obj-$(CONFIG_TCG_QCOM) += tpm_qcom.o
obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o
diff --git a/drivers/char/tpm/tpm_qcom.c b/drivers/char/tpm/tpm_qcom.c
new file mode 100644
index 000000000000..ef29c66f18ae
--- /dev/null
+++ b/drivers/char/tpm/tpm_qcom.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/tee.h>
+#include <linux/tee_drv.h>
+#include <linux/tpm.h>
+#include <linux/uuid.h>
+
+#include "tpm.h"
+#include "tpm_qcom.h"
+
+/* UUID of the QTEE-bus device representing the TPM TA. */
+static const uuid_t tpm_qcom_uuid =
+ UUID_INIT(0xaabcb593, 0x7083, 0x5536,
+ 0xac, 0x27, 0x3d, 0x2d, 0x89, 0x41, 0x9d, 0xdb);
+
+static void tpm_qcom_release_object(struct tee_context *ctx,
+ struct tee_param_objref object)
+{
+ struct tee_ioctl_object_invoke_arg inv_arg = {};
+
+ inv_arg.id = object.id;
+ inv_arg.op = QCOMTEE_MSG_OBJECT_OP_RELEASE;
+ inv_arg.num_params = 0;
+
+ tee_client_object_invoke_func(ctx, &inv_arg, NULL);
+}
+
+static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
+ struct tee_param_objref *client_env_obj)
+{
+ struct tee_ioctl_object_invoke_arg inv_arg = {};
+ struct tee_param param[2] = {};
+ int ret;
+
+ inv_arg.id = TEE_OBJREF_NULL;
+ inv_arg.op = QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS;
+ inv_arg.num_params = 2;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT;
+ param[0].u.objref.id = TEE_OBJREF_NULL;
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
+
+ ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0)
+ return ret ?: inv_arg.ret;
+
+ *client_env_obj = param[1].u.objref;
+ return ret;
+}
+
+static int tpm_qcom_get_svc_obj(struct tee_context *ctx,
+ struct tee_param_objref client_env_obj,
+ struct tee_param_objref *tpm_svc_obj)
+{
+ struct tee_ioctl_object_invoke_arg inv_arg = {};
+ struct tee_param param[2] = {};
+ u32 tpm_uid = QCOMTEE_TPM_UID;
+ int ret;
+
+ inv_arg.id = client_env_obj.id;
+ inv_arg.op = QCOMTEE_OP_CLIENT_ENV_OPEN;
+ inv_arg.num_params = 2;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
+ param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &tpm_uid,
+ .size = sizeof(tpm_uid) };
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
+
+ ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0)
+ return ret ?: inv_arg.ret;
+
+ *tpm_svc_obj = param[1].u.objref;
+ return ret;
+}
+
+static int tpm_qcom_send_command(struct tpm_qcom_private *pvt_data,
+ u32 locality, void *req, size_t req_len,
+ void *rsp, size_t *rsp_len)
+{
+ struct tee_ioctl_object_invoke_arg inv_arg = {};
+ struct tee_param param[3] = {};
+ u8 locality_arg = locality;
+ int ret;
+
+ inv_arg.id = pvt_data->tpm_svc_obj.id;
+ inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
+ inv_arg.num_params = 3;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
+ param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &locality_arg,
+ .size = sizeof(locality_arg) };
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
+ param[1].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
+ param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
+ param[2].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
+
+ ret = tee_client_object_invoke_func(pvt_data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(pvt_data->dev,
+ "send_command invoke ret: %d, err: 0x%x\n",
+ ret, inv_arg.ret);
+ return ret ?: inv_arg.ret;
+ }
+
+ *rsp_len = param[2].u.ubuf.size;
+
+ return ret;
+}
+
+static int tpm_qcom_get_ta_details(struct tpm_qcom_private *pvt_data)
+{
+ struct tpm_qcom_ta_version_req ver_req = {
+ .command_id = QCOMTEE_TPM_GET_TA_VERSION_ID,
+ };
+ struct tpm_qcom_ta_version_rsp ver_rsp;
+ size_t ver_rsp_len = sizeof(ver_rsp);
+ struct tpm_qcom_type_req type_req = {
+ .command_id = QCOMTEE_TPM_TYPE_ID,
+ };
+ struct tpm_qcom_type_rsp type_rsp;
+ size_t type_rsp_len = sizeof(type_rsp);
+ int ret;
+
+ ret = tpm_qcom_send_command(pvt_data, 0, &ver_req, sizeof(ver_req),
+ &ver_rsp, &ver_rsp_len);
+ if (ret || ver_rsp_len < sizeof(ver_rsp) || ver_rsp.status != 0) {
+ dev_err(pvt_data->dev,
+ "failed to query TA version: ret=%d, status=%u\n",
+ ret, ret ? 0 : ver_rsp.status);
+ return ret ?: -EIO;
+ }
+
+ dev_info(pvt_data->dev, "TPM TA version %lu.%lu\n",
+ FIELD_GET(QCOMTEE_TPM_TA_VERSION_MAJOR, ver_rsp.version_num),
+ FIELD_GET(QCOMTEE_TPM_TA_VERSION_MINOR, ver_rsp.version_num));
+
+ ret = tpm_qcom_send_command(pvt_data, 0, &type_req, sizeof(type_req),
+ &type_rsp, &type_rsp_len);
+ if (ret || type_rsp_len < sizeof(type_rsp) || type_rsp.status != 0) {
+ dev_err(pvt_data->dev,
+ "failed to query TPM type: ret=%d, status=%u\n",
+ ret, ret ? 0 : type_rsp.status);
+ return ret ?: -EIO;
+ }
+
+ switch (type_rsp.tpm_type) {
+ case QCOMTEE_TPM_TYPE_FTPM:
+ dev_info(pvt_data->dev, "TPM type: fTPM\n");
+ pvt_data->is_dtpm = false;
+ break;
+ case QCOMTEE_TPM_TYPE_DTPM:
+ dev_info(pvt_data->dev, "TPM type: dTPM\n");
+ pvt_data->is_dtpm = true;
+ break;
+ default:
+ dev_err(pvt_data->dev, "unsupported TPM type: 0x%08x\n",
+ type_rsp.tpm_type);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* fTPM does not implement this command and to be invoked via dtpm only. */
+static void tpm_qcom_transfer(struct tpm_qcom_private *pvt_data,
+ u32 transfer_state)
+{
+ struct tpm_qcom_transfer_req req = {
+ .command_id = QCOMTEE_TPM_TRANSFER_ID,
+ .transfer_state = transfer_state,
+ };
+ struct tpm_qcom_transfer_rsp rsp;
+ size_t rsp_len = sizeof(rsp);
+ int ret;
+
+ ret = tpm_qcom_send_command(pvt_data, 0, &req, sizeof(req), &rsp,
+ &rsp_len);
+ if (ret || rsp_len < sizeof(rsp) || rsp.status != 0)
+ dev_warn(pvt_data->dev,
+ "transfer state=%u hint failed: ret=%d, status=%u\n",
+ transfer_state, ret, ret ? 0 : rsp.status);
+}
+
+static int tpm_qcom_cmd_ready(struct tpm_chip *chip)
+{
+ struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
+
+ if (pvt_data->is_dtpm)
+ tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_START);
+
+ return 0;
+}
+
+static int tpm_qcom_go_idle(struct tpm_chip *chip)
+{
+ struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
+
+ if (pvt_data->is_dtpm)
+ tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_END);
+
+ return 0;
+}
+
+/*
+ * The raw TPM2 command in @buf is sent directly as send_command's UBUF-in
+ * param and the raw TPM2 response is read back from its UBUF-out param.
+ */
+static int tpm_qcom_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t cmd_len)
+{
+ struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
+ size_t rsp_len = PAGE_ALIGN(MAX_RESPONSE_SIZE);
+ size_t copy_len;
+ int ret;
+
+ if (cmd_len > MAX_COMMAND_SIZE) {
+ dev_err(&chip->dev, "len=%zd exceeds MAX_COMMAND_SIZE\n", cmd_len);
+ return -EIO;
+ }
+
+ u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
+ if (!response)
+ return -ENOMEM;
+
+ ret = tpm_qcom_send_command(pvt_data, 0, buf, cmd_len, response,
+ &rsp_len);
+ if (ret < 0) {
+ dev_err(&chip->dev, "send_command failed: ret=%d\n", ret);
+ return ret;
+ }
+
+ copy_len = min_t(size_t, bufsiz, rsp_len);
+ memcpy(buf, response, copy_len);
+
+ return copy_len;
+}
+
+static const struct tpm_class_ops tpm_qcom_ops = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .send = tpm_qcom_send,
+ .cmd_ready = tpm_qcom_cmd_ready,
+ .go_idle = tpm_qcom_go_idle,
+};
+
+static int tpm_qcom_ctx_match(struct tee_ioctl_version_data *ver,
+ const void *data)
+{
+ return (ver->impl_id == TEE_IMPL_ID_QTEE);
+}
+
+static int tpm_qcom_probe(struct tee_client_device *tee_dev)
+{
+ struct device *dev = &tee_dev->dev;
+ struct tpm_qcom_private *pvt_data;
+ struct tee_param_objref client_env_obj;
+ struct tee_param_objref tpm_svc_obj;
+ struct tpm_chip *chip;
+ int rc, err;
+
+ pvt_data = devm_kzalloc(dev, sizeof(*pvt_data), GFP_KERNEL);
+ if (!pvt_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pvt_data);
+
+ pvt_data->ctx = tee_client_open_context(NULL, tpm_qcom_ctx_match, NULL, NULL);
+ if (IS_ERR(pvt_data->ctx))
+ return -ENODEV;
+
+ rc = tpm_qcom_get_client_env_obj(pvt_data->ctx, &client_env_obj);
+ if (rc) {
+ err = -EINVAL;
+ goto out_ctx;
+ }
+
+ rc = tpm_qcom_get_svc_obj(pvt_data->ctx, client_env_obj, &tpm_svc_obj);
+ if (rc) {
+ err = -EINVAL;
+ goto out_client_env;
+ }
+ pvt_data->tpm_svc_obj = tpm_svc_obj;
+ pvt_data->dev = dev;
+
+ err = tpm_qcom_get_ta_details(pvt_data);
+ if (err)
+ goto out_svc_obj;
+
+ chip = tpm_chip_alloc(dev, &tpm_qcom_ops);
+ if (IS_ERR(chip)) {
+ dev_err(dev, "tpm_chip_alloc failed\n");
+ err = PTR_ERR(chip);
+ goto out_svc_obj;
+ }
+
+ pvt_data->chip = chip;
+ pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC;
+
+ err = tpm_chip_register(pvt_data->chip);
+ if (err) {
+ dev_err(dev, "tpm_chip_register failed with rc=%d\n", err);
+ goto out_chip;
+ }
+
+ tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
+ return 0;
+
+out_chip:
+ put_device(&pvt_data->chip->dev);
+out_svc_obj:
+ tpm_qcom_release_object(pvt_data->ctx, tpm_svc_obj);
+out_client_env:
+ tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
+out_ctx:
+ tee_client_close_context(pvt_data->ctx);
+ return err;
+}
+
+static void tpm_qcom_remove(struct tee_client_device *tee_dev)
+{
+ struct tpm_qcom_private *pvt_data = dev_get_drvdata(&tee_dev->dev);
+
+ tpm_chip_unregister(pvt_data->chip);
+ put_device(&pvt_data->chip->dev);
+ tpm_qcom_release_object(pvt_data->ctx, pvt_data->tpm_svc_obj);
+ tee_client_close_context(pvt_data->ctx);
+}
+
+static const struct tee_client_device_id tpm_qcom_id_table[] = {
+ { tpm_qcom_uuid },
+ {}
+};
+MODULE_DEVICE_TABLE(tee, tpm_qcom_id_table);
+
+static struct tee_client_driver tpm_qcom_driver = {
+ .id_table = tpm_qcom_id_table,
+ .probe = tpm_qcom_probe,
+ .remove = tpm_qcom_remove,
+ .driver = {
+ .name = "tpm_qcom",
+ },
+};
+
+module_tee_client_driver(tpm_qcom_driver);
+
+MODULE_DESCRIPTION("TPM driver for Qualcomm TPM TA");
+MODULE_AUTHOR("Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_qcom.h b/drivers/char/tpm/tpm_qcom.h
new file mode 100644
index 000000000000..3945b12643b7
--- /dev/null
+++ b/drivers/char/tpm/tpm_qcom.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __TPM_QCOM_H__
+#define __TPM_QCOM_H__
+
+#include <linux/bitfield.h>
+#include <linux/tee_drv.h>
+#include <linux/tpm.h>
+#include <linux/uuid.h>
+
+#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5
+#define QCOMTEE_OP_CLIENT_ENV_OPEN 0
+#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0)
+#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0)
+
+#define QCOMTEE_TPM_OP_SEND_COMMAND 0
+
+/* UID of the "qcom.tz.tpm" service */
+#define QCOMTEE_TPM_UID 81
+
+/* Max buffer size supported by TPM TA */
+#define MAX_COMMAND_SIZE SZ_4K
+#define MAX_RESPONSE_SIZE SZ_4K
+
+#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
+#define QCOMTEE_TPM_TA_VERSION_MAJOR GENMASK(31, 16)
+#define QCOMTEE_TPM_TA_VERSION_MINOR GENMASK(15, 0)
+
+struct tpm_qcom_ta_version_req {
+ u32 command_id;
+} __packed;
+
+struct tpm_qcom_ta_version_rsp {
+ u32 status;
+ u32 command_id;
+ u32 version_num;
+} __packed;
+
+#define QCOMTEE_TPM_TYPE_ID 0x0080000
+#define QCOMTEE_TPM_TYPE_DTPM 0x6454504dU
+#define QCOMTEE_TPM_TYPE_FTPM 0x6654504dU
+#define QCOMTEE_TPM_TYPE_NONE 0x4e6f6e65U
+
+struct tpm_qcom_type_req {
+ u32 command_id;
+} __packed;
+
+struct tpm_qcom_type_rsp {
+ u32 command_id;
+ u32 status;
+ u32 tpm_type;
+} __packed;
+
+/*
+ * dTPM SPI transfer optimization:
+ * TRANSFER_START before a burst of commands, TRANSFER_END once done.
+ */
+#define QCOMTEE_TPM_TRANSFER_ID 0x0000002
+#define QCOMTEE_TPM_TRANSFER_END 0
+#define QCOMTEE_TPM_TRANSFER_START 1
+
+struct tpm_qcom_transfer_req {
+ u32 command_id;
+ u32 transfer_state;
+} __packed;
+
+struct tpm_qcom_transfer_rsp {
+ u32 command_id;
+ u32 status;
+} __packed;
+
+struct tpm_qcom_private {
+ struct tpm_chip *chip;
+ struct device *dev;
+ struct tee_context *ctx;
+ struct tee_param_objref tpm_svc_obj;
+ bool is_dtpm;
+};
+
+#endif /* __TPM_QCOM_H__ */
--
2.34.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver Kuldeep Singh
@ 2026-09-07 9:28 ` Kuldeep Singh
2026-09-18 1:49 ` Jarkko Sakkinen
2026-09-16 9:02 ` [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-09-18 1:53 ` Jarkko Sakkinen
4 siblings, 1 reply; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-07 9:28 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity, Kuldeep Singh
Add a dedicated MAINTAINERS entry for the Qualcomm TPM driver, which
uses the Qualcomm TEE interface (qcomtee) to communicate with a TPM
Trusted Application running in TrustZone.
Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 47b04968e79a..cfa5b66823c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22636,6 +22636,13 @@ S: Maintained
F: Documentation/tee/qtee.rst
F: drivers/tee/qcomtee/
+QUALCOMM TPM DRIVER
+M: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: drivers/char/tpm/tpm_qcom.c
+F: drivers/char/tpm/tpm_qcom.h
+
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-arm-msm@vger.kernel.org
--
2.34.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
` (2 preceding siblings ...)
2026-09-07 9:28 ` [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry Kuldeep Singh
@ 2026-09-16 9:02 ` Kuldeep Singh
2026-09-18 1:53 ` Jarkko Sakkinen
4 siblings, 0 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-16 9:02 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity
On 07-09-2026 14:58, Kuldeep Singh wrote:
> Qualcomm platforms with a discrete TPM (dTPM) talked to it directly over
> a non-secure SPI channel from the kernel. Arm's Base Boot Security
> Requirements (BBSR) v1.4 require that access to go through TrustZone
> instead, so on affected Qualcomm platforms the TPM 2.0 instance is now
> fronted by a Trusted Application (TA) running inside Qualcomm's Trusted
> Execution Environment (QTEE), which talks to the dTPM (or implements an
> fTPM) on the kernel's behalf.
>
> This series adds a kernel driver for that TA, built on the QCOMTEE
> object-IPC transport (drivers/tee/qcomtee/) already used to reach other
> QTEE services.
>
> This patch series functionally depends on below(patch 5/6 specifically)
> for qtee service discovery.
> - https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
>
> Tested on Glymur-crd target with tpm2-tools utility.
>
> Validations:
> - Get capabilities
> - Random number generator
> - RSA key creation, encryption and decryption.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
Hi Jarkko,
Kindly let me know for any review comments/feedback so that we can align
and shape next revision if needed.
Many thanks!
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-09-07 9:28 ` [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
@ 2026-09-18 1:46 ` Jarkko Sakkinen
2026-09-21 8:10 ` Kuldeep Singh
0 siblings, 1 reply; 22+ messages in thread
From: Jarkko Sakkinen @ 2026-09-18 1:46 UTC (permalink / raw)
To: Kuldeep Singh
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On Mon, Sep 07, 2026 at 02:58:41PM +0530, Kuldeep Singh wrote:
> Add "qcom.tz.tpm" (QCOMTEE_TPM_UID) to qtee_services[] so the TPM TA
> is enumerated as a TEE-bus device, allowing a client driver to bind to
> it via its generated UUID.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> ---
> drivers/tee/qcomtee/call.c | 4 +++-
> drivers/tee/qcomtee/qcomtee_msg.h | 2 ++
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c
> index 4212572ff54b..aca4e8c6e37b 100644
> --- a/drivers/tee/qcomtee/call.c
> +++ b/drivers/tee/qcomtee/call.c
> @@ -753,7 +753,9 @@ static const uuid_t qtee_service_uuid_ns = UUID_INIT(0xe1b48857, 0x6154, 0x49f9,
>
> static const struct qtee_service qtee_services[] = {
> { "qcom.tz.uefisecapp",
> - QCOMTEE_UEFI_SEC_UID }
> + QCOMTEE_UEFI_SEC_UID },
> + { "qcom.tz.tpm",
> + QCOMTEE_TPM_UID }
> };
>
> static void qtee_release_service(struct device *dev)
> diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h
> index ecaf8db67d45..87f1c257a877 100644
> --- a/drivers/tee/qcomtee/qcomtee_msg.h
> +++ b/drivers/tee/qcomtee/qcomtee_msg.h
> @@ -106,6 +106,8 @@ union qcomtee_msg_arg {
> #define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU)
>
> #define QCOMTEE_UEFI_SEC_UID 413
> +#define QCOMTEE_TPM_UID 81
> +
> /* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */
>
> /* The message contains a callback request. */
>
> --
> 2.34.1
>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
BR, Jarkko
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver
2026-09-07 9:28 ` [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver Kuldeep Singh
@ 2026-09-18 1:48 ` Jarkko Sakkinen
2026-09-28 23:29 ` Amirreza Zarrabi
1 sibling, 0 replies; 22+ messages in thread
From: Jarkko Sakkinen @ 2026-09-18 1:48 UTC (permalink / raw)
To: Kuldeep Singh
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On Mon, Sep 07, 2026 at 02:58:42PM +0530, Kuldeep Singh wrote:
> Add a TPM chip driver for platforms where a TPM 2.0 instance is
> implemented by a Trusted Application (TA) running in Qualcomm's Trusted
> Execution Environment (QTEE), reachable over the QCOMTEE object-IPC
> transport.
>
> The driver discovers the qcom.tz.tpm TEE-bus device, opens a session
> with the TPM TA, and register with tpm interface. This exposes the TA
> through the standard /dev/tpm interface and the existing tpm2 command
> layer. OS need not be aware underlying TPM instance is dTPM or fTPM.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> ---
> drivers/char/tpm/Kconfig | 9 ++
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm_qcom.h | 83 +++++++++++
> 4 files changed, 447 insertions(+)
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 5f672f2c01b0..05d704ed3632 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -243,6 +243,15 @@ config TCG_FTPM_TEE
> help
> This driver proxies for firmware TPM running in TEE.
>
> +config TCG_QCOM
> + tristate "Qualcomm TEE based TPM Interface"
> + depends on QCOMTEE
> + help
> + This driver provides interface to run TPM instances with Trustzone
> + having Qualcomm TPM TA running in Qualcomm TEE.
> + The mechanism uses the object-IPC based transport provided by
> + QCOMTEE.
> +
> config TCG_SVSM
> tristate "SNP SVSM vTPM interface"
> depends on AMD_MEM_ENCRYPT
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 5b5cdc0d32e4..471cbf49afd2 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -45,5 +45,6 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o
> obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
> obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
> obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
> +obj-$(CONFIG_TCG_QCOM) += tpm_qcom.o
> obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
> obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o
> diff --git a/drivers/char/tpm/tpm_qcom.c b/drivers/char/tpm/tpm_qcom.c
> new file mode 100644
> index 000000000000..ef29c66f18ae
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.c
> @@ -0,0 +1,354 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + */
> +
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/tee.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +#include "tpm.h"
> +#include "tpm_qcom.h"
> +
> +/* UUID of the QTEE-bus device representing the TPM TA. */
> +static const uuid_t tpm_qcom_uuid =
> + UUID_INIT(0xaabcb593, 0x7083, 0x5536,
> + 0xac, 0x27, 0x3d, 0x2d, 0x89, 0x41, 0x9d, 0xdb);
> +
> +static void tpm_qcom_release_object(struct tee_context *ctx,
> + struct tee_param_objref object)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> +
> + inv_arg.id = object.id;
> + inv_arg.op = QCOMTEE_MSG_OBJECT_OP_RELEASE;
> + inv_arg.num_params = 0;
> +
> + tee_client_object_invoke_func(ctx, &inv_arg, NULL);
> +}
> +
> +static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
> + struct tee_param_objref *client_env_obj)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[2] = {};
> + int ret;
> +
> + inv_arg.id = TEE_OBJREF_NULL;
> + inv_arg.op = QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS;
> + inv_arg.num_params = 2;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT;
> + param[0].u.objref.id = TEE_OBJREF_NULL;
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
> +
> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0)
> + return ret ?: inv_arg.ret;
> +
> + *client_env_obj = param[1].u.objref;
> + return ret;
> +}
> +
> +static int tpm_qcom_get_svc_obj(struct tee_context *ctx,
> + struct tee_param_objref client_env_obj,
> + struct tee_param_objref *tpm_svc_obj)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[2] = {};
> + u32 tpm_uid = QCOMTEE_TPM_UID;
> + int ret;
> +
> + inv_arg.id = client_env_obj.id;
> + inv_arg.op = QCOMTEE_OP_CLIENT_ENV_OPEN;
> + inv_arg.num_params = 2;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &tpm_uid,
> + .size = sizeof(tpm_uid) };
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
> +
> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0)
> + return ret ?: inv_arg.ret;
> +
> + *tpm_svc_obj = param[1].u.objref;
> + return ret;
> +}
> +
> +static int tpm_qcom_send_command(struct tpm_qcom_private *pvt_data,
> + u32 locality, void *req, size_t req_len,
> + void *rsp, size_t *rsp_len)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[3] = {};
> + u8 locality_arg = locality;
> + int ret;
> +
> + inv_arg.id = pvt_data->tpm_svc_obj.id;
> + inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
> + inv_arg.num_params = 3;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &locality_arg,
> + .size = sizeof(locality_arg) };
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[1].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
> + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
> + param[2].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
> +
> + ret = tee_client_object_invoke_func(pvt_data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(pvt_data->dev,
> + "send_command invoke ret: %d, err: 0x%x\n",
> + ret, inv_arg.ret);
> + return ret ?: inv_arg.ret;
> + }
> +
> + *rsp_len = param[2].u.ubuf.size;
> +
> + return ret;
> +}
> +
> +static int tpm_qcom_get_ta_details(struct tpm_qcom_private *pvt_data)
> +{
> + struct tpm_qcom_ta_version_req ver_req = {
> + .command_id = QCOMTEE_TPM_GET_TA_VERSION_ID,
> + };
> + struct tpm_qcom_ta_version_rsp ver_rsp;
> + size_t ver_rsp_len = sizeof(ver_rsp);
> + struct tpm_qcom_type_req type_req = {
> + .command_id = QCOMTEE_TPM_TYPE_ID,
> + };
> + struct tpm_qcom_type_rsp type_rsp;
> + size_t type_rsp_len = sizeof(type_rsp);
> + int ret;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &ver_req, sizeof(ver_req),
> + &ver_rsp, &ver_rsp_len);
> + if (ret || ver_rsp_len < sizeof(ver_rsp) || ver_rsp.status != 0) {
> + dev_err(pvt_data->dev,
> + "failed to query TA version: ret=%d, status=%u\n",
> + ret, ret ? 0 : ver_rsp.status);
> + return ret ?: -EIO;
> + }
> +
> + dev_info(pvt_data->dev, "TPM TA version %lu.%lu\n",
> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MAJOR, ver_rsp.version_num),
> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MINOR, ver_rsp.version_num));
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &type_req, sizeof(type_req),
> + &type_rsp, &type_rsp_len);
> + if (ret || type_rsp_len < sizeof(type_rsp) || type_rsp.status != 0) {
> + dev_err(pvt_data->dev,
> + "failed to query TPM type: ret=%d, status=%u\n",
> + ret, ret ? 0 : type_rsp.status);
> + return ret ?: -EIO;
> + }
> +
> + switch (type_rsp.tpm_type) {
> + case QCOMTEE_TPM_TYPE_FTPM:
> + dev_info(pvt_data->dev, "TPM type: fTPM\n");
> + pvt_data->is_dtpm = false;
> + break;
> + case QCOMTEE_TPM_TYPE_DTPM:
> + dev_info(pvt_data->dev, "TPM type: dTPM\n");
> + pvt_data->is_dtpm = true;
> + break;
> + default:
> + dev_err(pvt_data->dev, "unsupported TPM type: 0x%08x\n",
> + type_rsp.tpm_type);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +/* fTPM does not implement this command and to be invoked via dtpm only. */
> +static void tpm_qcom_transfer(struct tpm_qcom_private *pvt_data,
> + u32 transfer_state)
> +{
> + struct tpm_qcom_transfer_req req = {
> + .command_id = QCOMTEE_TPM_TRANSFER_ID,
> + .transfer_state = transfer_state,
> + };
> + struct tpm_qcom_transfer_rsp rsp;
> + size_t rsp_len = sizeof(rsp);
> + int ret;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &req, sizeof(req), &rsp,
> + &rsp_len);
> + if (ret || rsp_len < sizeof(rsp) || rsp.status != 0)
> + dev_warn(pvt_data->dev,
> + "transfer state=%u hint failed: ret=%d, status=%u\n",
> + transfer_state, ret, ret ? 0 : rsp.status);
> +}
> +
> +static int tpm_qcom_cmd_ready(struct tpm_chip *chip)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> +
> + if (pvt_data->is_dtpm)
> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_START);
> +
> + return 0;
> +}
> +
> +static int tpm_qcom_go_idle(struct tpm_chip *chip)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> +
> + if (pvt_data->is_dtpm)
> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_END);
> +
> + return 0;
> +}
> +
> +/*
> + * The raw TPM2 command in @buf is sent directly as send_command's UBUF-in
> + * param and the raw TPM2 response is read back from its UBUF-out param.
> + */
> +static int tpm_qcom_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
> + size_t cmd_len)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> + size_t rsp_len = PAGE_ALIGN(MAX_RESPONSE_SIZE);
> + size_t copy_len;
> + int ret;
> +
> + if (cmd_len > MAX_COMMAND_SIZE) {
> + dev_err(&chip->dev, "len=%zd exceeds MAX_COMMAND_SIZE\n", cmd_len);
> + return -EIO;
> + }
> +
> + u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
> + if (!response)
> + return -ENOMEM;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, buf, cmd_len, response,
> + &rsp_len);
> + if (ret < 0) {
> + dev_err(&chip->dev, "send_command failed: ret=%d\n", ret);
> + return ret;
> + }
> +
> + copy_len = min_t(size_t, bufsiz, rsp_len);
> + memcpy(buf, response, copy_len);
> +
> + return copy_len;
> +}
> +
> +static const struct tpm_class_ops tpm_qcom_ops = {
> + .flags = TPM_OPS_AUTO_STARTUP,
> + .send = tpm_qcom_send,
> + .cmd_ready = tpm_qcom_cmd_ready,
> + .go_idle = tpm_qcom_go_idle,
> +};
> +
> +static int tpm_qcom_ctx_match(struct tee_ioctl_version_data *ver,
> + const void *data)
> +{
> + return (ver->impl_id == TEE_IMPL_ID_QTEE);
> +}
> +
> +static int tpm_qcom_probe(struct tee_client_device *tee_dev)
> +{
> + struct device *dev = &tee_dev->dev;
> + struct tpm_qcom_private *pvt_data;
> + struct tee_param_objref client_env_obj;
> + struct tee_param_objref tpm_svc_obj;
> + struct tpm_chip *chip;
> + int rc, err;
> +
> + pvt_data = devm_kzalloc(dev, sizeof(*pvt_data), GFP_KERNEL);
> + if (!pvt_data)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, pvt_data);
> +
> + pvt_data->ctx = tee_client_open_context(NULL, tpm_qcom_ctx_match, NULL, NULL);
> + if (IS_ERR(pvt_data->ctx))
> + return -ENODEV;
> +
> + rc = tpm_qcom_get_client_env_obj(pvt_data->ctx, &client_env_obj);
> + if (rc) {
> + err = -EINVAL;
> + goto out_ctx;
> + }
> +
> + rc = tpm_qcom_get_svc_obj(pvt_data->ctx, client_env_obj, &tpm_svc_obj);
> + if (rc) {
> + err = -EINVAL;
> + goto out_client_env;
> + }
> + pvt_data->tpm_svc_obj = tpm_svc_obj;
> + pvt_data->dev = dev;
> +
> + err = tpm_qcom_get_ta_details(pvt_data);
> + if (err)
> + goto out_svc_obj;
> +
> + chip = tpm_chip_alloc(dev, &tpm_qcom_ops);
> + if (IS_ERR(chip)) {
> + dev_err(dev, "tpm_chip_alloc failed\n");
> + err = PTR_ERR(chip);
> + goto out_svc_obj;
> + }
> +
> + pvt_data->chip = chip;
> + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC;
> +
> + err = tpm_chip_register(pvt_data->chip);
> + if (err) {
> + dev_err(dev, "tpm_chip_register failed with rc=%d\n", err);
> + goto out_chip;
> + }
> +
> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
> + return 0;
> +
> +out_chip:
> + put_device(&pvt_data->chip->dev);
> +out_svc_obj:
> + tpm_qcom_release_object(pvt_data->ctx, tpm_svc_obj);
> +out_client_env:
> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
> +out_ctx:
> + tee_client_close_context(pvt_data->ctx);
> + return err;
> +}
> +
> +static void tpm_qcom_remove(struct tee_client_device *tee_dev)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(&tee_dev->dev);
> +
> + tpm_chip_unregister(pvt_data->chip);
> + put_device(&pvt_data->chip->dev);
> + tpm_qcom_release_object(pvt_data->ctx, pvt_data->tpm_svc_obj);
> + tee_client_close_context(pvt_data->ctx);
> +}
> +
> +static const struct tee_client_device_id tpm_qcom_id_table[] = {
> + { tpm_qcom_uuid },
> + {}
> +};
> +MODULE_DEVICE_TABLE(tee, tpm_qcom_id_table);
> +
> +static struct tee_client_driver tpm_qcom_driver = {
> + .id_table = tpm_qcom_id_table,
> + .probe = tpm_qcom_probe,
> + .remove = tpm_qcom_remove,
> + .driver = {
> + .name = "tpm_qcom",
> + },
> +};
> +
> +module_tee_client_driver(tpm_qcom_driver);
> +
> +MODULE_DESCRIPTION("TPM driver for Qualcomm TPM TA");
> +MODULE_AUTHOR("Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/char/tpm/tpm_qcom.h b/drivers/char/tpm/tpm_qcom.h
> new file mode 100644
> index 000000000000..3945b12643b7
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.h
> @@ -0,0 +1,83 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __TPM_QCOM_H__
> +#define __TPM_QCOM_H__
> +
> +#include <linux/bitfield.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5
> +#define QCOMTEE_OP_CLIENT_ENV_OPEN 0
> +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0)
> +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0)
> +
> +#define QCOMTEE_TPM_OP_SEND_COMMAND 0
> +
> +/* UID of the "qcom.tz.tpm" service */
> +#define QCOMTEE_TPM_UID 81
> +
> +/* Max buffer size supported by TPM TA */
> +#define MAX_COMMAND_SIZE SZ_4K
> +#define MAX_RESPONSE_SIZE SZ_4K
> +
> +#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
> +#define QCOMTEE_TPM_TA_VERSION_MAJOR GENMASK(31, 16)
> +#define QCOMTEE_TPM_TA_VERSION_MINOR GENMASK(15, 0)
> +
> +struct tpm_qcom_ta_version_req {
> + u32 command_id;
> +} __packed;
> +
> +struct tpm_qcom_ta_version_rsp {
> + u32 status;
> + u32 command_id;
> + u32 version_num;
> +} __packed;
> +
> +#define QCOMTEE_TPM_TYPE_ID 0x0080000
> +#define QCOMTEE_TPM_TYPE_DTPM 0x6454504dU
> +#define QCOMTEE_TPM_TYPE_FTPM 0x6654504dU
> +#define QCOMTEE_TPM_TYPE_NONE 0x4e6f6e65U
> +
> +struct tpm_qcom_type_req {
> + u32 command_id;
> +} __packed;
> +
> +struct tpm_qcom_type_rsp {
> + u32 command_id;
> + u32 status;
> + u32 tpm_type;
> +} __packed;
> +
> +/*
> + * dTPM SPI transfer optimization:
> + * TRANSFER_START before a burst of commands, TRANSFER_END once done.
> + */
> +#define QCOMTEE_TPM_TRANSFER_ID 0x0000002
> +#define QCOMTEE_TPM_TRANSFER_END 0
> +#define QCOMTEE_TPM_TRANSFER_START 1
> +
> +struct tpm_qcom_transfer_req {
> + u32 command_id;
> + u32 transfer_state;
> +} __packed;
> +
> +struct tpm_qcom_transfer_rsp {
> + u32 command_id;
> + u32 status;
> +} __packed;
> +
> +struct tpm_qcom_private {
> + struct tpm_chip *chip;
> + struct device *dev;
> + struct tee_context *ctx;
> + struct tee_param_objref tpm_svc_obj;
> + bool is_dtpm;
> +};
> +
> +#endif /* __TPM_QCOM_H__ */
>
> --
> 2.34.1
>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
BR, Jarkko
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry
2026-09-07 9:28 ` [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry Kuldeep Singh
@ 2026-09-18 1:49 ` Jarkko Sakkinen
0 siblings, 0 replies; 22+ messages in thread
From: Jarkko Sakkinen @ 2026-09-18 1:49 UTC (permalink / raw)
To: Kuldeep Singh
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On Mon, Sep 07, 2026 at 02:58:43PM +0530, Kuldeep Singh wrote:
> Add a dedicated MAINTAINERS entry for the Qualcomm TPM driver, which
> uses the Qualcomm TEE interface (qcomtee) to communicate with a TPM
> Trusted Application running in TrustZone.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> ---
> MAINTAINERS | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 47b04968e79a..cfa5b66823c7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22636,6 +22636,13 @@ S: Maintained
> F: Documentation/tee/qtee.rst
> F: drivers/tee/qcomtee/
>
> +QUALCOMM TPM DRIVER
> +M: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> +L: linux-arm-msm@vger.kernel.org
> +S: Maintained
> +F: drivers/char/tpm/tpm_qcom.c
> +F: drivers/char/tpm/tpm_qcom.h
> +
> QUALCOMM TRUST ZONE MEMORY ALLOCATOR
> M: Bartosz Golaszewski <brgl@kernel.org>
> L: linux-arm-msm@vger.kernel.org
>
> --
> 2.34.1
>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
BR, Jarkko
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
` (3 preceding siblings ...)
2026-09-16 9:02 ` [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
@ 2026-09-18 1:53 ` Jarkko Sakkinen
2026-09-21 8:36 ` Kuldeep Singh
4 siblings, 1 reply; 22+ messages in thread
From: Jarkko Sakkinen @ 2026-09-18 1:53 UTC (permalink / raw)
To: Kuldeep Singh
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On Mon, Sep 07, 2026 at 02:58:40PM +0530, Kuldeep Singh wrote:
> Qualcomm platforms with a discrete TPM (dTPM) talked to it directly over
> a non-secure SPI channel from the kernel. Arm's Base Boot Security
> Requirements (BBSR) v1.4 require that access to go through TrustZone
> instead, so on affected Qualcomm platforms the TPM 2.0 instance is now
> fronted by a Trusted Application (TA) running inside Qualcomm's Trusted
> Execution Environment (QTEE), which talks to the dTPM (or implements an
> fTPM) on the kernel's behalf.
>
> This series adds a kernel driver for that TA, built on the QCOMTEE
> object-IPC transport (drivers/tee/qcomtee/) already used to reach other
> QTEE services.
>
> This patch series functionally depends on below(patch 5/6 specifically)
> for qtee service discovery.
> - https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
>
> Tested on Glymur-crd target with tpm2-tools utility.
>
> Validations:
> - Get capabilities
> - Random number generator
> - RSA key creation, encryption and decryption.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> ---
> Changes in v2:
> - Use QCOMTEE_TPM_UID as 81 for service discovery in patch 1.
> - Use FIELD_GET, zero initialised array and log improvement (Konrad)
> - Improve commit title and other fixes (Jarkko)
> - Split MAINTAINERS entry as separate patch.
> - Link to v1: https://patch.msgid.link/20260831-tpm_qcom_driver-v1-0-6f16fa6924fa@oss.qualcomm.com
>
> To: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
> To: Jens Wiklander <jenswi@kernel.org>
> To: Sumit Garg <sumit.garg@kernel.org>
> To: Peter Huewe <peterhuewe@gmx.de>
> To: Jarkko Sakkinen <jarkko@kernel.org>
> To: Jason Gunthorpe <jgg@ziepe.ca>
> To: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> Cc: linux-arm-msm@vger.kernel.org
> Cc: op-tee@lists.trustedfirmware.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-integrity@vger.kernel.org
>
> ---
> Kuldeep Singh (3):
> tee: qcomtee: Register qcom.tz.tpm service for discovery
> tpm: Introduce Qualcomm TPM driver
> MAINTAINERS: Add Qualcomm TPM driver entry
>
> MAINTAINERS | 7 +
> drivers/char/tpm/Kconfig | 9 +
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm_qcom.h | 83 +++++++++
> drivers/tee/qcomtee/call.c | 4 +-
> drivers/tee/qcomtee/qcomtee_msg.h | 2 +
> 7 files changed, 459 insertions(+), 1 deletion(-)
> ---
> base-commit: f3e6330d7fe42b204af05a2dbc68b379e0ad179e
> change-id: 20260831-tpm_qcom_driver-d21c720e73b2
> prerequisite-change-id: 20260408-qcom_uefisecapp_migrate_qcomtee-13869d45e014:v2
> prerequisite-patch-id: 4dc81445c9baf36f420da8c2e2bed96e71b31a5b
> prerequisite-patch-id: b487dfe2fbc076f4815dc6c73b9e68b0b78c961f
> prerequisite-patch-id: c5df2b3696520a96f95b2d3535ed84cdc21cc315
> prerequisite-patch-id: bbdd5327c15aeaa99ce9b74bab324a98f084ed48
> prerequisite-patch-id: 07d9c4e9fe9fd61f60e3f35b30b9d81716f0734c
> prerequisite-patch-id: 10ff88d87586f21f3cff3f72dbd21c27adbfbbcc
>
> Best regards,
> --
> Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
>
Causes merge conflicts with my tree when applied with git am (actually
b4 shazam).
BR, Jarkko
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-09-18 1:46 ` Jarkko Sakkinen
@ 2026-09-21 8:10 ` Kuldeep Singh
2026-09-25 8:51 ` Jens Wiklander
0 siblings, 1 reply; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-21 8:10 UTC (permalink / raw)
To: Jarkko Sakkinen, Jens Wiklander, Amirreza Zarrabi
Cc: Sumit Garg, Peter Huewe, Jason Gunthorpe, linux-arm-msm, op-tee,
linux-kernel, linux-integrity
On 18-09-2026 07:16, Jarkko Sakkinen wrote:
> On Mon, Sep 07, 2026 at 02:58:41PM +0530, Kuldeep Singh wrote:
>> Add "qcom.tz.tpm" (QCOMTEE_TPM_UID) to qtee_services[] so the TPM TA
>> is enumerated as a TEE-bus device, allowing a client driver to bind to
>> it via its generated UUID.
>>
>> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
[...]
>
> Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Thanks Jarkko for the review.
Can TEE maintainers(Amirreza/Jens?) also review this change?
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-09-18 1:53 ` Jarkko Sakkinen
@ 2026-09-21 8:36 ` Kuldeep Singh
2026-09-25 14:46 ` Jarkko Sakkinen
0 siblings, 1 reply; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-21 8:36 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
> Causes merge conflicts with my tree when applied with git am (actually
> b4 shazam).
Jarkko, this series was sent based on tag next-20260828 after applying
prerequisite series[1]. Some minor Makefile/MAINTAINERS conflict was
observed IIRC.
Kindly note for merge strategy, prerequisite series[1] will be merged
via TEE tree and current series need patch 1-5(not patch6) only as
prerequisite.
To merge TPM series, it's better to pick from your tree and TEE
maintainer can ACK patch 1/2 for seamless integration?
[1]
https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-09-21 8:10 ` Kuldeep Singh
@ 2026-09-25 8:51 ` Jens Wiklander
2026-09-28 8:58 ` Amirreza Zarrabi
0 siblings, 1 reply; 22+ messages in thread
From: Jens Wiklander @ 2026-09-25 8:51 UTC (permalink / raw)
To: Kuldeep Singh, Amirreza Zarrabi
Cc: Jarkko Sakkinen, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
Hi,
On Mon, Sep 21, 2026 at 10:10 AM Kuldeep Singh
<kuldeep.singh@oss.qualcomm.com> wrote:
>
> On 18-09-2026 07:16, Jarkko Sakkinen wrote:
> > On Mon, Sep 07, 2026 at 02:58:41PM +0530, Kuldeep Singh wrote:
> >> Add "qcom.tz.tpm" (QCOMTEE_TPM_UID) to qtee_services[] so the TPM TA
> >> is enumerated as a TEE-bus device, allowing a client driver to bind to
> >> it via its generated UUID.
> >>
> >> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> [...]
>
> >
> > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
> Thanks Jarkko for the review.
> Can TEE maintainers(Amirreza/Jens?) also review this change?
Amir, please review.
Cheers,
Jens
>
> --
> Regards
> Kuldeep
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-09-21 8:36 ` Kuldeep Singh
@ 2026-09-25 14:46 ` Jarkko Sakkinen
2026-09-28 9:34 ` Kuldeep Singh
0 siblings, 1 reply; 22+ messages in thread
From: Jarkko Sakkinen @ 2026-09-25 14:46 UTC (permalink / raw)
To: Kuldeep Singh
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On Mon, Sep 21, 2026 at 02:06:43PM +0530, Kuldeep Singh wrote:
> > Causes merge conflicts with my tree when applied with git am (actually
> > b4 shazam).
>
> Jarkko, this series was sent based on tag next-20260828 after applying
> prerequisite series[1]. Some minor Makefile/MAINTAINERS conflict was
> observed IIRC.
>
> Kindly note for merge strategy, prerequisite series[1] will be merged
> via TEE tree and current series need patch 1-5(not patch6) only as
> prerequisite.
>
> To merge TPM series, it's better to pick from your tree and TEE
> maintainer can ACK patch 1/2 for seamless integration?
So I'm just having trouble following so: what is the best option
for you? What do you want me to do? Which route you prefer?
>
> [1]
> https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
>
> --
> Regards
> Kuldeep
>
Br, Jarkko
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-09-25 8:51 ` Jens Wiklander
@ 2026-09-28 8:58 ` Amirreza Zarrabi
0 siblings, 0 replies; 22+ messages in thread
From: Amirreza Zarrabi @ 2026-09-28 8:58 UTC (permalink / raw)
To: Jens Wiklander, Kuldeep Singh
Cc: Jarkko Sakkinen, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
Hi Jens,
On 9/25/2026 6:51 PM, Jens Wiklander wrote:
> Hi,
>
> On Mon, Sep 21, 2026 at 10:10 AM Kuldeep Singh
> <kuldeep.singh@oss.qualcomm.com> wrote:
>>
>> On 18-09-2026 07:16, Jarkko Sakkinen wrote:
>>> On Mon, Sep 07, 2026 at 02:58:41PM +0530, Kuldeep Singh wrote:
>>>> Add "qcom.tz.tpm" (QCOMTEE_TPM_UID) to qtee_services[] so the TPM TA
>>>> is enumerated as a TEE-bus device, allowing a client driver to bind to
>>>> it via its generated UUID.
>>>>
>>>> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
>> [...]
>>
>>>
>>> Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
>> Thanks Jarkko for the review.
>> Can TEE maintainers(Amirreza/Jens?) also review this change?
>
> Amir, please review.
>
Sorry for the delay. I'll do.
Regards,
Amir
> Cheers,
> Jens
>
>>
>> --
>> Regards
>> Kuldeep
>>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-09-25 14:46 ` Jarkko Sakkinen
@ 2026-09-28 9:34 ` Kuldeep Singh
0 siblings, 0 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-09-28 9:34 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jason Gunthorpe, linux-arm-msm, op-tee, linux-kernel,
linux-integrity
On 25-09-2026 20:16, Jarkko Sakkinen wrote:
> On Mon, Sep 21, 2026 at 02:06:43PM +0530, Kuldeep Singh wrote:
>>> Causes merge conflicts with my tree when applied with git am (actually
>>> b4 shazam).
>>
>> Jarkko, this series was sent based on tag next-20260828 after applying
>> prerequisite series[1]. Some minor Makefile/MAINTAINERS conflict was
>> observed IIRC.
>>
>> Kindly note for merge strategy, prerequisite series[1] will be merged
>> via TEE tree and current series need patch 1-5(not patch6) only as
>> prerequisite.
>>
>> To merge TPM series, it's better to pick from your tree and TEE
>> maintainer can ACK patch 1/2 for seamless integration?
>
> So I'm just having trouble following so: what is the best option
> for you? What do you want me to do? Which route you prefer?
I took latest linux-next (based on tag next-20260925) and tried b4
shazam and observed minor merge conflict in patch 6/6 of dependent
series[1] in drivers/firmware/qcom/Makefile.
Makefile conflict was easy to do as seems recently qcom-pas framework[2]
merged which updated the Makefile.
Also, I don't think we can merge tpm series once dependent series[1]
patch 1-5 merges. I initiated thread to atleast conclude 1-5 so that tpm
series can be picked completely from your tree.
[1]
https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
[2]
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/firmware/qcom/Makefile#n12
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver
2026-09-07 9:28 ` [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver Kuldeep Singh
2026-09-18 1:48 ` Jarkko Sakkinen
@ 2026-09-28 23:29 ` Amirreza Zarrabi
2026-10-07 9:20 ` Kuldeep Singh
1 sibling, 1 reply; 22+ messages in thread
From: Amirreza Zarrabi @ 2026-09-28 23:29 UTC (permalink / raw)
To: Kuldeep Singh, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity
Hi Kuldeep,
Sorry for late review.
On 9/7/2026 7:28 PM, Kuldeep Singh wrote:
> Add a TPM chip driver for platforms where a TPM 2.0 instance is
> implemented by a Trusted Application (TA) running in Qualcomm's Trusted
> Execution Environment (QTEE), reachable over the QCOMTEE object-IPC
> transport.
>
> The driver discovers the qcom.tz.tpm TEE-bus device, opens a session
> with the TPM TA, and register with tpm interface. This exposes the TA
> through the standard /dev/tpm interface and the existing tpm2 command
> layer. OS need not be aware underlying TPM instance is dTPM or fTPM.
>
> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
> ---
> drivers/char/tpm/Kconfig | 9 ++
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm_qcom.h | 83 +++++++++++
> 4 files changed, 447 insertions(+)
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 5f672f2c01b0..05d704ed3632 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -243,6 +243,15 @@ config TCG_FTPM_TEE
> help
> This driver proxies for firmware TPM running in TEE.
>
> +config TCG_QCOM
> + tristate "Qualcomm TEE based TPM Interface"
> + depends on QCOMTEE
> + help
> + This driver provides interface to run TPM instances with Trustzone
> + having Qualcomm TPM TA running in Qualcomm TEE.
> + The mechanism uses the object-IPC based transport provided by
> + QCOMTEE.
> +
> config TCG_SVSM
> tristate "SNP SVSM vTPM interface"
> depends on AMD_MEM_ENCRYPT
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 5b5cdc0d32e4..471cbf49afd2 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -45,5 +45,6 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o
> obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
> obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
> obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
> +obj-$(CONFIG_TCG_QCOM) += tpm_qcom.o
> obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
> obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o
> diff --git a/drivers/char/tpm/tpm_qcom.c b/drivers/char/tpm/tpm_qcom.c
> new file mode 100644
> index 000000000000..ef29c66f18ae
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.c
> @@ -0,0 +1,354 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + */
> +
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/tee.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +#include "tpm.h"
> +#include "tpm_qcom.h"
> +
> +/* UUID of the QTEE-bus device representing the TPM TA. */
> +static const uuid_t tpm_qcom_uuid =
> + UUID_INIT(0xaabcb593, 0x7083, 0x5536,
> + 0xac, 0x27, 0x3d, 0x2d, 0x89, 0x41, 0x9d, 0xdb);
> +
> +static void tpm_qcom_release_object(struct tee_context *ctx,
> + struct tee_param_objref object)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> +
> + inv_arg.id = object.id;
> + inv_arg.op = QCOMTEE_MSG_OBJECT_OP_RELEASE;
> + inv_arg.num_params = 0;
> +
> + tee_client_object_invoke_func(ctx, &inv_arg, NULL);
> +}
> +
> +static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
> + struct tee_param_objref *client_env_obj)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[2] = {};
> + int ret;
> +
> + inv_arg.id = TEE_OBJREF_NULL;
> + inv_arg.op = QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS;
> + inv_arg.num_params = 2;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT;
> + param[0].u.objref.id = TEE_OBJREF_NULL;
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
> +
> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0)
> + return ret ?: inv_arg.ret;
These two functions are local. Do we really care about the return value?
No caller seems to check it. Why not return `TEE_OBJREF_NULL` on failure instead?
> +
> + *client_env_obj = param[1].u.objref;
> + return ret;
> +}
> +
> +static int tpm_qcom_get_svc_obj(struct tee_context *ctx,
> + struct tee_param_objref client_env_obj,
> + struct tee_param_objref *tpm_svc_obj)
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[2] = {};
> + u32 tpm_uid = QCOMTEE_TPM_UID;
> + int ret;
> +
> + inv_arg.id = client_env_obj.id;
> + inv_arg.op = QCOMTEE_OP_CLIENT_ENV_OPEN;
> + inv_arg.num_params = 2;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &tpm_uid,
> + .size = sizeof(tpm_uid) };
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
> +
> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0)
> + return ret ?: inv_arg.ret;
> +
> + *tpm_svc_obj = param[1].u.objref;
> + return ret;
> +}
> +
> +static int tpm_qcom_send_command(struct tpm_qcom_private *pvt_data,
> + u32 locality, void *req, size_t req_len,
> + void *rsp, size_t *rsp_len)
This function seems to return both negative and positive values,
with different translations. This can cause issues; see the bug in tpm_qcom_send().
Maybe this should be documented?
> +{
> + struct tee_ioctl_object_invoke_arg inv_arg = {};
> + struct tee_param param[3] = {};
> + u8 locality_arg = locality;
What is the `locality` arg if it is always zero? planning for future?
Why not `u8 locality_arg = 0`?
> + int ret;
> +
> + inv_arg.id = pvt_data->tpm_svc_obj.id;
> + inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
> + inv_arg.num_params = 3;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &locality_arg,
> + .size = sizeof(locality_arg) };
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[1].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
> + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
> + param[2].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
> +
> + ret = tee_client_object_invoke_func(pvt_data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(pvt_data->dev,
> + "send_command invoke ret: %d, err: 0x%x\n",
> + ret, inv_arg.ret);
> + return ret ?: inv_arg.ret;
> + }
> +
> + *rsp_len = param[2].u.ubuf.size;
> +
> + return ret;
> +}
> +
> +static int tpm_qcom_get_ta_details(struct tpm_qcom_private *pvt_data)
> +{
> + struct tpm_qcom_ta_version_req ver_req = {
> + .command_id = QCOMTEE_TPM_GET_TA_VERSION_ID,
> + };
> + struct tpm_qcom_ta_version_rsp ver_rsp;
> + size_t ver_rsp_len = sizeof(ver_rsp);
> + struct tpm_qcom_type_req type_req = {
> + .command_id = QCOMTEE_TPM_TYPE_ID,
> + };
> + struct tpm_qcom_type_rsp type_rsp;
> + size_t type_rsp_len = sizeof(type_rsp);
> + int ret;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &ver_req, sizeof(ver_req),
> + &ver_rsp, &ver_rsp_len);
> + if (ret || ver_rsp_len < sizeof(ver_rsp) || ver_rsp.status != 0) {
> + dev_err(pvt_data->dev,
> + "failed to query TA version: ret=%d, status=%u\n",
> + ret, ret ? 0 : ver_rsp.status);
> + return ret ?: -EIO;
> + }
You already print in tpm_qcom_send_command(), why here again.
> +
> + dev_info(pvt_data->dev, "TPM TA version %lu.%lu\n",
> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MAJOR, ver_rsp.version_num),
> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MINOR, ver_rsp.version_num));
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &type_req, sizeof(type_req),
> + &type_rsp, &type_rsp_len);
> + if (ret || type_rsp_len < sizeof(type_rsp) || type_rsp.status != 0) {
> + dev_err(pvt_data->dev,
> + "failed to query TPM type: ret=%d, status=%u\n",
> + ret, ret ? 0 : type_rsp.status);
> + return ret ?: -EIO;
> + }
You already print in tpm_qcom_send_command(), why here again.
> +
> + switch (type_rsp.tpm_type) {
> + case QCOMTEE_TPM_TYPE_FTPM:
> + dev_info(pvt_data->dev, "TPM type: fTPM\n");
> + pvt_data->is_dtpm = false;
> + break;
> + case QCOMTEE_TPM_TYPE_DTPM:
> + dev_info(pvt_data->dev, "TPM type: dTPM\n");
> + pvt_data->is_dtpm = true;
> + break;
> + default:
> + dev_err(pvt_data->dev, "unsupported TPM type: 0x%08x\n",
> + type_rsp.tpm_type);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +/* fTPM does not implement this command and to be invoked via dtpm only. */
> +static void tpm_qcom_transfer(struct tpm_qcom_private *pvt_data,
> + u32 transfer_state)
> +{
> + struct tpm_qcom_transfer_req req = {
> + .command_id = QCOMTEE_TPM_TRANSFER_ID,
> + .transfer_state = transfer_state,
> + };
> + struct tpm_qcom_transfer_rsp rsp;
> + size_t rsp_len = sizeof(rsp);
> + int ret;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, &req, sizeof(req), &rsp,
> + &rsp_len);
> + if (ret || rsp_len < sizeof(rsp) || rsp.status != 0)
> + dev_warn(pvt_data->dev,
> + "transfer state=%u hint failed: ret=%d, status=%u\n",
> + transfer_state, ret, ret ? 0 : rsp.status);
You already print in tpm_qcom_send_command(), why here again.
If you remove the message, I also argue the function is not required.
Directly call tpm_qcom_send_command() bellow.
> +}
> +
> +static int tpm_qcom_cmd_ready(struct tpm_chip *chip)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> +
> + if (pvt_data->is_dtpm)
> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_START);
Is it intentional to ignore failures here and in the next function, and always return success?
Are these functions best-effort, such that failures are considered irrelevant?
> +
> + return 0;
> +}
> +
> +static int tpm_qcom_go_idle(struct tpm_chip *chip)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> +
> + if (pvt_data->is_dtpm)
> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_END);
> +
> + return 0;
> +}
> +
> +/*
> + * The raw TPM2 command in @buf is sent directly as send_command's UBUF-in
> + * param and the raw TPM2 response is read back from its UBUF-out param.
> + */
> +static int tpm_qcom_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
> + size_t cmd_len)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> + size_t rsp_len = PAGE_ALIGN(MAX_RESPONSE_SIZE);
> + size_t copy_len;
> + int ret;
> +
> + if (cmd_len > MAX_COMMAND_SIZE) {
> + dev_err(&chip->dev, "len=%zd exceeds MAX_COMMAND_SIZE\n", cmd_len);
> + return -EIO;
> + }
> +
> + u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
> + if (!response)
> + return -ENOMEM;
> +
> + ret = tpm_qcom_send_command(pvt_data, 0, buf, cmd_len, response,
> + &rsp_len);
> + if (ret < 0) {
This does not seem right; tpm_qcom_send_command() can return positive on failure.
How about tpm_qcom_ops.send?
> + dev_err(&chip->dev, "send_command failed: ret=%d\n", ret);
> + return ret;
> + }
> +
> + copy_len = min_t(size_t, bufsiz, rsp_len);
> + memcpy(buf, response, copy_len);
> +
> + return copy_len;
> +}
> +
> +static const struct tpm_class_ops tpm_qcom_ops = {
> + .flags = TPM_OPS_AUTO_STARTUP,
> + .send = tpm_qcom_send,
> + .cmd_ready = tpm_qcom_cmd_ready,
> + .go_idle = tpm_qcom_go_idle,
> +};
> +
> +static int tpm_qcom_ctx_match(struct tee_ioctl_version_data *ver,
> + const void *data)
> +{
> + return (ver->impl_id == TEE_IMPL_ID_QTEE);
> +}
> +
> +static int tpm_qcom_probe(struct tee_client_device *tee_dev)
> +{
> + struct device *dev = &tee_dev->dev;
> + struct tpm_qcom_private *pvt_data;
> + struct tee_param_objref client_env_obj;
> + struct tee_param_objref tpm_svc_obj;
> + struct tpm_chip *chip;
> + int rc, err;
> +
> + pvt_data = devm_kzalloc(dev, sizeof(*pvt_data), GFP_KERNEL);
> + if (!pvt_data)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, pvt_data);
> +
> + pvt_data->ctx = tee_client_open_context(NULL, tpm_qcom_ctx_match, NULL, NULL);
> + if (IS_ERR(pvt_data->ctx))
> + return -ENODEV;
> +
> + rc = tpm_qcom_get_client_env_obj(pvt_data->ctx, &client_env_obj);
> + if (rc) {
> + err = -EINVAL;
> + goto out_ctx;
> + }
> +
> + rc = tpm_qcom_get_svc_obj(pvt_data->ctx, client_env_obj, &tpm_svc_obj);
> + if (rc) {
> + err = -EINVAL;
> + goto out_client_env;
> + }
> + pvt_data->tpm_svc_obj = tpm_svc_obj;
> + pvt_data->dev = dev;
> +
> + err = tpm_qcom_get_ta_details(pvt_data);
> + if (err)
> + goto out_svc_obj;
> +
> + chip = tpm_chip_alloc(dev, &tpm_qcom_ops);
> + if (IS_ERR(chip)) {
> + dev_err(dev, "tpm_chip_alloc failed\n");
> + err = PTR_ERR(chip);
> + goto out_svc_obj;
> + }
> +
> + pvt_data->chip = chip;
> + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC;
> +
> + err = tpm_chip_register(pvt_data->chip);
> + if (err) {
> + dev_err(dev, "tpm_chip_register failed with rc=%d\n", err);
> + goto out_chip;
> + }
> +
> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
> + return 0;
> +
> +out_chip:
> + put_device(&pvt_data->chip->dev);
> +out_svc_obj:
> + tpm_qcom_release_object(pvt_data->ctx, tpm_svc_obj);
> +out_client_env:
> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
> +out_ctx:
> + tee_client_close_context(pvt_data->ctx);
> + return err;
> +}
> +
> +static void tpm_qcom_remove(struct tee_client_device *tee_dev)
> +{
> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(&tee_dev->dev);
> +
> + tpm_chip_unregister(pvt_data->chip);
> + put_device(&pvt_data->chip->dev);
Is there any reason you do not use tpmm_chip_alloc() and directly call put_device?
> + tpm_qcom_release_object(pvt_data->ctx, pvt_data->tpm_svc_obj);
> + tee_client_close_context(pvt_data->ctx);
> +}
> +
> +static const struct tee_client_device_id tpm_qcom_id_table[] = {
> + { tpm_qcom_uuid },
> + {}
> +};
> +MODULE_DEVICE_TABLE(tee, tpm_qcom_id_table);
> +
> +static struct tee_client_driver tpm_qcom_driver = {
> + .id_table = tpm_qcom_id_table,
> + .probe = tpm_qcom_probe,
> + .remove = tpm_qcom_remove,
> + .driver = {
> + .name = "tpm_qcom",
> + },
> +};
> +
> +module_tee_client_driver(tpm_qcom_driver);
> +
> +MODULE_DESCRIPTION("TPM driver for Qualcomm TPM TA");
> +MODULE_AUTHOR("Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/char/tpm/tpm_qcom.h b/drivers/char/tpm/tpm_qcom.h
> new file mode 100644
> index 000000000000..3945b12643b7
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.h
> @@ -0,0 +1,83 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __TPM_QCOM_H__
> +#define __TPM_QCOM_H__
As most of these are only consumed in qcom_tpm.c, it makes more sense to
move them and drop the header. Unless you have some reason.
> +
> +#include <linux/bitfield.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5
> +#define QCOMTEE_OP_CLIENT_ENV_OPEN 0
> +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0)
> +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0)
> +
> +#define QCOMTEE_TPM_OP_SEND_COMMAND 0
> +
> +/* UID of the "qcom.tz.tpm" service */
> +#define QCOMTEE_TPM_UID 81
> +
> +/* Max buffer size supported by TPM TA */
> +#define MAX_COMMAND_SIZE SZ_4K
> +#define MAX_RESPONSE_SIZE SZ_4K
These names are confusing, rename to something like
`QCOM_TPM_MAX_COMMAND_SIZE` and `QCOM_TPM_MAX_RESPONSE_SIZE`.
> +
> +#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
> +#define QCOMTEE_TPM_TA_VERSION_MAJOR GENMASK(31, 16)
> +#define QCOMTEE_TPM_TA_VERSION_MINOR GENMASK(15, 0)
> +
> +struct tpm_qcom_ta_version_req {
> + u32 command_id;
> +} __packed;
> +
> +struct tpm_qcom_ta_version_rsp {
> + u32 status;
> + u32 command_id;
> + u32 version_num;
> +} __packed;
> +
> +#define QCOMTEE_TPM_TYPE_ID 0x0080000
> +#define QCOMTEE_TPM_TYPE_DTPM 0x6454504dU
> +#define QCOMTEE_TPM_TYPE_FTPM 0x6654504dU
> +#define QCOMTEE_TPM_TYPE_NONE 0x4e6f6e65U
> +
> +struct tpm_qcom_type_req {
> + u32 command_id;
> +} __packed;
> +
> +struct tpm_qcom_type_rsp {
> + u32 command_id;
> + u32 status;
> + u32 tpm_type;
> +} __packed;
> +
> +/*
> + * dTPM SPI transfer optimization:
> + * TRANSFER_START before a burst of commands, TRANSFER_END once done.
> + */
> +#define QCOMTEE_TPM_TRANSFER_ID 0x0000002
> +#define QCOMTEE_TPM_TRANSFER_END 0
> +#define QCOMTEE_TPM_TRANSFER_START 1
> +
> +struct tpm_qcom_transfer_req {
> + u32 command_id;
> + u32 transfer_state;
> +} __packed;
> +
> +struct tpm_qcom_transfer_rsp {
nitpik: can you use `resp` instead of `rsp`?
> + u32 command_id;
> + u32 status;
> +} __packed;
> +
> +struct tpm_qcom_private {
> + struct tpm_chip *chip;
> + struct device *dev;
> + struct tee_context *ctx;
> + struct tee_param_objref tpm_svc_obj;
> + bool is_dtpm;
> +};
> +
> +#endif /* __TPM_QCOM_H__ */
>
Best Regards,
Amir
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver
2026-09-28 23:29 ` Amirreza Zarrabi
@ 2026-10-07 9:20 ` Kuldeep Singh
0 siblings, 0 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-10-07 9:20 UTC (permalink / raw)
To: Amirreza Zarrabi, Jens Wiklander, Sumit Garg, Peter Huewe,
Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity
On 29-09-2026 04:59, Amirreza Zarrabi wrote:
> Hi Kuldeep,
>
> Sorry for late review.
>
> On 9/7/2026 7:28 PM, Kuldeep Singh wrote:
>> Add a TPM chip driver for platforms where a TPM 2.0 instance is
>> implemented by a Trusted Application (TA) running in Qualcomm's Trusted
>> Execution Environment (QTEE), reachable over the QCOMTEE object-IPC
>> transport.
>>
>> The driver discovers the qcom.tz.tpm TEE-bus device, opens a session
>> with the TPM TA, and register with tpm interface. This exposes the TA
>> through the standard /dev/tpm interface and the existing tpm2 command
>> layer. OS need not be aware underlying TPM instance is dTPM or fTPM.
>>
>> Signed-off-by: Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>
>> ---
>> drivers/char/tpm/Kconfig | 9 ++
>> drivers/char/tpm/Makefile | 1 +
>> drivers/char/tpm/tpm_qcom.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
>> drivers/char/tpm/tpm_qcom.h | 83 +++++++++++
>> 4 files changed, 447 insertions(+)
>>
>> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
>> index 5f672f2c01b0..05d704ed3632 100644
>> --- a/drivers/char/tpm/Kconfig
>> +++ b/drivers/char/tpm/Kconfig
>> @@ -243,6 +243,15 @@ config TCG_FTPM_TEE
>> help
>> This driver proxies for firmware TPM running in TEE.
>>
>> +config TCG_QCOM
>> + tristate "Qualcomm TEE based TPM Interface"
>> + depends on QCOMTEE
>> + help
>> + This driver provides interface to run TPM instances with Trustzone
>> + having Qualcomm TPM TA running in Qualcomm TEE.
>> + The mechanism uses the object-IPC based transport provided by
>> + QCOMTEE.
>> +
>> config TCG_SVSM
>> tristate "SNP SVSM vTPM interface"
>> depends on AMD_MEM_ENCRYPT
>> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
>> index 5b5cdc0d32e4..471cbf49afd2 100644
>> --- a/drivers/char/tpm/Makefile
>> +++ b/drivers/char/tpm/Makefile
>> @@ -45,5 +45,6 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o
>> obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
>> obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
>> obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
>> +obj-$(CONFIG_TCG_QCOM) += tpm_qcom.o
>> obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
>> obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o
>> diff --git a/drivers/char/tpm/tpm_qcom.c b/drivers/char/tpm/tpm_qcom.c
>> new file mode 100644
>> index 000000000000..ef29c66f18ae
>> --- /dev/null
>> +++ b/drivers/char/tpm/tpm_qcom.c
>> @@ -0,0 +1,354 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + *
>> + */
>> +
>> +#include <linux/mm.h>
>> +#include <linux/slab.h>
>> +#include <linux/tee.h>
>> +#include <linux/tee_drv.h>
>> +#include <linux/tpm.h>
>> +#include <linux/uuid.h>
>> +
>> +#include "tpm.h"
>> +#include "tpm_qcom.h"
>> +
>> +/* UUID of the QTEE-bus device representing the TPM TA. */
>> +static const uuid_t tpm_qcom_uuid =
>> + UUID_INIT(0xaabcb593, 0x7083, 0x5536,
>> + 0xac, 0x27, 0x3d, 0x2d, 0x89, 0x41, 0x9d, 0xdb);
>> +
>> +static void tpm_qcom_release_object(struct tee_context *ctx,
>> + struct tee_param_objref object)
>> +{
>> + struct tee_ioctl_object_invoke_arg inv_arg = {};
>> +
>> + inv_arg.id = object.id;
>> + inv_arg.op = QCOMTEE_MSG_OBJECT_OP_RELEASE;
>> + inv_arg.num_params = 0;
>> +
>> + tee_client_object_invoke_func(ctx, &inv_arg, NULL);
>> +}
>> +
>> +static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
>> + struct tee_param_objref *client_env_obj)
>> +{
>> + struct tee_ioctl_object_invoke_arg inv_arg = {};
>> + struct tee_param param[2] = {};
>> + int ret;
>> +
>> + inv_arg.id = TEE_OBJREF_NULL;
>> + inv_arg.op = QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS;
>> + inv_arg.num_params = 2;
>> +
>> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT;
>> + param[0].u.objref.id = TEE_OBJREF_NULL;
>> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
>> +
>> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
>> + if (ret < 0 || inv_arg.ret != 0)
>> + return ret ?: inv_arg.ret;
>
> These two functions are local. Do we really care about the return value?
> No caller seems to check it. Why not return `TEE_OBJREF_NULL` on failure instead?
Ok, I think we can return common error but still need some error log
which will point to exact failure. Maybe I'll add log to print
ret/inv_arg.ret and return TEE_OBJREF_NULL directly. Sounds good?
>
>> +
>> + *client_env_obj = param[1].u.objref;
>> + return ret;
>> +}
>> +
>> +static int tpm_qcom_get_svc_obj(struct tee_context *ctx,
>> + struct tee_param_objref client_env_obj,
>> + struct tee_param_objref *tpm_svc_obj)
>> +{
>> + struct tee_ioctl_object_invoke_arg inv_arg = {};
>> + struct tee_param param[2] = {};
>> + u32 tpm_uid = QCOMTEE_TPM_UID;
>> + int ret;
>> +
>> + inv_arg.id = client_env_obj.id;
>> + inv_arg.op = QCOMTEE_OP_CLIENT_ENV_OPEN;
>> + inv_arg.num_params = 2;
>> +
>> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
>> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &tpm_uid,
>> + .size = sizeof(tpm_uid) };
>> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT;
>> +
>> + ret = tee_client_object_invoke_func(ctx, &inv_arg, param);
>> + if (ret < 0 || inv_arg.ret != 0)
>> + return ret ?: inv_arg.ret;
>> +
>> + *tpm_svc_obj = param[1].u.objref;
>> + return ret;
>> +}
>> +
>> +static int tpm_qcom_send_command(struct tpm_qcom_private *pvt_data,
>> + u32 locality, void *req, size_t req_len,
>> + void *rsp, size_t *rsp_len)
>
> This function seems to return both negative and positive values,
> with different translations. This can cause issues; see the bug in tpm_qcom_send().
> Maybe this should be documented?
I think we can improve like below:
1. Returning -ve ret value directly.
2. IF ret==0 and inv_arg.ret contains failure, print both values and
return common value i.e -EINVAL to the callers.
This will guarantee error is propagated correctly either from qcomtee/TA
or QTEE.
>> +{
>> + struct tee_ioctl_object_invoke_arg inv_arg = {};
>> + struct tee_param param[3] = {};
>> + u8 locality_arg = locality;
>
> What is the `locality` arg if it is always zero? planning for future?
> Why not `u8 locality_arg = 0`?
Locality arg is an input to idl and it's 0 for calls here.
We can update directly in send_command and no need for caller input here.
>
>> + int ret;
>> +
>> + inv_arg.id = pvt_data->tpm_svc_obj.id;
>> + inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
>> + inv_arg.num_params = 3;
>> +
>> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
>> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = &locality_arg,
>> + .size = sizeof(locality_arg) };
>> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
>> + param[1].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
>> + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
>> + param[2].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
>> +
>> + ret = tee_client_object_invoke_func(pvt_data->ctx, &inv_arg, param);
>> + if (ret < 0 || inv_arg.ret != 0) {
>> + dev_err(pvt_data->dev,
>> + "send_command invoke ret: %d, err: 0x%x\n",
>> + ret, inv_arg.ret);
>> + return ret ?: inv_arg.ret;
>> + }
We can have cmd parameter here and this way we can avoid prints in
callers of tpm_qcom_send_command.
>> +
>> + *rsp_len = param[2].u.ubuf.size;
>> +
>> + return ret;
>> +}
>> +
>> +static int tpm_qcom_get_ta_details(struct tpm_qcom_private *pvt_data)
>> +{
>> + struct tpm_qcom_ta_version_req ver_req = {
>> + .command_id = QCOMTEE_TPM_GET_TA_VERSION_ID,
>> + };
>> + struct tpm_qcom_ta_version_rsp ver_rsp;
>> + size_t ver_rsp_len = sizeof(ver_rsp);
>> + struct tpm_qcom_type_req type_req = {
>> + .command_id = QCOMTEE_TPM_TYPE_ID,
>> + };
>> + struct tpm_qcom_type_rsp type_rsp;
>> + size_t type_rsp_len = sizeof(type_rsp);
>> + int ret;
>> +
>> + ret = tpm_qcom_send_command(pvt_data, 0, &ver_req, sizeof(ver_req),
>> + &ver_rsp, &ver_rsp_len);
>> + if (ret || ver_rsp_len < sizeof(ver_rsp) || ver_rsp.status != 0) {
>> + dev_err(pvt_data->dev,
>> + "failed to query TA version: ret=%d, status=%u\n",
>> + ret, ret ? 0 : ver_rsp.status);
>> + return ret ?: -EIO;
>> + }
>
> You already print in tpm_qcom_send_command(), why here again.
Yeah, we can skip printing here.
>
>> +
>> + dev_info(pvt_data->dev, "TPM TA version %lu.%lu\n",
>> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MAJOR, ver_rsp.version_num),
>> + FIELD_GET(QCOMTEE_TPM_TA_VERSION_MINOR, ver_rsp.version_num));
>> +
>> + ret = tpm_qcom_send_command(pvt_data, 0, &type_req, sizeof(type_req),
>> + &type_rsp, &type_rsp_len);
>> + if (ret || type_rsp_len < sizeof(type_rsp) || type_rsp.status != 0) {
>> + dev_err(pvt_data->dev,
>> + "failed to query TPM type: ret=%d, status=%u\n",
>> + ret, ret ? 0 : type_rsp.status);
>> + return ret ?: -EIO;
>> + }
>
> You already print in tpm_qcom_send_command(), why here again.
Yeah, we can skip printing here.
>
>> +
>> + switch (type_rsp.tpm_type) {
>> + case QCOMTEE_TPM_TYPE_FTPM:
>> + dev_info(pvt_data->dev, "TPM type: fTPM\n");
>> + pvt_data->is_dtpm = false;
>> + break;
>> + case QCOMTEE_TPM_TYPE_DTPM:
>> + dev_info(pvt_data->dev, "TPM type: dTPM\n");
>> + pvt_data->is_dtpm = true;
>> + break;
>> + default:
>> + dev_err(pvt_data->dev, "unsupported TPM type: 0x%08x\n",
>> + type_rsp.tpm_type);
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* fTPM does not implement this command and to be invoked via dtpm only. */
>> +static void tpm_qcom_transfer(struct tpm_qcom_private *pvt_data,
>> + u32 transfer_state)
>> +{
>> + struct tpm_qcom_transfer_req req = {
>> + .command_id = QCOMTEE_TPM_TRANSFER_ID,
>> + .transfer_state = transfer_state,
>> + };
>> + struct tpm_qcom_transfer_rsp rsp;
>> + size_t rsp_len = sizeof(rsp);
>> + int ret;
>> +
>> + ret = tpm_qcom_send_command(pvt_data, 0, &req, sizeof(req), &rsp,
>> + &rsp_len);
>> + if (ret || rsp_len < sizeof(rsp) || rsp.status != 0)
>> + dev_warn(pvt_data->dev,
>> + "transfer state=%u hint failed: ret=%d, status=%u\n",
>> + transfer_state, ret, ret ? 0 : rsp.status);
>
> You already print in tpm_qcom_send_command(), why here again.
> If you remove the message, I also argue the function is not required.
> Directly call tpm_qcom_send_command() bellow.
Seems we can save merge this chunk into tpm_qcom_cmd_ready only.
Let me do it.
>
>> +}
>> +
>> +static int tpm_qcom_cmd_ready(struct tpm_chip *chip)
>> +{
>> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
>> +
>> + if (pvt_data->is_dtpm)
>> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_START);
>
> Is it intentional to ignore failures here and in the next function, and always return success?
> Are these functions best-effort, such that failures are considered irrelevant?
Yes, this is a mechanism to notify TA about making an optimization like
acquiring dtpm spi bus and keeping spi/gpio resources active for
upcoming raw tpm command.
This indeed help for long running commands by keeping spi connections
active and releasing once TRANSFER_END is called.
If we don't make this call, it won't impact the functionality but some
performance penalty will be there.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int tpm_qcom_go_idle(struct tpm_chip *chip)
>> +{
>> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
>> +
>> + if (pvt_data->is_dtpm)
>> + tpm_qcom_transfer(pvt_data, QCOMTEE_TPM_TRANSFER_END);
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * The raw TPM2 command in @buf is sent directly as send_command's UBUF-in
>> + * param and the raw TPM2 response is read back from its UBUF-out param.
>> + */
>> +static int tpm_qcom_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
>> + size_t cmd_len)
>> +{
>> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(chip->dev.parent);
>> + size_t rsp_len = PAGE_ALIGN(MAX_RESPONSE_SIZE);
>> + size_t copy_len;
>> + int ret;
>> +
>> + if (cmd_len > MAX_COMMAND_SIZE) {
>> + dev_err(&chip->dev, "len=%zd exceeds MAX_COMMAND_SIZE\n", cmd_len);
>> + return -EIO;
>> + }
>> +
>> + u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
>> + if (!response)
>> + return -ENOMEM;
>> +
>> + ret = tpm_qcom_send_command(pvt_data, 0, buf, cmd_len, response,
>> + &rsp_len);
>> + if (ret < 0) {
>
> This does not seem right; tpm_qcom_send_command() can return positive on failure.
> How about tpm_qcom_ops.send?
Mentioned below, we can update return value to propagate errors
correctly to userspace layer or the callers.
>
>> + dev_err(&chip->dev, "send_command failed: ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + copy_len = min_t(size_t, bufsiz, rsp_len);
>> + memcpy(buf, response, copy_len);
>> +
>> + return copy_len;
>> +}
>> +
>> +static const struct tpm_class_ops tpm_qcom_ops = {
>> + .flags = TPM_OPS_AUTO_STARTUP,
>> + .send = tpm_qcom_send,
>> + .cmd_ready = tpm_qcom_cmd_ready,
>> + .go_idle = tpm_qcom_go_idle,
>> +};
>> +
>> +static int tpm_qcom_ctx_match(struct tee_ioctl_version_data *ver,
>> + const void *data)
>> +{
>> + return (ver->impl_id == TEE_IMPL_ID_QTEE);
>> +}
>> +
>> +static int tpm_qcom_probe(struct tee_client_device *tee_dev)
>> +{
>> + struct device *dev = &tee_dev->dev;
>> + struct tpm_qcom_private *pvt_data;
>> + struct tee_param_objref client_env_obj;
>> + struct tee_param_objref tpm_svc_obj;
>> + struct tpm_chip *chip;
>> + int rc, err;
>> +
>> + pvt_data = devm_kzalloc(dev, sizeof(*pvt_data), GFP_KERNEL);
>> + if (!pvt_data)
>> + return -ENOMEM;
>> +
>> + dev_set_drvdata(dev, pvt_data);
>> +
>> + pvt_data->ctx = tee_client_open_context(NULL, tpm_qcom_ctx_match, NULL, NULL);
>> + if (IS_ERR(pvt_data->ctx))
>> + return -ENODEV;
>> +
>> + rc = tpm_qcom_get_client_env_obj(pvt_data->ctx, &client_env_obj);
>> + if (rc) {
>> + err = -EINVAL;
>> + goto out_ctx;
>> + }
>> +
>> + rc = tpm_qcom_get_svc_obj(pvt_data->ctx, client_env_obj, &tpm_svc_obj);
>> + if (rc) {
>> + err = -EINVAL;
>> + goto out_client_env;
>> + }
>> + pvt_data->tpm_svc_obj = tpm_svc_obj;
>> + pvt_data->dev = dev;
>> +
>> + err = tpm_qcom_get_ta_details(pvt_data);
>> + if (err)
>> + goto out_svc_obj;
>> +
>> + chip = tpm_chip_alloc(dev, &tpm_qcom_ops);
>> + if (IS_ERR(chip)) {
>> + dev_err(dev, "tpm_chip_alloc failed\n");
>> + err = PTR_ERR(chip);
>> + goto out_svc_obj;
>> + }
>> +
>> + pvt_data->chip = chip;
>> + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC;
>> +
>> + err = tpm_chip_register(pvt_data->chip);
>> + if (err) {
>> + dev_err(dev, "tpm_chip_register failed with rc=%d\n", err);
>> + goto out_chip;
>> + }
>> +
>> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
>> + return 0;
>> +
>> +out_chip:
>> + put_device(&pvt_data->chip->dev);
>> +out_svc_obj:
>> + tpm_qcom_release_object(pvt_data->ctx, tpm_svc_obj);
>> +out_client_env:
>> + tpm_qcom_release_object(pvt_data->ctx, client_env_obj);
>> +out_ctx:
>> + tee_client_close_context(pvt_data->ctx);
>> + return err;
>> +}
>> +
>> +static void tpm_qcom_remove(struct tee_client_device *tee_dev)
>> +{
>> + struct tpm_qcom_private *pvt_data = dev_get_drvdata(&tee_dev->dev);
>> +
>> + tpm_chip_unregister(pvt_data->chip);
>> + put_device(&pvt_data->chip->dev);
>
> Is there any reason you do not use tpmm_chip_alloc() and directly call put_device?
I explored and seems tpmm_chip_alloc is devm managed so put_device can
be skipped but tpm_chip_unregister will still need to be called.
>
>> + tpm_qcom_release_object(pvt_data->ctx, pvt_data->tpm_svc_obj);
>> + tee_client_close_context(pvt_data->ctx);
>> +}
>> +
>> +static const struct tee_client_device_id tpm_qcom_id_table[] = {
>> + { tpm_qcom_uuid },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(tee, tpm_qcom_id_table);
>> +
>> +static struct tee_client_driver tpm_qcom_driver = {
>> + .id_table = tpm_qcom_id_table,
>> + .probe = tpm_qcom_probe,
>> + .remove = tpm_qcom_remove,
>> + .driver = {
>> + .name = "tpm_qcom",
>> + },
>> +};
>> +
>> +module_tee_client_driver(tpm_qcom_driver);
>> +
>> +MODULE_DESCRIPTION("TPM driver for Qualcomm TPM TA");
>> +MODULE_AUTHOR("Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/char/tpm/tpm_qcom.h b/drivers/char/tpm/tpm_qcom.h
>> new file mode 100644
>> index 000000000000..3945b12643b7
>> --- /dev/null
>> +++ b/drivers/char/tpm/tpm_qcom.h
>> @@ -0,0 +1,83 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + */
>> +
>> +#ifndef __TPM_QCOM_H__
>> +#define __TPM_QCOM_H__
>
> As most of these are only consumed in qcom_tpm.c, it makes more sense to
> move them and drop the header. Unless you have some reason.
Yeah makes sense.
We can have it in tpm_qcom.c only.
>
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/tee_drv.h>
>> +#include <linux/tpm.h>
>> +#include <linux/uuid.h>
>> +
>> +#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5
>> +#define QCOMTEE_OP_CLIENT_ENV_OPEN 0
>> +#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0)
>> +#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0)
>> +
>> +#define QCOMTEE_TPM_OP_SEND_COMMAND 0
>> +
>> +/* UID of the "qcom.tz.tpm" service */
>> +#define QCOMTEE_TPM_UID 81
>> +
>> +/* Max buffer size supported by TPM TA */
>> +#define MAX_COMMAND_SIZE SZ_4K
>> +#define MAX_RESPONSE_SIZE SZ_4K
>
> These names are confusing, rename to something like
> `QCOM_TPM_MAX_COMMAND_SIZE` and `QCOM_TPM_MAX_RESPONSE_SIZE`.
Sure.
>
>> +
>> +#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
>> +#define QCOMTEE_TPM_TA_VERSION_MAJOR GENMASK(31, 16)
>> +#define QCOMTEE_TPM_TA_VERSION_MINOR GENMASK(15, 0)
>> +
>> +struct tpm_qcom_ta_version_req {
>> + u32 command_id;
>> +} __packed;
>> +
>> +struct tpm_qcom_ta_version_rsp {
>> + u32 status;
>> + u32 command_id;
>> + u32 version_num;
>> +} __packed;
>> +
>> +#define QCOMTEE_TPM_TYPE_ID 0x0080000
>> +#define QCOMTEE_TPM_TYPE_DTPM 0x6454504dU
>> +#define QCOMTEE_TPM_TYPE_FTPM 0x6654504dU
>> +#define QCOMTEE_TPM_TYPE_NONE 0x4e6f6e65U
>> +
>> +struct tpm_qcom_type_req {
>> + u32 command_id;
>> +} __packed;
>> +
>> +struct tpm_qcom_type_rsp {
>> + u32 command_id;
>> + u32 status;
>> + u32 tpm_type;
>> +} __packed;
>> +
>> +/*
>> + * dTPM SPI transfer optimization:
>> + * TRANSFER_START before a burst of commands, TRANSFER_END once done.
>> + */
>> +#define QCOMTEE_TPM_TRANSFER_ID 0x0000002
>> +#define QCOMTEE_TPM_TRANSFER_END 0
>> +#define QCOMTEE_TPM_TRANSFER_START 1
>> +
>> +struct tpm_qcom_transfer_req {
>> + u32 command_id;
>> + u32 transfer_state;
>> +} __packed;
>> +
>> +struct tpm_qcom_transfer_rsp {
>
> nitpik: can you use `resp` instead of `rsp`?
Umm, okay.
>
>> + u32 command_id;
>> + u32 status;
>> +} __packed;
>> +
>> +struct tpm_qcom_private {
>> + struct tpm_chip *chip;
>> + struct device *dev;
>> + struct tee_context *ctx;
>> + struct tee_param_objref tpm_svc_obj;
>> + bool is_dtpm;
>> +};
>> +
>> +#endif /* __TPM_QCOM_H__ */
>>
>
> Best Regards,
> Amir
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-10-05 7:24 ` Dmitry Baryshkov
2026-10-05 12:00 ` Kuldeep Singh
@ 2026-10-07 13:40 ` Xilin Wu
1 sibling, 0 replies; 22+ messages in thread
From: Xilin Wu @ 2026-10-07 13:40 UTC (permalink / raw)
To: Dmitry Baryshkov, Kuldeep Singh
Cc: zeroknots, amirreza.zarrabi, jarkko, jenswi, jgg, linux-arm-msm,
linux-integrity, linux-kernel, op-tee, peterhuewe, sumit.garg
On 10/5/2026 3:24 PM, Dmitry Baryshkov wrote:
> On Mon, Oct 05, 2026 at 12:27:40PM +0530, Kuldeep Singh wrote:
>> On 01-10-2026 11:20, zeroknots wrote:
>>> Hi Kuldeep,
>>>
>>> A data point from retail hardware, in case it is useful for this
>>> series: on an ASUS Zenbook A14 UX3407NA (Glymur, X2E-88-100, BIOS
>>> UX3407NA.315) the TPM TA is not reachable as QTEE service 81, but it
>>> is present as the QSEECOM application "qcom.tz.tpm".
>>
>> Thanks for reaching out, Kindly check below.
>>
>>> With the qcomtee driver on a 7.3-rc3 based kernel, QTEE reports
>>> version 5.2.0, and service discovery finds neither 81 (TPM) nor 413
>>> (UEFI secure app). On the same boot, qseecom (version 0x1402000) works
>>> and backs efivars through "qcom.tz.uefisecapp" (app id 7). An app-id
>>> lookup for "qcom.tz.tpm" returns app id 1; a made-up name returns
>>> -ENOENT.
>>>
>>> The Windows driver for this machine agrees: QcTrEE8480.inf configures
>>> the TPM service with AppName="qcom.tz.tpm", SecureApp=1, LoadApp=0
>>> (preloaded by firmware). The EFI configuration table carries the
>>> TPMEventLog and TPMFinalLog entries, so the firmware TPM is active.
>>>
>>> Through that app, using a QSEECOM transport (based on Xilin Wu's
>>> out-of-tree SC8280XP driver: QUERY_INFO_2 / SEND_COMMAND with a
>>> CRB-style control area, here at 0x81d10000), /dev/tpm0 works: TPM 2.0,
>>> manufacturer QCOM, vendor string "xCG fTPM", firmware 0x40000, real
>>> PCR 0-7 values, GetRandom, ECC and RSA-2048 primaries, sign and
>>> verify, and a sealed object that persists across reboots.
>>>
>>> So, as far as I can tell, retail Glymur laptops may ship firmware on
>>> which this driver finds no TPM, while the same TA is available over
>>> QSEECOM.
>>>
>>> The same applies to the prerequisite series that moves uefisecapp to
>>> QCOMTEE [1]: on this firmware service 413 is absent and EFI variables
>>> work only through the QSEECOM uefisecapp. If the QSEECOM path were
>>> dropped for Glymur, efivars would stop working on this machine, so it
>>> would be good to keep it as a fallback when the QTEE service is not
>>> found.
>
> No, the existing paths are not going to be stripped.
>
>>>
>>> Two questions:
>>>
>>> - Is service 81 expected to appear on retail firmware through an
>>> update, or is it specific to the CRD firmware?
>>
>> Yes, there's spinor update(bootfw2.mbn) needed which is in progress to
>> publish as QTEE side changes were already merged long back.
>> Roughly it takes ~1month(ideal scenario) to get firmware changes
>> available but somehow it's taking more this time.
>>
>> I think i should have captured this dependency info in my series cover
>> letter to avoid any confusion.
>>
>> With the changes, service uid 81 will be available and won't see qcomtee
>> driver issue log there.
>
> You are responding to the user who uses a COTS commercial device. Are
> you suggesting that the user can somehow update the firmware on that
> device?
>
>>
>>> - Would you consider a QSEECOM-based path for such firmware? I am
>>> happy to test this series, or any other service UID you would like
>>> checked, on this machine, and to share the transport code.
>>>
>>
>> Qseecom-based firmware path is not recommend approach as it's not
>> generic and less scalable leaving less room for expanding featuresets.
>> qcomtee uses mink-ipc based model which does all interaction with TA
>> with just 2 scm calls and is very much scalable compared to qseecom.
>>
>> So, we are planning to use mink-ipc based qcomtee path only and I'll
>> drop an update once QTEE firmware changes are available in meta builds.
>> I'd be happy you to try it out and give any suggestions!
>
> I feel it really sad that the team (again) ignores existing available
> devices. Yes, QSEECOM is legacy, etc., etc. However we must make sure
> that devices are being sold can be used.
>
> I guess, at this point, the best course of action would be for Xilin Wu
> to submit the QSEECOM driver upstream. Xiling, could you please do it?
>
I'm also sad to hear that the proposed qcomtee path isn't even supported
on Glymur, and yet the Qualcomm team is ignoring QSEECOM, which is used
on all the devices currently being sold. I might submit the driver
upstream, but I can't promise anything.
--
Best regards,
Xilin Wu <sophon@radxa.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-10-05 7:24 ` Dmitry Baryshkov
@ 2026-10-05 12:00 ` Kuldeep Singh
2026-10-07 13:40 ` Xilin Wu
1 sibling, 0 replies; 22+ messages in thread
From: Kuldeep Singh @ 2026-10-05 12:00 UTC (permalink / raw)
To: Dmitry Baryshkov, Xilin Wu
Cc: zeroknots, amirreza.zarrabi, jarkko, jenswi, jgg, linux-arm-msm,
linux-integrity, linux-kernel, op-tee, peterhuewe, sumit.garg
>>> Two questions:
>>>
>>> - Is service 81 expected to appear on retail firmware through an
>>> update, or is it specific to the CRD firmware?
>>
>> Yes, there's spinor update(bootfw2.mbn) needed which is in progress to
>> publish as QTEE side changes were already merged long back.
>> Roughly it takes ~1month(ideal scenario) to get firmware changes
>> available but somehow it's taking more this time.
>>
>> I think i should have captured this dependency info in my series cover
>> letter to avoid any confusion.
>>
>> With the changes, service uid 81 will be available and won't see qcomtee
>> driver issue log there.
>
> You are responding to the user who uses a COTS commercial device. Are
> you suggesting that the user can somehow update the firmware on that
> device?
Ahh, for COTS devices as one cannot replace firmware so it's better to
use qseecom path in that case.
For latest ones, one can switch to qcomtee based approach.
If Xilin already has qseecom interface written then it's best to have it
for the COTS devices.
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-10-05 6:57 ` Kuldeep Singh
@ 2026-10-05 7:24 ` Dmitry Baryshkov
2026-10-05 12:00 ` Kuldeep Singh
2026-10-07 13:40 ` Xilin Wu
0 siblings, 2 replies; 22+ messages in thread
From: Dmitry Baryshkov @ 2026-10-05 7:24 UTC (permalink / raw)
To: Kuldeep Singh, Xilin Wu
Cc: zeroknots, amirreza.zarrabi, jarkko, jenswi, jgg, linux-arm-msm,
linux-integrity, linux-kernel, op-tee, peterhuewe, sumit.garg
On Mon, Oct 05, 2026 at 12:27:40PM +0530, Kuldeep Singh wrote:
> On 01-10-2026 11:20, zeroknots wrote:
> > Hi Kuldeep,
> >
> > A data point from retail hardware, in case it is useful for this
> > series: on an ASUS Zenbook A14 UX3407NA (Glymur, X2E-88-100, BIOS
> > UX3407NA.315) the TPM TA is not reachable as QTEE service 81, but it
> > is present as the QSEECOM application "qcom.tz.tpm".
>
> Thanks for reaching out, Kindly check below.
>
> > With the qcomtee driver on a 7.3-rc3 based kernel, QTEE reports
> > version 5.2.0, and service discovery finds neither 81 (TPM) nor 413
> > (UEFI secure app). On the same boot, qseecom (version 0x1402000) works
> > and backs efivars through "qcom.tz.uefisecapp" (app id 7). An app-id
> > lookup for "qcom.tz.tpm" returns app id 1; a made-up name returns
> > -ENOENT.
> >
> > The Windows driver for this machine agrees: QcTrEE8480.inf configures
> > the TPM service with AppName="qcom.tz.tpm", SecureApp=1, LoadApp=0
> > (preloaded by firmware). The EFI configuration table carries the
> > TPMEventLog and TPMFinalLog entries, so the firmware TPM is active.
> >
> > Through that app, using a QSEECOM transport (based on Xilin Wu's
> > out-of-tree SC8280XP driver: QUERY_INFO_2 / SEND_COMMAND with a
> > CRB-style control area, here at 0x81d10000), /dev/tpm0 works: TPM 2.0,
> > manufacturer QCOM, vendor string "xCG fTPM", firmware 0x40000, real
> > PCR 0-7 values, GetRandom, ECC and RSA-2048 primaries, sign and
> > verify, and a sealed object that persists across reboots.
> >
> > So, as far as I can tell, retail Glymur laptops may ship firmware on
> > which this driver finds no TPM, while the same TA is available over
> > QSEECOM.
> >
> > The same applies to the prerequisite series that moves uefisecapp to
> > QCOMTEE [1]: on this firmware service 413 is absent and EFI variables
> > work only through the QSEECOM uefisecapp. If the QSEECOM path were
> > dropped for Glymur, efivars would stop working on this machine, so it
> > would be good to keep it as a fallback when the QTEE service is not
> > found.
No, the existing paths are not going to be stripped.
> >
> > Two questions:
> >
> > - Is service 81 expected to appear on retail firmware through an
> > update, or is it specific to the CRD firmware?
>
> Yes, there's spinor update(bootfw2.mbn) needed which is in progress to
> publish as QTEE side changes were already merged long back.
> Roughly it takes ~1month(ideal scenario) to get firmware changes
> available but somehow it's taking more this time.
>
> I think i should have captured this dependency info in my series cover
> letter to avoid any confusion.
>
> With the changes, service uid 81 will be available and won't see qcomtee
> driver issue log there.
You are responding to the user who uses a COTS commercial device. Are
you suggesting that the user can somehow update the firmware on that
device?
>
> > - Would you consider a QSEECOM-based path for such firmware? I am
> > happy to test this series, or any other service UID you would like
> > checked, on this machine, and to share the transport code.
> >
>
> Qseecom-based firmware path is not recommend approach as it's not
> generic and less scalable leaving less room for expanding featuresets.
> qcomtee uses mink-ipc based model which does all interaction with TA
> with just 2 scm calls and is very much scalable compared to qseecom.
>
> So, we are planning to use mink-ipc based qcomtee path only and I'll
> drop an update once QTEE firmware changes are available in meta builds.
> I'd be happy you to try it out and give any suggestions!
I feel it really sad that the team (again) ignores existing available
devices. Yes, QSEECOM is legacy, etc., etc. However we must make sure
that devices are being sold can be used.
I guess, at this point, the best course of action would be for Xilin Wu
to submit the QSEECOM driver upstream. Xiling, could you please do it?
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
2026-10-01 5:50 zeroknots
@ 2026-10-05 6:57 ` Kuldeep Singh
2026-10-05 7:24 ` Dmitry Baryshkov
0 siblings, 1 reply; 22+ messages in thread
From: Kuldeep Singh @ 2026-10-05 6:57 UTC (permalink / raw)
To: zeroknots
Cc: amirreza.zarrabi, jarkko, jenswi, jgg, linux-arm-msm,
linux-integrity, linux-kernel, op-tee, peterhuewe, sumit.garg
On 01-10-2026 11:20, zeroknots wrote:
> Hi Kuldeep,
>
> A data point from retail hardware, in case it is useful for this
> series: on an ASUS Zenbook A14 UX3407NA (Glymur, X2E-88-100, BIOS
> UX3407NA.315) the TPM TA is not reachable as QTEE service 81, but it
> is present as the QSEECOM application "qcom.tz.tpm".
Thanks for reaching out, Kindly check below.
> With the qcomtee driver on a 7.3-rc3 based kernel, QTEE reports
> version 5.2.0, and service discovery finds neither 81 (TPM) nor 413
> (UEFI secure app). On the same boot, qseecom (version 0x1402000) works
> and backs efivars through "qcom.tz.uefisecapp" (app id 7). An app-id
> lookup for "qcom.tz.tpm" returns app id 1; a made-up name returns
> -ENOENT.
>
> The Windows driver for this machine agrees: QcTrEE8480.inf configures
> the TPM service with AppName="qcom.tz.tpm", SecureApp=1, LoadApp=0
> (preloaded by firmware). The EFI configuration table carries the
> TPMEventLog and TPMFinalLog entries, so the firmware TPM is active.
>
> Through that app, using a QSEECOM transport (based on Xilin Wu's
> out-of-tree SC8280XP driver: QUERY_INFO_2 / SEND_COMMAND with a
> CRB-style control area, here at 0x81d10000), /dev/tpm0 works: TPM 2.0,
> manufacturer QCOM, vendor string "xCG fTPM", firmware 0x40000, real
> PCR 0-7 values, GetRandom, ECC and RSA-2048 primaries, sign and
> verify, and a sealed object that persists across reboots.
>
> So, as far as I can tell, retail Glymur laptops may ship firmware on
> which this driver finds no TPM, while the same TA is available over
> QSEECOM.
>
> The same applies to the prerequisite series that moves uefisecapp to
> QCOMTEE [1]: on this firmware service 413 is absent and EFI variables
> work only through the QSEECOM uefisecapp. If the QSEECOM path were
> dropped for Glymur, efivars would stop working on this machine, so it
> would be good to keep it as a fallback when the QTEE service is not
> found.
>
> Two questions:
>
> - Is service 81 expected to appear on retail firmware through an
> update, or is it specific to the CRD firmware?
Yes, there's spinor update(bootfw2.mbn) needed which is in progress to
publish as QTEE side changes were already merged long back.
Roughly it takes ~1month(ideal scenario) to get firmware changes
available but somehow it's taking more this time.
I think i should have captured this dependency info in my series cover
letter to avoid any confusion.
With the changes, service uid 81 will be available and won't see qcomtee
driver issue log there.
> - Would you consider a QSEECOM-based path for such firmware? I am
> happy to test this series, or any other service UID you would like
> checked, on this machine, and to share the transport code.
>
Qseecom-based firmware path is not recommend approach as it's not
generic and less scalable leaving less room for expanding featuresets.
qcomtee uses mink-ipc based model which does all interaction with TA
with just 2 scm calls and is very much scalable compared to qseecom.
So, we are planning to use mink-ipc based qcomtee path only and I'll
drop an update once QTEE firmware changes are available in meta builds.
I'd be happy you to try it out and give any suggestions!
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA
@ 2026-10-01 5:50 zeroknots
2026-10-05 6:57 ` Kuldeep Singh
0 siblings, 1 reply; 22+ messages in thread
From: zeroknots @ 2026-10-01 5:50 UTC (permalink / raw)
To: kuldeep.singh
Cc: amirreza.zarrabi, jarkko, jenswi, jgg, linux-arm-msm,
linux-integrity, linux-kernel, op-tee, peterhuewe, sumit.garg
Hi Kuldeep,
A data point from retail hardware, in case it is useful for this
series: on an ASUS Zenbook A14 UX3407NA (Glymur, X2E-88-100, BIOS
UX3407NA.315) the TPM TA is not reachable as QTEE service 81, but it
is present as the QSEECOM application "qcom.tz.tpm".
With the qcomtee driver on a 7.3-rc3 based kernel, QTEE reports
version 5.2.0, and service discovery finds neither 81 (TPM) nor 413
(UEFI secure app). On the same boot, qseecom (version 0x1402000) works
and backs efivars through "qcom.tz.uefisecapp" (app id 7). An app-id
lookup for "qcom.tz.tpm" returns app id 1; a made-up name returns
-ENOENT.
The Windows driver for this machine agrees: QcTrEE8480.inf configures
the TPM service with AppName="qcom.tz.tpm", SecureApp=1, LoadApp=0
(preloaded by firmware). The EFI configuration table carries the
TPMEventLog and TPMFinalLog entries, so the firmware TPM is active.
Through that app, using a QSEECOM transport (based on Xilin Wu's
out-of-tree SC8280XP driver: QUERY_INFO_2 / SEND_COMMAND with a
CRB-style control area, here at 0x81d10000), /dev/tpm0 works: TPM 2.0,
manufacturer QCOM, vendor string "xCG fTPM", firmware 0x40000, real
PCR 0-7 values, GetRandom, ECC and RSA-2048 primaries, sign and
verify, and a sealed object that persists across reboots.
So, as far as I can tell, retail Glymur laptops may ship firmware on
which this driver finds no TPM, while the same TA is available over
QSEECOM.
The same applies to the prerequisite series that moves uefisecapp to
QCOMTEE [1]: on this firmware service 413 is absent and EFI variables
work only through the QSEECOM uefisecapp. If the QSEECOM path were
dropped for Glymur, efivars would stop working on this machine, so it
would be good to keep it as a fallback when the QTEE service is not
found.
Two questions:
- Is service 81 expected to appear on retail firmware through an
update, or is it specific to the CRD firmware?
- Would you consider a QSEECOM-based path for such firmware? I am
happy to test this series, or any other service UID you would like
checked, on this machine, and to share the transport code.
[1] https://lore.kernel.org/lkml/20260722-qcom_uefisecapp_migrate_qcomtee-v2-0-b8a8fcbe4211@oss.qualcomm.com/
Thanks,
zeroknots
Assisted-by: LLM (analysis and drafting; the measurements were made on
the hardware and reviewed by me)
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-10-07 13:40 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-07 9:28 [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 1/3] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
2026-09-18 1:46 ` Jarkko Sakkinen
2026-09-21 8:10 ` Kuldeep Singh
2026-09-25 8:51 ` Jens Wiklander
2026-09-28 8:58 ` Amirreza Zarrabi
2026-09-07 9:28 ` [PATCH v2 2/3] tpm: Introduce Qualcomm TPM driver Kuldeep Singh
2026-09-18 1:48 ` Jarkko Sakkinen
2026-09-28 23:29 ` Amirreza Zarrabi
2026-10-07 9:20 ` Kuldeep Singh
2026-09-07 9:28 ` [PATCH v2 3/3] MAINTAINERS: Add Qualcomm TPM driver entry Kuldeep Singh
2026-09-18 1:49 ` Jarkko Sakkinen
2026-09-16 9:02 ` [PATCH v2 0/3] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-09-18 1:53 ` Jarkko Sakkinen
2026-09-21 8:36 ` Kuldeep Singh
2026-09-25 14:46 ` Jarkko Sakkinen
2026-09-28 9:34 ` Kuldeep Singh
2026-10-01 5:50 zeroknots
2026-10-05 6:57 ` Kuldeep Singh
2026-10-05 7:24 ` Dmitry Baryshkov
2026-10-05 12:00 ` Kuldeep Singh
2026-10-07 13:40 ` Xilin Wu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®