[PATCH 1/5] firmware: qcom: Add a generic ICE service
From: Harshal Dev
Date: Wed Oct 21 2026 - 11:48:09 EST
Qualcomm platforms such as the IQ-9075 EVK now boot with both QTEE and
OPTEE. However, the Qualcomm Inline Crypto Engine (ICE) driver
currently only makes SCM calls into QTEE. With the arrival of ICE
support on OPTEE for Qualcomm platforms, there is a need for a generic
layer which re-directs the call to the correct TEE at runtime.
Add a generic ICE service which acts as a common layer with different
TEE backends plugged in whether it's an SCM implementation or a proper
TEE bus based implementation (such as for OPTEE).
Signed-off-by: Harshal Dev <harshal.dev@xxxxxxxxxxxxxxxx>
---
MAINTAINERS | 9 +++
drivers/firmware/qcom/Kconfig | 8 +++
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_ice.c | 125 +++++++++++++++++++++++++++++++++
drivers/firmware/qcom/qcom_ice.h | 51 ++++++++++++++
include/linux/firmware/qcom/qcom_ice.h | 32 +++++++++
6 files changed, 226 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f58a305de664..7a80b776896c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23007,6 +23007,15 @@ 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 TZ SERVICE
+M: Harshal Dev <harshal.dev@xxxxxxxxxxxxxxxx>
+L: linux-arm-msm@xxxxxxxxxxxxxxx
+L: linux-crypto@xxxxxxxxxxxxxxx
+S: Maintained
+F: drivers/firmware/qcom/qcom_ice.c
+F: drivers/firmware/qcom/qcom_ice.h
+F: include/linux/firmware/qcom/qcom_ice.h
+
QUALCOMM INTERCONNECT BWMON DRIVER
M: Krzysztof Kozlowski <krzk@xxxxxxxxxx>
L: linux-arm-msm@xxxxxxxxxxxxxxx
diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
index 3ad22f8fc3e5..e2d1c17296c2 100644
--- a/drivers/firmware/qcom/Kconfig
+++ b/drivers/firmware/qcom/Kconfig
@@ -25,6 +25,14 @@ config QCOM_PAS_TEE
Enable the generic Peripheral Authentication Service (PAS) provided
by the firmware TEE implementation as the backend.
+config QCOM_ICE_SERVICE
+ tristate
+ help
+ Enable the generic Inline Crypto Engine (ICE) service provided by
+ firmware. It acts as a common layer with different TZ backends
+ plugged in whether it's an SCM implementation or a TEE bus based
+ ICE service implementation.
+
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..6403e8a17249 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_SERVICE) += qcom_ice.o
diff --git a/drivers/firmware/qcom/qcom_ice.c b/drivers/firmware/qcom/qcom_ice.c
new file mode 100644
index 000000000000..2e42d70c855a
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_ice.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/firmware/qcom/qcom_ice.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "qcom_ice.h"
+
+static struct qcom_ice_ops *ops_ptr;
+
+bool qcom_ice_svc_ops_registered(void)
+{
+ /*
+ * The barrier for ops_ptr is intended to synchronize the data stores
+ * for the ops data structure when client drivers are in parallel
+ * checking for ICE service availability.
+ */
+ return !!smp_load_acquire(&ops_ptr);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_ops_registered);
+
+bool qcom_ice_svc_available(void)
+{
+ if (!ops_ptr)
+ return false;
+
+ return ops_ptr->is_ice_available(ops_ptr->dev);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_available);
+
+int qcom_ice_svc_invalidate_key(u32 index)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_invalidate_key(ops_ptr->dev, index);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_invalidate_key);
+
+int qcom_ice_svc_set_key(u32 index, const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher, u32 data_unit_size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_set_key(ops_ptr->dev, index, key, key_size,
+ cipher, data_unit_size);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_set_key);
+
+bool qcom_ice_svc_has_wrapped_key_support(void)
+{
+ if (!ops_ptr)
+ return false;
+
+ return ops_ptr->ice_has_wrapped_key_support(ops_ptr->dev);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_has_wrapped_key_support);
+
+int qcom_ice_svc_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
+ u8 *sw_secret, size_t sw_secret_size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_derive_sw_secret(ops_ptr->dev, eph_key, eph_key_size,
+ sw_secret, sw_secret_size);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_derive_sw_secret);
+
+int qcom_ice_svc_generate_key(u8 *lt_key, size_t lt_key_size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_generate_key(ops_ptr->dev, lt_key, lt_key_size);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_generate_key);
+
+int qcom_ice_svc_prepare_key(const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_prepare_key(ops_ptr->dev, lt_key, lt_key_size,
+ eph_key, eph_key_size);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_prepare_key);
+
+int qcom_ice_svc_import_key(const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size)
+{
+ if (!ops_ptr)
+ return -ENODEV;
+
+ return ops_ptr->ice_import_key(ops_ptr->dev, raw_key, raw_key_size,
+ lt_key, lt_key_size);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_import_key);
+
+void qcom_ice_svc_ops_register(struct qcom_ice_ops *ops)
+{
+ if (!qcom_ice_svc_ops_registered())
+ /* Paired with smp_load_acquire() in qcom_ice_svc_ops_registered() */
+ smp_store_release(&ops_ptr, ops);
+ else
+ pr_err("qcom_ice: ops already registered (%s), rejecting %s\n",
+ ops_ptr->drv_name, ops->drv_name);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_ops_register);
+
+void qcom_ice_svc_ops_unregister(struct qcom_ice_ops *ops)
+{
+ if (READ_ONCE(ops_ptr) == ops)
+ /* Paired with smp_load_acquire() in qcom_ice_svc_ops_registered() */
+ smp_store_release(&ops_ptr, NULL);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_svc_ops_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm ICE service");
diff --git a/drivers/firmware/qcom/qcom_ice.h b/drivers/firmware/qcom/qcom_ice.h
new file mode 100644
index 000000000000..aefaf1ecc3df
--- /dev/null
+++ b/drivers/firmware/qcom/qcom_ice.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QCOM_ICE_INT_H
+#define __QCOM_ICE_INT_H
+
+#include <linux/firmware/qcom/qcom_ice.h>
+
+struct device;
+
+/**
+ * struct qcom_ice_ops - Qcom ICE service backend ops
+ * @drv_name: ICE backend driver name.
+ * @dev: ICE backend device pointer.
+ * @is_ice_available: ICE service available callback.
+ * @ice_invalidate_key: Invalidate keyslot callback.
+ * @ice_set_key: Program keyslot callback.
+ * @ice_has_wrapped_key_support: Wrapped-key support callback.
+ * @ice_derive_sw_secret: Derive software secret callback.
+ * @ice_generate_key: Generate wrapped key callback.
+ * @ice_prepare_key: Prepare wrapped key callback.
+ * @ice_import_key: Import wrapped key callback.
+ */
+struct qcom_ice_ops {
+ const char *drv_name;
+ struct device *dev;
+ bool (*is_ice_available)(struct device *dev);
+ int (*ice_invalidate_key)(struct device *dev, u32 index);
+ int (*ice_set_key)(struct device *dev, u32 index, const u8 *key,
+ u32 key_size, enum qcom_ice_cipher cipher,
+ u32 data_unit_size);
+ bool (*ice_has_wrapped_key_support)(struct device *dev);
+ int (*ice_derive_sw_secret)(struct device *dev, const u8 *eph_key,
+ size_t eph_key_size, u8 *sw_secret,
+ size_t sw_secret_size);
+ int (*ice_generate_key)(struct device *dev, u8 *lt_key,
+ size_t lt_key_size);
+ int (*ice_prepare_key)(struct device *dev, const u8 *lt_key,
+ size_t lt_key_size, u8 *eph_key,
+ size_t eph_key_size);
+ int (*ice_import_key)(struct device *dev, const u8 *raw_key,
+ size_t raw_key_size, u8 *lt_key,
+ size_t lt_key_size);
+};
+
+void qcom_ice_svc_ops_register(struct qcom_ice_ops *ops);
+void qcom_ice_svc_ops_unregister(struct qcom_ice_ops *ops);
+
+#endif /* __QCOM_ICE_INT_H */
diff --git a/include/linux/firmware/qcom/qcom_ice.h b/include/linux/firmware/qcom/qcom_ice.h
new file mode 100644
index 000000000000..c3d93259c72a
--- /dev/null
+++ b/include/linux/firmware/qcom/qcom_ice.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QCOM_TZ_ICE_H
+#define __QCOM_TZ_ICE_H
+
+#include <linux/types.h>
+
+enum qcom_ice_cipher {
+ QCOM_ICE_CIPHER_AES_128_XTS = 0,
+ QCOM_ICE_CIPHER_AES_128_CBC = 1,
+ QCOM_ICE_CIPHER_AES_256_XTS = 3,
+ QCOM_ICE_CIPHER_AES_256_CBC = 4,
+};
+
+bool qcom_ice_svc_ops_registered(void);
+bool qcom_ice_svc_available(void);
+int qcom_ice_svc_invalidate_key(u32 index);
+int qcom_ice_svc_set_key(u32 index, const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher, u32 data_unit_size);
+bool qcom_ice_svc_has_wrapped_key_support(void);
+int qcom_ice_svc_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
+ u8 *sw_secret, size_t sw_secret_size);
+int qcom_ice_svc_generate_key(u8 *lt_key, size_t lt_key_size);
+int qcom_ice_svc_prepare_key(const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size);
+int qcom_ice_svc_import_key(const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size);
+
+#endif /* __QCOM_TZ_ICE_H */
--
2.34.1