From: Harshal Dev <harshal.dev@oss.qualcomm.com>
To: Eric Biggers <ebiggers@kernel.org>,
Bjorn Andersson <andersson@kernel.org>,
Konrad Dybcio <konradybcio@kernel.org>,
Abel Vesa <abelvesa@kernel.org>,
Kuldeep Singh <kuldeep.singh@oss.qualcomm.com>,
Neeraj Soni <neeraj.soni@oss.qualcomm.com>,
Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>,
Sumit Garg <sumit.garg@oss.qualcomm.com>,
Selvam Sathappan Periakaruppan
<selvam.periakaruppan@oss.qualcomm.com>
Cc: Basant Kumar <basantk@qti.qualcomm.com>,
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-crypto@vger.kernel.org,
Harshal Dev <harshal.dev@oss.qualcomm.com>
Subject: [PATCH v2 3/6] firmware: qcom: Add a TEE bus based ICE back-end
Date: Wed, 11 Nov 2026 19:47:03 +0530 [thread overview]
Message-ID: <20261111-ice-migrate-tee-svc-v2-3-29d19f6c92ab@oss.qualcomm.com> (raw)
In-Reply-To: <20261111-ice-migrate-tee-svc-v2-0-29d19f6c92ab@oss.qualcomm.com>
Add support for a TEE client driver which forwards calls from the Inline
Crypto Engine (ICE) driver to the OPTEE ICE Pseudo-TA.
The ICE PTA supports both ICE Legacy and Standard modes. In Standard mode
Hardware Key Manager v1 is supported.
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
MAINTAINERS | 7 +
drivers/firmware/qcom/Kconfig | 9 +
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_ice_tee.c | 402 +++++++++++++++++++++++++++++++++++
4 files changed, 419 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f58a305de664..1132e456488b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23007,6 +23007,13 @@ F: Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
F: drivers/soc/qcom/ice.c
F: include/soc/qcom/ice.h
+QUALCOMM INLINE CRYPTO ENGINE TEE SERVICE
+M: Harshal Dev <harshal.dev@oss.qualcomm.com>
+L: linux-arm-msm@vger.kernel.org
+L: linux-crypto@vger.kernel.org
+S: Maintained
+F: drivers/firmware/qcom/qcom_ice_tee.c
+
QUALCOMM INTERCONNECT BWMON DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-arm-msm@vger.kernel.org
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index 3ad22f8fc3e5..189ada7fdcd6 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -25,6 +25,15 @@ config QCOM_PAS_TEE
Enable the generic Peripheral Authentication Service (PAS) provided
by the firmware TEE implementation as the backend.
+config QCOM_ICE_TEE
+ tristate
+ depends on TEE
+ depends on !CPU_BIG_ENDIAN
+ default m if ARCH_QCOM
+ help
+ Enable the generic Inline Crypto Engine (ICE) firmware service TEE
+ implementation as a backend.
+
config QCOM_SCM
tristate "Qualcomm PAS SCM interface driver" if ARCH_QCOM || COMPILE_TEST
select QCOM_PAS
diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
index 88ce74d74c3e..5ef64f17e6e5 100644
--- a/drivers/firmware/qcom/Makefile
+++ b/drivers/firmware/qcom/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
obj-$(CONFIG_QCOM_PAS) += qcom_pas.o
obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o
+obj-$(CONFIG_QCOM_ICE_TEE) += qcom_ice_tee.o
diff --git a/drivers/firmware/qcom/qcom_ice_tee.c b/drivers/firmware/qcom/qcom_ice_tee.c
new file mode 100644
index 000000000000..694aa05847f0
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_ice_tee.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+#include <linux/firmware/qcom/qcom_ice_tee.h>
+
+#include <soc/qcom/ice.h>
+
+#define PTA_QCOM_ICE_INVALIDATE_KEY 0
+#define PTA_QCOM_ICE_SET_CONFIG_KEY 1
+#define PTA_QCOM_ICE_GENERATE_KEY 2
+#define PTA_QCOM_ICE_IMPORT_KEY 3
+#define PTA_QCOM_ICE_EXPORT_KEY 4
+#define PTA_QCOM_ICE_GET_RAW_SECRET 5
+#define PTA_QCOM_ICE_HAS_WRAPPED_KEY_SUPPORT 6
+
+#define TEE_NUM_PARAMS 4
+
+struct qcom_ice_tee_private {
+ struct device *dev;
+ struct tee_context *ctx;
+ u32 session_id;
+};
+
+static int qcom_ice_tee_invalidate_key(struct device *dev, u32 index)
+{
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = PTA_QCOM_ICE_INVALIDATE_KEY,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS,
+ };
+ struct tee_param param[TEE_NUM_PARAMS] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = index,
+ .u.value.b = 0,
+ }
+ };
+ int ret;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0)
+ ret = ret ?: -EINVAL;
+
+ return ret;
+}
+
+static int qcom_ice_tee_set_key(struct device *dev, u32 index,
+ const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher,
+ u32 data_unit_size)
+{
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = PTA_QCOM_ICE_SET_CONFIG_KEY,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS,
+ };
+ struct tee_param param[TEE_NUM_PARAMS] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = index,
+ .u.value.b = cipher,
+ },
+ [1] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = data_unit_size,
+ },
+ [2] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
+ }
+ };
+ struct tee_shm *key_shm;
+ u8 *key_buf;
+ int ret;
+
+ key_shm = tee_shm_alloc_kernel_buf(data->ctx, key_size);
+ if (IS_ERR(key_shm))
+ return PTR_ERR(key_shm);
+
+ key_buf = tee_shm_get_va(key_shm, 0);
+ if (IS_ERR(key_buf)) {
+ ret = PTR_ERR(key_buf);
+ goto exit;
+ }
+
+ memcpy(key_buf, key, key_size);
+ param[2].u.memref.shm = key_shm;
+ param[2].u.memref.size = key_size;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ memzero_explicit(key_buf, key_size);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev,
+ "ICE set_key failed, idx=%u, ret=%d, err=0x%x\n",
+ index, ret, inv_arg.ret);
+ ret = ret ?: -EINVAL;
+ }
+exit:
+ tee_shm_free(key_shm);
+ return ret;
+}
+
+static int qcom_ice_tee_wrapped_key_call(struct device *dev, u32 cmd,
+ const u8 *in, size_t in_size,
+ u8 *out, size_t out_size)
+{
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = cmd,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS,
+ };
+ struct tee_param param[TEE_NUM_PARAMS] = { };
+ struct tee_shm *in_shm = NULL;
+ struct tee_shm *out_shm = NULL;
+ size_t out_buf_size;
+ u8 *buf;
+ int ret;
+
+ if (in && in_size) {
+ in_shm = tee_shm_alloc_kernel_buf(data->ctx, in_size);
+ if (IS_ERR(in_shm))
+ return PTR_ERR(in_shm);
+
+ buf = tee_shm_get_va(in_shm, 0);
+ if (IS_ERR(buf)) {
+ ret = PTR_ERR(buf);
+ goto exit;
+ }
+ memcpy(buf, in, in_size);
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+ param[0].u.memref.shm = in_shm;
+ param[0].u.memref.size = in_size;
+ }
+
+ if (out && out_size) {
+ out_shm = tee_shm_alloc_kernel_buf(data->ctx, out_size);
+ if (IS_ERR(out_shm)) {
+ ret = PTR_ERR(out_shm);
+ goto exit;
+ }
+
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[1].u.memref.shm = out_shm;
+ param[1].u.memref.size = out_size;
+ }
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev,
+ "ICE cmd %u failed, ret=%d, err=0x%x\n",
+ cmd, ret, inv_arg.ret);
+ ret = ret ?: -EINVAL;
+ goto exit;
+ }
+
+ if (out && out_size) {
+ out_buf_size = param[1].u.memref.size;
+ if (out_size != out_buf_size) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ buf = tee_shm_get_va(out_shm, 0);
+ if (IS_ERR(buf)) {
+ ret = PTR_ERR(buf);
+ goto exit;
+ }
+ memcpy(out, buf, out_buf_size);
+ memzero_explicit(buf, out_buf_size);
+ }
+exit:
+ if (in_shm && !IS_ERR(in_shm)) {
+ buf = tee_shm_get_va(in_shm, 0);
+ if (!IS_ERR(buf))
+ memzero_explicit(buf, in_size);
+ tee_shm_free(in_shm);
+ }
+ if (out_shm && !IS_ERR(out_shm)) {
+ buf = tee_shm_get_va(out_shm, 0);
+ if (!IS_ERR(buf))
+ memzero_explicit(buf, out_size);
+ tee_shm_free(out_shm);
+ }
+
+ return ret;
+}
+
+static int qcom_ice_tee_derive_sw_secret(struct device *dev,
+ const u8 *eph_key,
+ size_t eph_key_size,
+ u8 *sw_secret,
+ size_t sw_secret_size)
+{
+ return qcom_ice_tee_wrapped_key_call(dev, PTA_QCOM_ICE_GET_RAW_SECRET,
+ eph_key, eph_key_size,
+ sw_secret, sw_secret_size);
+}
+
+static int qcom_ice_tee_generate_key(struct device *dev,
+ u8 *lt_key, size_t lt_key_size)
+{
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = PTA_QCOM_ICE_GENERATE_KEY,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS,
+ };
+ struct tee_param param[TEE_NUM_PARAMS] = { };
+ struct tee_shm *key_shm;
+ u8 *buf;
+ size_t out_size;
+ int ret;
+
+ key_shm = tee_shm_alloc_kernel_buf(data->ctx, lt_key_size);
+ if (IS_ERR(key_shm))
+ return PTR_ERR(key_shm);
+
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param[0].u.memref.shm = key_shm;
+ param[0].u.memref.shm_offs = 0;
+ param[0].u.memref.size = lt_key_size;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev,
+ "ICE generate_key failed, ret=%d, err=0x%x\n",
+ ret, inv_arg.ret);
+ ret = ret ?: -EINVAL;
+ goto exit;
+ }
+
+ buf = tee_shm_get_va(key_shm, 0);
+ if (IS_ERR(buf)) {
+ ret = PTR_ERR(buf);
+ goto exit;
+ }
+
+ out_size = param[0].u.memref.size;
+ if (out_size != lt_key_size) {
+ ret = -EINVAL;
+ goto clear;
+ }
+
+ memcpy(lt_key, buf, out_size);
+clear:
+ memzero_explicit(buf, lt_key_size);
+exit:
+ tee_shm_free(key_shm);
+ return ret;
+}
+
+static int qcom_ice_tee_prepare_key(struct device *dev,
+ const u8 *lt_key,
+ size_t lt_key_size,
+ u8 *eph_key,
+ size_t eph_key_size)
+{
+ return qcom_ice_tee_wrapped_key_call(dev, PTA_QCOM_ICE_EXPORT_KEY,
+ lt_key, lt_key_size,
+ eph_key, eph_key_size);
+}
+
+static int qcom_ice_tee_import_key(struct device *dev,
+ const u8 *raw_key,
+ size_t raw_key_size,
+ u8 *lt_key,
+ size_t lt_key_size)
+{
+ return qcom_ice_tee_wrapped_key_call(dev, PTA_QCOM_ICE_IMPORT_KEY,
+ raw_key, raw_key_size,
+ lt_key, lt_key_size);
+}
+
+static bool qcom_ice_tee_has_wrapped_key_support(struct device *dev)
+{
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+ struct tee_ioctl_invoke_arg inv_arg = {
+ .func = PTA_QCOM_ICE_HAS_WRAPPED_KEY_SUPPORT,
+ .session = data->session_id,
+ .num_params = TEE_NUM_PARAMS,
+ };
+ struct tee_param param[TEE_NUM_PARAMS] = {
+ [0] = {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT,
+ .u.value.a = 0,
+ }
+ };
+ int ret;
+ bool has_support = false;
+
+ ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
+ if (ret < 0 || inv_arg.ret != 0) {
+ dev_err(dev,
+ "ICE wrapped_key_support failed, ret=%d, err=0x%x\n",
+ ret, inv_arg.ret);
+ goto exit;
+ }
+
+ has_support = param[0].u.value.a;
+exit:
+ return has_support;
+}
+
+static struct qcom_ice_tz_ops qcom_ice_tee_ops = {
+ .drv_name = "qcom-ice-tee",
+ .ice_invalidate_key = qcom_ice_tee_invalidate_key,
+ .ice_set_key = qcom_ice_tee_set_key,
+ .ice_has_wrapped_key_support = qcom_ice_tee_has_wrapped_key_support,
+ .ice_derive_sw_secret = qcom_ice_tee_derive_sw_secret,
+ .ice_generate_key = qcom_ice_tee_generate_key,
+ .ice_prepare_key = qcom_ice_tee_prepare_key,
+ .ice_import_key = qcom_ice_tee_import_key,
+};
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ return ver->impl_id == TEE_IMPL_ID_OPTEE;
+}
+
+static int qcom_ice_tee_probe(struct tee_client_device *ice_dev)
+{
+ struct device *dev = &ice_dev->dev;
+ struct qcom_ice_tee_private *data;
+ struct tee_ioctl_open_session_arg sess_arg = {
+ .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL,
+ };
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+ if (IS_ERR(data->ctx))
+ return -ENODEV;
+
+ export_uuid(sess_arg.uuid, &ice_dev->id.uuid);
+ ret = tee_client_open_session(data->ctx, &sess_arg, NULL);
+ if (ret < 0 || sess_arg.ret != 0) {
+ dev_err(dev,
+ "tee_client_open_session failed, ret=%d, err=0x%x\n",
+ ret, sess_arg.ret);
+ ret = ret ?: -EINVAL;
+ goto err_sess;
+ }
+
+ data->session_id = sess_arg.session;
+ dev_set_drvdata(dev, data);
+
+ qcom_ice_tee_ops.dev = dev;
+ ret = qcom_ice_tz_ops_register(&qcom_ice_tee_ops);
+ if (ret)
+ goto err_ops;
+
+ return 0;
+err_ops:
+ tee_client_close_session(data->ctx, data->session_id);
+err_sess:
+ tee_client_close_context(data->ctx);
+ return ret;
+}
+
+static void qcom_ice_tee_remove(struct tee_client_device *ice_dev)
+{
+ struct device *dev = &ice_dev->dev;
+ struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
+
+ qcom_ice_tz_ops_unregister(&qcom_ice_tee_ops);
+ tee_client_close_session(data->ctx, data->session_id);
+ tee_client_close_context(data->ctx);
+}
+
+static const struct tee_client_device_id qcom_ice_tee_id_table[] = {
+ {UUID_INIT(0x29e87b9e, 0x012a, 0x4878,
+ 0xa1, 0xe1, 0xa1, 0xb9, 0x0a, 0x21, 0x5b, 0x16)},
+ {}
+};
+MODULE_DEVICE_TABLE(tee, qcom_ice_tee_id_table);
+
+static struct tee_client_driver qcom_ice_tee_driver = {
+ .probe = qcom_ice_tee_probe,
+ .remove = qcom_ice_tee_remove,
+ .id_table = qcom_ice_tee_id_table,
+ .driver = {
+ .name = "qcom-ice-tee",
+ },
+};
+
+module_tee_client_driver(qcom_ice_tee_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm ICE TEE driver");
--
2.34.1
next prev parent reply other threads:[~2026-11-11 14:17 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 14:17 [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
2026-11-11 14:17 ` [PATCH v2 1/6] soc: qcom: ice: Add TZ agnostic re-direction functions Harshal Dev
2026-11-11 14:17 ` [PATCH v2 2/6] firmware: qcom_scm: Transition to ICE TZ agnostic operations Harshal Dev
2026-11-11 14:17 ` Harshal Dev [this message]
2026-11-11 14:17 ` [PATCH v2 4/6] tee: optee: Register TEE devices on the platform bus Harshal Dev
2026-11-11 14:57 ` Harshal Dev
2026-11-11 14:17 ` [PATCH v2 5/6] soc: qcom: ice: Migrate to the TZ agnostic ICE APIs Harshal Dev
2026-11-11 14:17 ` [PATCH v2 6/6] firmware: qcom: scm: Drop exported ICE wrapper functions Harshal Dev
2026-11-11 14:21 ` [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111-ice-migrate-tee-svc-v2-3-29d19f6c92ab@oss.qualcomm.com \
--to=harshal.dev@oss.qualcomm.com \
--cc=abelvesa@kernel.org \
--cc=andersson@kernel.org \
--cc=basantk@qti.qualcomm.com \
--cc=ebiggers@kernel.org \
--cc=konradybcio@kernel.org \
--cc=kuldeep.singh@oss.qualcomm.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=manivannan.sadhasivam@oss.qualcomm.com \
--cc=neeraj.soni@oss.qualcomm.com \
--cc=selvam.periakaruppan@oss.qualcomm.com \
--cc=sumit.garg@oss.qualcomm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®