mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®