* [PATCH 0/2] Add TPM support via Qualcomm TEE TPM TA
@ 2026-08-31 9:42 Kuldeep Singh
2026-08-31 9:43 ` [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
2026-08-31 9:43 ` [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver Kuldeep Singh
0 siblings, 2 replies; 8+ messages in thread
From: Kuldeep Singh @ 2026-08-31 9:42 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>
---
Kuldeep Singh (2):
tee: qcomtee: Register qcom.tz.tpm service for discovery
firmware: tpm: Introduce tpm-qcom driver
MAINTAINERS | 7 +
drivers/char/tpm/Kconfig | 9 +
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_qcom.c | 371 ++++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm_qcom.h | 82 +++++++++
drivers/tee/qcomtee/call.c | 4 +-
drivers/tee/qcomtee/qcomtee_msg.h | 2 +
7 files changed, 475 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] 8+ messages in thread
* [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-08-31 9:42 [PATCH 0/2] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
@ 2026-08-31 9:43 ` Kuldeep Singh
2026-09-02 9:27 ` Kuldeep Singh
2026-08-31 9:43 ` [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver Kuldeep Singh
1 sibling, 1 reply; 8+ messages in thread
From: Kuldeep Singh @ 2026-08-31 9:43 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..888611b801c8 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 489
+
/* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */
/* The message contains a callback request. */
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver
2026-08-31 9:42 [PATCH 0/2] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-08-31 9:43 ` [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
@ 2026-08-31 9:43 ` Kuldeep Singh
2026-08-31 11:15 ` Konrad Dybcio
2026-09-01 13:46 ` Jarkko Sakkinen
1 sibling, 2 replies; 8+ messages in thread
From: Kuldeep Singh @ 2026-08-31 9:43 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>
---
MAINTAINERS | 7 +
drivers/char/tpm/Kconfig | 9 ++
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_qcom.c | 371 ++++++++++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm_qcom.h | 82 ++++++++++
5 files changed, 470 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
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..00ee61204056
--- /dev/null
+++ b/drivers/char/tpm/tpm_qcom.c
@@ -0,0 +1,371 @@
+// 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;
+
+ memset(&inv_arg, 0, sizeof(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)
+{
+ int ret;
+ struct tee_ioctl_object_invoke_arg inv_arg;
+ struct tee_param param[2];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(¶m, 0, sizeof(param));
+
+ 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)
+{
+ int ret;
+ struct tee_ioctl_object_invoke_arg inv_arg;
+ struct tee_param param[2];
+ u32 tpm_uid = QCOMTEE_TPM_UID;
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(¶m, 0, sizeof(param));
+
+ 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,
+ void *req, size_t req_len,
+ void *rsp, size_t *rsp_len)
+{
+ int ret;
+ struct tee_ioctl_object_invoke_arg inv_arg;
+ struct tee_param param[2];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(¶m, 0, sizeof(param));
+
+ inv_arg.id = pvt_data->tpm_svc_obj.id;
+ inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
+ inv_arg.num_params = 2;
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
+ param[0].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
+ param[1].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
+
+ print_hex_dump_debug("tpm_qcom req: ", DUMP_PREFIX_OFFSET,
+ 16, 1, req, req_len, true);
+
+ 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[1].u.ubuf.size;
+
+ print_hex_dump_debug("tpm_qcom rsp: ", DUMP_PREFIX_OFFSET,
+ 16, 1, rsp, *rsp_len, true);
+
+ 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, &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 %u.%u\n",
+ QCOMTEE_TPM_TA_VERSION_GET_MAJOR(ver_rsp.version_num),
+ QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver_rsp.version_num));
+
+ ret = tpm_qcom_send_command(pvt_data, &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, so this is only ever called in case of
+ * 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, &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,
+ "%s: len=%zd exceeds MAX_COMMAND_SIZE\n",
+ __func__, cmd_len);
+ return -EIO;
+ }
+
+ u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
+ if (!response)
+ return -ENOMEM;
+
+ ret = tpm_qcom_send_command(pvt_data, buf, cmd_len, response, &rsp_len);
+ if (ret < 0) {
+ dev_err(&chip->dev, "%s: failed: ret=%d\n", __func__, 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, "%s: tpm_chip_alloc failed\n", __func__);
+ 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, "%s: tpm_chip_register failed with rc=%d\n",
+ __func__, 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..0f3756944768
--- /dev/null
+++ b/drivers/char/tpm/tpm_qcom.h
@@ -0,0 +1,82 @@
+/* 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 489
+
+/* 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_GET_MAJOR(ver) ((u32)(ver) >> 16)
+#define QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver) ((u32)(ver) & 0x0000ffffU)
+
+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 related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver
2026-08-31 9:43 ` [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver Kuldeep Singh
@ 2026-08-31 11:15 ` Konrad Dybcio
2026-08-31 11:26 ` Kuldeep Singh
2026-09-01 13:46 ` Jarkko Sakkinen
1 sibling, 1 reply; 8+ messages in thread
From: Konrad Dybcio @ 2026-08-31 11:15 UTC (permalink / raw)
To: Kuldeep Singh, Amirreza Zarrabi, Jens Wiklander, Sumit Garg,
Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity
On 8/31/26 11:43 AM, 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.
[...]
> +static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
> + struct tee_param_objref *client_env_obj)
> +{
> + int ret;
> + struct tee_ioctl_object_invoke_arg inv_arg;
> + struct tee_param param[2];
nit: Reverse-Christmas-tree would be preferred
> +
> + memset(&inv_arg, 0, sizeof(inv_arg));
> + memset(¶m, 0, sizeof(param));
You can zero-initialize local struct variables like this:
struct foo bar = { };
[...]
> +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,
> + "%s: len=%zd exceeds MAX_COMMAND_SIZE\n",
> + __func__, cmd_len);
The name of the function isn't helpful here, this is the only time this
message appears, so it's easy to grep
[...]
> + err = tpm_chip_register(pvt_data->chip);
> + if (err) {
> + dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n",
> + __func__, err);
Likewise
[...]
> +#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
> +#define QCOMTEE_TPM_TA_VERSION_GET_MAJOR(ver) ((u32)(ver) >> 16)
> +#define QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver) ((u32)(ver) & 0x0000ffffU)
That's FIELD_GET(mask, x)
Konrad
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver
2026-08-31 11:15 ` Konrad Dybcio
@ 2026-08-31 11:26 ` Kuldeep Singh
0 siblings, 0 replies; 8+ messages in thread
From: Kuldeep Singh @ 2026-08-31 11:26 UTC (permalink / raw)
To: Konrad Dybcio, Amirreza Zarrabi, Jens Wiklander, Sumit Garg,
Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe
Cc: linux-arm-msm, op-tee, linux-kernel, linux-integrity
On 31-08-2026 16:45, Konrad Dybcio wrote:
> On 8/31/26 11:43 AM, 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.
>
> [...]
>
>> +static int tpm_qcom_get_client_env_obj(struct tee_context *ctx,
>> + struct tee_param_objref *client_env_obj)
>> +{
>> + int ret;
>> + struct tee_ioctl_object_invoke_arg inv_arg;
>> + struct tee_param param[2];
>
> nit: Reverse-Christmas-tree would be preferred
Ok.
>
>> +
>> + memset(&inv_arg, 0, sizeof(inv_arg));
>> + memset(¶m, 0, sizeof(param));
>
> You can zero-initialize local struct variables like this:
>
> struct foo bar = { };
>
> [...]
>
>> +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,
>> + "%s: len=%zd exceeds MAX_COMMAND_SIZE\n",
>> + __func__, cmd_len);
>
> The name of the function isn't helpful here, this is the only time this
> message appears, so it's easy to grep
Sure.
>
> [...]
>
>
>> + err = tpm_chip_register(pvt_data->chip);
>> + if (err) {
>> + dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n",
>> + __func__, err);
>
> Likewise
Since it's dev_err so dev name should be sufficient i think.
Let me drop function naming from log.
>
> [...]
>
>> +#define QCOMTEE_TPM_GET_TA_VERSION_ID 0x0001000
>> +#define QCOMTEE_TPM_TA_VERSION_GET_MAJOR(ver) ((u32)(ver) >> 16)
>> +#define QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver) ((u32)(ver) & 0x0000ffffU)
>
> That's FIELD_GET(mask, x)
Sounds good, Let me use FIELD_GET.
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver
2026-08-31 9:43 ` [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver Kuldeep Singh
2026-08-31 11:15 ` Konrad Dybcio
@ 2026-09-01 13:46 ` Jarkko Sakkinen
2026-09-02 9:22 ` Kuldeep Singh
1 sibling, 1 reply; 8+ messages in thread
From: Jarkko Sakkinen @ 2026-09-01 13: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
Short summary has a different driver name that is implemented i.e.,
it should has "tpm_qcom", not "tpm-qcom".
On Mon, Aug 31, 2026 at 03:13:01PM +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>
> ---
> MAINTAINERS | 7 +
> drivers/char/tpm/Kconfig | 9 ++
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_qcom.c | 371 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm_qcom.h | 82 ++++++++++
Rename it as tpm_qcom_tee just to be aligned on how Microsoft's fTPM
driver is named.
> 5 files changed, 470 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
this change should be a separate patch.
> 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..00ee61204056
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.c
> @@ -0,0 +1,371 @@
> +// 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;
> +
> + memset(&inv_arg, 0, sizeof(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)
> +{
> + int ret;
> + struct tee_ioctl_object_invoke_arg inv_arg;
> + struct tee_param param[2];
> +
> + memset(&inv_arg, 0, sizeof(inv_arg));
> + memset(¶m, 0, sizeof(param));
> +
> + 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)
> +{
> + int ret;
> + struct tee_ioctl_object_invoke_arg inv_arg;
> + struct tee_param param[2];
> + u32 tpm_uid = QCOMTEE_TPM_UID;
> +
> + memset(&inv_arg, 0, sizeof(inv_arg));
> + memset(¶m, 0, sizeof(param));
> +
> + 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,
> + void *req, size_t req_len,
> + void *rsp, size_t *rsp_len)
> +{
> + int ret;
> + struct tee_ioctl_object_invoke_arg inv_arg;
> + struct tee_param param[2];
> +
> + memset(&inv_arg, 0, sizeof(inv_arg));
> + memset(¶m, 0, sizeof(param));
> +
> + inv_arg.id = pvt_data->tpm_svc_obj.id;
> + inv_arg.op = QCOMTEE_TPM_OP_SEND_COMMAND;
> + inv_arg.num_params = 2;
> +
> + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT;
> + param[0].u.ubuf = (struct tee_param_ubuf){ .addr = req, .size = req_len };
> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT;
> + param[1].u.ubuf = (struct tee_param_ubuf){ .addr = rsp, .size = *rsp_len };
> +
> + print_hex_dump_debug("tpm_qcom req: ", DUMP_PREFIX_OFFSET,
> + 16, 1, req, req_len, true);
Please don't do this. It can put production systems at risk.
> +
> + 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[1].u.ubuf.size;
> +
> + print_hex_dump_debug("tpm_qcom rsp: ", DUMP_PREFIX_OFFSET,
> + 16, 1, rsp, *rsp_len, true);
> +
> + 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, &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 %u.%u\n",
> + QCOMTEE_TPM_TA_VERSION_GET_MAJOR(ver_rsp.version_num),
> + QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver_rsp.version_num));
> +
> + ret = tpm_qcom_send_command(pvt_data, &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, so this is only ever called in case of
> + * 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, &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,
> + "%s: len=%zd exceeds MAX_COMMAND_SIZE\n",
> + __func__, cmd_len);
> + return -EIO;
> + }
> +
> + u8 *response __free(kfree) = kzalloc(rsp_len, GFP_KERNEL);
> + if (!response)
> + return -ENOMEM;
> +
> + ret = tpm_qcom_send_command(pvt_data, buf, cmd_len, response, &rsp_len);
> + if (ret < 0) {
> + dev_err(&chip->dev, "%s: failed: ret=%d\n", __func__, 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, "%s: tpm_chip_alloc failed\n", __func__);
> + 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, "%s: tpm_chip_register failed with rc=%d\n",
> + __func__, 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..0f3756944768
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_qcom.h
> @@ -0,0 +1,82 @@
> +/* 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 489
> +
> +/* 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_GET_MAJOR(ver) ((u32)(ver) >> 16)
> +#define QCOMTEE_TPM_TA_VERSION_GET_MINOR(ver) ((u32)(ver) & 0x0000ffffU)
> +
> +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:
Block comments AFAIK should be
/*
* <TEXT>
> + * 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
>
BR, Jarkko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver
2026-09-01 13:46 ` Jarkko Sakkinen
@ 2026-09-02 9:22 ` Kuldeep Singh
0 siblings, 0 replies; 8+ messages in thread
From: Kuldeep Singh @ 2026-09-02 9:22 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 01-09-2026 19:16, Jarkko Sakkinen wrote:
> Short summary has a different driver name that is implemented i.e.,
> it should has "tpm_qcom", not "tpm-qcom".
True, let me make more generic like "Introduce Qualcomm TPM driver"
instead of having driver filename in commit title for any confusion.
>> MAINTAINERS | 7 +
>> drivers/char/tpm/Kconfig | 9 ++
>> drivers/char/tpm/Makefile | 1 +
>> drivers/char/tpm/tpm_qcom.c | 371 ++++++++++++++++++++++++++++++++++++++++++++
>> drivers/char/tpm/tpm_qcom.h | 82 ++++++++++
>
> Rename it as tpm_qcom_tee just to be aligned on how Microsoft's fTPM
> driver is named.
I'd say firstly microsoft ftpm driver should be renamed.
For example, MS ftpm is based on optee(session based context) whereas
Qcom tpm driver is based on qcomtee(part of same TEE subsystem but uses
completely different mink-ipc based sharing mechanism).
So, it's completely different driver compared to MS.
My suggestions:
- Rename MS driver i.e tpm_ftpm_tee.c -> tpm_microsoft.c
- Rename Qcom driver i.e tpm_qcom.c -> tpm_qcom.c
I see pattern like tpm_{loongson/atmel/ibm} etc. so better to follow
this convention?
If ever need optee/qtee to specify, more namings can be tpm_ftpm_optee.c
and tpm_qcom_qtee.c?
Personally, I don't prefer tpm_ftpm_tee.c as it's all generic name with
no optee specification.
>
>> 5 files changed, 470 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
>
> this change should be a separate patch.
Ok, let me make MAINTAINERS entry as separate patch.
Also, i noticed none of other driver are being even compiled as 'M' like
atmel, ibm etc.
Any obvious reason behind this?
Should i enable tpm_qcom as Module in this patch only.
>> + print_hex_dump_debug("tpm_qcom req: ", DUMP_PREFIX_OFFSET,
>> + 16, 1, req, req_len, true);
>
> Please don't do this. It can put production systems at risk.
Ok. I want to dump some contents on failures which makes debug easier.
Instead of dumping entire req/rsp buffer, dump tag, reqlen, rsplen etc?
>> +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",
Whatever naming we'll decide, I'll update same here too.
>> +
>> +/* dTPM SPI transfer optimization:
>
> Block comments AFAIK should be
>
> /*
> * <TEXT>
>
Let me fix it.
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery
2026-08-31 9:43 ` [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
@ 2026-09-02 9:27 ` Kuldeep Singh
0 siblings, 0 replies; 8+ messages in thread
From: Kuldeep Singh @ 2026-09-02 9:27 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
> diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h
> index ecaf8db67d45..888611b801c8 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 489
There's discussion ongoing in QTEE side on keeping idl with uid 81 and
not a new idl(uid 489).
I'll update if needed in my next patch.
--
Regards
Kuldeep
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-02 9:27 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 9:42 [PATCH 0/2] Add TPM support via Qualcomm TEE TPM TA Kuldeep Singh
2026-08-31 9:43 ` [PATCH 1/2] tee: qcomtee: Register qcom.tz.tpm service for discovery Kuldeep Singh
2026-09-02 9:27 ` Kuldeep Singh
2026-08-31 9:43 ` [PATCH 2/2] firmware: tpm: Introduce tpm-qcom driver Kuldeep Singh
2026-08-31 11:15 ` Konrad Dybcio
2026-08-31 11:26 ` Kuldeep Singh
2026-09-01 13:46 ` Jarkko Sakkinen
2026-09-02 9:22 ` Kuldeep Singh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox