* [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service
@ 2026-11-11 14:17 Harshal Dev
2026-11-11 14:17 ` [PATCH v2 1/6] soc: qcom: ice: Add TZ agnostic re-direction functions Harshal Dev
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
Qualcomm platforms such as the IQ-9075 EVK now boot with both Qualcomm
Trusted Execution Environment (QTEE) and OP-TEE [1]. However, the Qualcomm
Inline Crypto Engine (ICE) driver currently only makes SCM calls into QTEE.
With the arrival of ICE support on OPTEE [2,3] for Qualcomm platforms,
there is a need for TrustZone (TZ) agnostic APIs which re-direct the call
to the correct TZ at runtime to request programming of the secure interfaces
on the ICE hardware block.
This patch series adds such TZ agnostic APIs to the ICE driver for
requesting secure services from either QTEE or OP-TEE depending on whether
an SCM driver back-end or a TEE client driver back-end is registered with
it at runtime. This series is logically similar to the one introduced for
Peripheral Authentication Service (PAS) [3] which was recently merged to
provide a generic PAS service for both QTEE and OP-TEE.
Apart from introducing a TEE bus based client driver for ICE, this series
also adds support for optionally registering a TEE device which represents
a particular TEE (QTEE or OPTEE) on the platform bus to enable drivers such
as ICE which poll on the availability of a TEE interface to be notified.
The ICE driver is an independent platform driver which polls on the
availability of a TEE interface (SCM driver) before checking if the ICE
secure service is supported by QTEE by querying qcom_scm_is_available().
The TEE subsystem currently does not provide any mechanism to notify the
availability of a TEE interface, i.e. whether QCOMTEE or OPTEE have probed.
Without the introduction of such a notification mechanism the ICE driver
would indefinitely defer probe until the OPTEE ICE Pseudo TA becomes available
on the TEE bus implying that for OPTEE builds without the ICE PTA enabled,
the dependent UFS/SDHC storage driver will fail to come online.
The TEE bus based client driver introduced in this series which registers
with the ICE driver has an ABI with OP-TEE that is 1-1 identical with the
SCM driver's ICE ABI with QTEE. This is because both TEEs implement logically
similar drivers with the exception that OP-TEE currently does not have
Hardware Key Manager v2 support and so the inline encryption functionality
with OP-TEE is currently limited to Qualcomm platforms with only Hardware
Key Manager v1.
Validation:
This series has been validated on a LeMans IQ-9075 EVK device by attempting
file system encryption via the fscrypt tool.
Merge Strategy:
All patches in this series except one touch Qualcomm firmware and soc drivers.
Hence, it should go via the QCOM-SoC tree. Request the TEE subsystem maintainers
to provide an Acknowledgement for this.
[1] https://ldts.github.io/qcom-buildroot/index.html
[2] https://github.com/OP-TEE/optee_os/pull/7906 (In review)
[3] https://github.com/OP-TEE/optee_os/pull/8137 (Draft, depends on PR#7906)
[4] https://lore.kernel.org/all/20260702115835.167602-1-sumit.garg@kernel.org/
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
Changes in v2:
- Merged the furnctionality of the middleware ICE service into the ICE
driver as requested by Eric.
- Introduced tee_platform_device_register() to fix the issue of indefinite
probe defer in the ICE driver when ICE PTA is not enabled in OPTEE as
pointed out by Eric.
- Fixed type "Psuedo" in ICE TEE back-end commit.
- Fixed space based indentation with tab in ICE back-end Kconfig.
- Ensure tee_shm_get_va() failure is checked and ERR_PTR() returned.
- Validate out_shm size everywhere in qcom_ice_tee.c
- Zero the memory holding the key when TEE invoke call fails.
- Check failure from qcom_ice_tz_ops_register() during probe.
- Cross-check out_size and lt_key_size in qcom_ice_tee_generate_key().
- Always return 0 when qcom_ice_tee_probe() is success.
- Did not collect tags from Abel since significant rework undertaken.
- Link to v1: https://lore.kernel.org/r/20261021-ice-migrate-tee-svc-v1-0-b035be7aab0a@oss.qualcomm.com
---
Harshal Dev (6):
soc: qcom: ice: Add TZ agnostic re-direction functions
firmware: qcom_scm: Transition to ICE TZ agnostic operations
firmware: qcom: Add a TEE bus based ICE back-end
tee: optee: Register TEE devices on the platform bus
soc: qcom: ice: Migrate to the TZ agnostic ICE APIs
firmware: qcom: scm: Drop exported ICE wrapper functions
MAINTAINERS | 8 +
drivers/firmware/qcom/Kconfig | 9 +
drivers/firmware/qcom/Makefile | 1 +
drivers/firmware/qcom/qcom_ice_tee.c | 443 +++++++++++++++++++++++++++++
drivers/firmware/qcom/qcom_scm.c | 90 +++---
drivers/soc/qcom/Kconfig | 1 +
drivers/soc/qcom/ice.c | 173 +++++++++--
drivers/tee/optee/ffa_abi.c | 8 +
drivers/tee/optee/smc_abi.c | 8 +
drivers/tee/tee_core.c | 21 ++
include/linux/firmware/qcom/qcom_ice_tee.h | 10 +
include/linux/firmware/qcom/qcom_scm.h | 20 --
include/linux/tee_core.h | 9 +
include/soc/qcom/ice.h | 56 ++++
14 files changed, 784 insertions(+), 73 deletions(-)
---
base-commit: 8f7df005a7564bd7ffa75da9c3085d5b996f1e16
change-id: 20261021-ice-migrate-tee-svc-a5cd6959ae36
Best regards,
--
Harshal Dev <harshal.dev@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/6] soc: qcom: ice: Add TZ agnostic re-direction functions
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 ` Harshal Dev
2026-11-11 14:17 ` [PATCH v2 2/6] firmware: qcom_scm: Transition to ICE TZ agnostic operations Harshal Dev
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
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 the ICE driver should call into the correct Trust
Zone (TZ) at runtime to request programming of the secure interfaces on
the hardware block.
Add TZ agnostic re-direction functions for all the current QTEE SCM calls
which can route the calls to either an SCM or TEE bus-based secure back
end when registered with ICE.
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
drivers/soc/qcom/ice.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
include/soc/qcom/ice.h | 56 ++++++++++++++++++++++
2 files changed, 180 insertions(+)
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index e256595b9826..452896b1858f 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -118,6 +118,130 @@ struct qcom_ice {
static DEFINE_XARRAY(ice_handles);
static DEFINE_MUTEX(ice_mutex);
+static struct qcom_ice_tz_ops *ice_tz_ops;
+
+static bool qcom_ice_tz_ops_registered(void)
+{
+ /*
+ * The barrier is intended to synchronize the data stores for the
+ * ice_tz_ops data structure when client drivers (UFS/SDHC) are in
+ * parallel checking for ICE service availability.
+ */
+ return !!smp_load_acquire(&ice_tz_ops);
+}
+
+static bool qcom_ice_tz_is_available(void)
+{
+ return qcom_scm_is_available();
+}
+
+static int qcom_ice_tz_invalidate_key(u32 index)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_invalidate_key(ops->dev, index);
+}
+
+static int qcom_ice_tz_set_key(u32 index, const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher,
+ u32 data_unit_size)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_set_key(ops->dev, index, key, key_size, cipher,
+ data_unit_size);
+}
+
+static bool qcom_ice_tz_has_wrapped_key_support(void)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_has_wrapped_key_support(ops->dev);
+}
+
+static int qcom_ice_tz_derive_sw_secret(const u8 *eph_key,
+ size_t eph_key_size, u8 *sw_secret,
+ size_t sw_secret_size)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_derive_sw_secret(ops->dev, eph_key, eph_key_size,
+ sw_secret, sw_secret_size);
+}
+
+static int qcom_ice_tz_generate_key(u8 *lt_key, size_t lt_key_size)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_generate_key(ops->dev, lt_key, lt_key_size);
+}
+
+static int qcom_ice_tz_prepare_key(const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_prepare_key(ops->dev, lt_key, lt_key_size, eph_key,
+ eph_key_size);
+}
+
+static int qcom_ice_tz_import_key(const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size)
+{
+ struct qcom_ice_tz_ops *ops;
+
+ ops = ice_tz_ops;
+ if (!ops)
+ return -ENODEV;
+
+ return ops->ice_import_key(ops->dev, raw_key, raw_key_size, lt_key,
+ lt_key_size);
+}
+
+int qcom_ice_tz_ops_register(struct qcom_ice_tz_ops *ops)
+{
+ if (!qcom_ice_tz_ops_registered())
+ /* Paired with smp_load_acquire() in qcom_ice_tz_ops_registered() */
+ smp_store_release(&ice_tz_ops, ops);
+ else
+ return -EBUSY;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_tz_ops_register);
+
+void qcom_ice_tz_ops_unregister(struct qcom_ice_tz_ops *ops)
+{
+ if (READ_ONCE(ice_tz_ops) == ops)
+ /* Paired with smp_load_acquire() in qcom_ice_tz_ops_registered() */
+ smp_store_release(&ice_tz_ops, NULL);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_tz_ops_unregister);
+
static bool qcom_ice_check_supported(struct qcom_ice *ice)
{
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h
index 4bee553f0a59..89e62e151b85 100644
--- a/include/soc/qcom/ice.h
+++ b/include/soc/qcom/ice.h
@@ -10,6 +10,47 @@
#include <linux/types.h>
struct qcom_ice;
+struct device;
+
+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,
+};
+
+/**
+ * struct qcom_ice_tz_ops - Qcom ICE TZ operations
+ * @drv_name: ICE TZ backend driver name.
+ * @dev: ICE TZ backend device.
+ * @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_tz_ops {
+ const char *drv_name;
+ 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);
+};
int qcom_ice_enable(struct qcom_ice *ice);
int qcom_ice_resume(struct qcom_ice *ice);
@@ -31,4 +72,19 @@ int qcom_ice_import_key(struct qcom_ice *ice,
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
struct qcom_ice *devm_of_qcom_ice_get(struct device *dev);
+#ifdef CONFIG_QCOM_INLINE_CRYPTO_ENGINE
+int qcom_ice_tz_ops_register(struct qcom_ice_tz_ops *ops);
+void qcom_ice_tz_ops_unregister(struct qcom_ice_tz_ops *ops);
+#else
+static inline int qcom_ice_tz_ops_register(struct qcom_ice_tz_ops *__unused)
+{
+ return 0;
+}
+
+static inline
+void qcom_ice_tz_ops_unregister(struct qcom_ice_tz_ops *__unused)
+{
+}
+#endif /* CONFIG_QCOM_INLINE_CRYPTO_ENGINE */
+
#endif /* __QCOM_ICE_H__ */
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/6] firmware: qcom_scm: Transition to ICE TZ agnostic operations
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 ` Harshal Dev
2026-11-11 14:17 ` [PATCH v2 3/6] firmware: qcom: Add a TEE bus based ICE back-end Harshal Dev
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
Transition the ICE related SCM calls to the generic ICE TZ operations by
registering them with the ICE driver and wrapping them in exported APIs
with the same signature.
This prepares the ICE driver for eventual migration to the TZ agnostic
secure programming interfaces once the TEE bus based back-end becomes
available.
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 131 +++++++++++++++++++++++++++++++--------
1 file changed, 106 insertions(+), 25 deletions(-)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 43e250faea75..6b66bcedc84f 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -32,6 +32,8 @@
#include <linux/sizes.h>
#include <linux/types.h>
+#include <soc/qcom/ice.h>
+
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "qcom_pas.h"
@@ -1397,6 +1399,14 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
}
EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock);
+bool __qcom_scm_ice_available(struct device *dev)
+{
+ return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
+ QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
+ __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
+ QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
+}
+
/**
* qcom_scm_ice_available() - Is the ICE key programming interface available?
*
@@ -1405,10 +1415,7 @@ EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock);
*/
bool qcom_scm_ice_available(void)
{
- return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
- QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
- __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
- QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
+ return __qcom_scm_ice_available(__scm->dev);
}
EXPORT_SYMBOL_GPL(qcom_scm_ice_available);
@@ -1424,7 +1431,7 @@ EXPORT_SYMBOL_GPL(qcom_scm_ice_available);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_ice_invalidate_key(u32 index)
+static int __qcom_scm_ice_invalidate_key(struct device *dev, u32 index)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1434,7 +1441,12 @@ int qcom_scm_ice_invalidate_key(u32 index)
.owner = ARM_SMCCC_OWNER_SIP,
};
- return qcom_scm_call(__scm->dev, &desc, NULL);
+ return qcom_scm_call(dev, &desc, NULL);
+}
+
+int qcom_scm_ice_invalidate_key(u32 index)
+{
+ return __qcom_scm_ice_invalidate_key(__scm->dev, index);
}
EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
@@ -1459,8 +1471,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
- enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
+static int __qcom_scm_ice_set_key(struct device *dev, u32 index,
+ const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher,
+ u32 data_unit_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1485,25 +1499,38 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
memcpy(keybuf, key, key_size);
desc.args[1] = qcom_tzmem_to_phys(keybuf);
- ret = qcom_scm_call(__scm->dev, &desc, NULL);
+ ret = qcom_scm_call(dev, &desc, NULL);
memzero_explicit(keybuf, key_size);
return ret;
}
+
+int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
+ enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
+{
+ return __qcom_scm_ice_set_key(__scm->dev, index, key, key_size,
+ (enum qcom_ice_cipher)cipher,
+ data_unit_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
-bool qcom_scm_has_wrapped_key_support(void)
+static bool __qcom_scm_has_wrapped_key_support(struct device *dev)
{
- return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
+ return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
QCOM_SCM_ES_DERIVE_SW_SECRET) &&
- __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
+ __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
QCOM_SCM_ES_GENERATE_ICE_KEY) &&
- __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
+ __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
QCOM_SCM_ES_PREPARE_ICE_KEY) &&
- __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES,
+ __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
QCOM_SCM_ES_IMPORT_ICE_KEY);
}
+
+bool qcom_scm_has_wrapped_key_support(void)
+{
+ return __qcom_scm_has_wrapped_key_support(__scm->dev);
+}
EXPORT_SYMBOL_GPL(qcom_scm_has_wrapped_key_support);
/**
@@ -1522,8 +1549,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_has_wrapped_key_support);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
- u8 *sw_secret, size_t sw_secret_size)
+static int __qcom_scm_derive_sw_secret(struct device *dev,
+ const u8 *eph_key, size_t eph_key_size,
+ u8 *sw_secret, size_t sw_secret_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1552,7 +1580,7 @@ int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
desc.args[2] = qcom_tzmem_to_phys(sw_secret_buf);
desc.args[3] = sw_secret_size;
- ret = qcom_scm_call(__scm->dev, &desc, NULL);
+ ret = qcom_scm_call(dev, &desc, NULL);
if (!ret)
memcpy(sw_secret, sw_secret_buf, sw_secret_size);
@@ -1560,6 +1588,13 @@ int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
memzero_explicit(sw_secret_buf, sw_secret_size);
return ret;
}
+
+int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
+ u8 *sw_secret, size_t sw_secret_size)
+{
+ return __qcom_scm_derive_sw_secret(__scm->dev, eph_key, eph_key_size,
+ sw_secret, sw_secret_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_derive_sw_secret);
/**
@@ -1573,7 +1608,8 @@ EXPORT_SYMBOL_GPL(qcom_scm_derive_sw_secret);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_generate_ice_key(u8 *lt_key, size_t lt_key_size)
+static int __qcom_scm_generate_ice_key(struct device *dev,
+ u8 *lt_key, size_t lt_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1592,13 +1628,18 @@ int qcom_scm_generate_ice_key(u8 *lt_key, size_t lt_key_size)
desc.args[0] = qcom_tzmem_to_phys(lt_key_buf);
desc.args[1] = lt_key_size;
- ret = qcom_scm_call(__scm->dev, &desc, NULL);
+ ret = qcom_scm_call(dev, &desc, NULL);
if (!ret)
memcpy(lt_key, lt_key_buf, lt_key_size);
memzero_explicit(lt_key_buf, lt_key_size);
return ret;
}
+
+int qcom_scm_generate_ice_key(u8 *lt_key, size_t lt_key_size)
+{
+ return __qcom_scm_generate_ice_key(__scm->dev, lt_key, lt_key_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_generate_ice_key);
/**
@@ -1614,8 +1655,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_generate_ice_key);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
- u8 *eph_key, size_t eph_key_size)
+static int __qcom_scm_prepare_ice_key(struct device *dev,
+ const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1644,7 +1686,7 @@ int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
desc.args[2] = qcom_tzmem_to_phys(eph_key_buf);
desc.args[3] = eph_key_size;
- ret = qcom_scm_call(__scm->dev, &desc, NULL);
+ ret = qcom_scm_call(dev, &desc, NULL);
if (!ret)
memcpy(eph_key, eph_key_buf, eph_key_size);
@@ -1652,6 +1694,13 @@ int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
memzero_explicit(eph_key_buf, eph_key_size);
return ret;
}
+
+int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size)
+{
+ return __qcom_scm_prepare_ice_key(__scm->dev, lt_key, lt_key_size,
+ eph_key, eph_key_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_prepare_ice_key);
/**
@@ -1667,8 +1716,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_prepare_ice_key);
*
* Return: 0 on success; -errno on failure.
*/
-int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
- u8 *lt_key, size_t lt_key_size)
+static int __qcom_scm_import_ice_key(struct device *dev,
+ const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1697,7 +1747,7 @@ int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
desc.args[2] = qcom_tzmem_to_phys(lt_key_buf);
desc.args[3] = lt_key_size;
- ret = qcom_scm_call(__scm->dev, &desc, NULL);
+ ret = qcom_scm_call(dev, &desc, NULL);
if (!ret)
memcpy(lt_key, lt_key_buf, lt_key_size);
@@ -1705,8 +1755,26 @@ int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
memzero_explicit(lt_key_buf, lt_key_size);
return ret;
}
+
+int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size)
+{
+ return __qcom_scm_import_ice_key(__scm->dev, raw_key, raw_key_size,
+ lt_key, lt_key_size);
+}
EXPORT_SYMBOL_GPL(qcom_scm_import_ice_key);
+static struct qcom_ice_tz_ops qcom_ice_scm_ops = {
+ .drv_name = "qcom_scm",
+ .ice_invalidate_key = __qcom_scm_ice_invalidate_key,
+ .ice_set_key = __qcom_scm_ice_set_key,
+ .ice_has_wrapped_key_support = __qcom_scm_has_wrapped_key_support,
+ .ice_derive_sw_secret = __qcom_scm_derive_sw_secret,
+ .ice_generate_key = __qcom_scm_generate_ice_key,
+ .ice_prepare_key = __qcom_scm_prepare_ice_key,
+ .ice_import_key = __qcom_scm_import_ice_key,
+};
+
/**
* qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
*
@@ -2820,6 +2888,18 @@ static int qcom_scm_probe(struct platform_device *pdev)
"Failed to request qcom-scm irq\n");
}
+ /*
+ * If the ICE SCM interface is available, the operations MUST be
+ * registered before the SCM API is published below, otherwise
+ * ICE driver could attempt to call the ICE SCM pointers before
+ * they are registered and assume that they aren't available.
+ */
+ if (__qcom_scm_ice_available(scm->dev)) {
+ qcom_ice_scm_ops.dev = scm->dev;
+ ret = qcom_ice_tz_ops_register(&qcom_ice_scm_ops);
+ WARN(ret < 0, "failed to register ice SCM ops: %d\n", ret);
+ }
+
/*
* Paired with smp_load_acquire() in qcom_scm_is_available().
*
@@ -2876,6 +2956,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev)
/* Clean shutdown, disable download mode to allow normal restart */
qcom_scm_set_download_mode(__scm, QCOM_DLOAD_NODUMP);
qcom_pas_ops_unregister();
+ qcom_ice_tz_ops_unregister(&qcom_ice_scm_ops);
}
static const struct of_device_id qcom_scm_dt_match[] = {
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 3/6] firmware: qcom: Add a TEE bus based ICE back-end
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
2026-11-11 14:17 ` [PATCH v2 4/6] tee: optee: Register TEE devices on the platform bus Harshal Dev
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 4/6] tee: optee: Register TEE devices on the platform bus
2026-11-11 14:17 [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
` (2 preceding siblings ...)
2026-11-11 14:17 ` [PATCH v2 3/6] firmware: qcom: Add a TEE bus based ICE back-end Harshal Dev
@ 2026-11-11 14:17 ` 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
` (2 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
The Qualcomm Inline Crypto Engine (ICE) driver is an independent platform
driver which polls on availability of a TEE interface (SCM driver) before
checking if the ICE secure service is supported by QTEE by querying the
qcom_scm_ice_available() API.
The TEE subsystem currently does not provide any way to notify regarding
the availability of a TEE interface, i.e. whether QCOMTEE or OPTEE have
probed. And so when migrating to the TEE subsystem via an ICE TEE client
driver, ICE needs to defer probe until the OPTEE ICE Pseudo TA becomes
available on the TEE bus. This means that for OPTEE builds without the
ICE PTA, the ICE driver would indefintely defer probe and the dependent
UFS/SDHC storage driver will fail to come online.
Add support for optionally registering the back-end TEE devices which
represent a particular TEE (say OPTEE) on the platform bus via the
tee_platform_device_register() API. This allows drivers which poll on
TEE secure interface availability to be notified even if the TEE service
itself is not supported.
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
MAINTAINERS | 1 +
drivers/firmware/qcom/qcom_ice_tee.c | 41 ++++++++++++++++++++++++++++++
drivers/soc/qcom/Kconfig | 1 +
drivers/soc/qcom/ice.c | 9 ++++++-
drivers/tee/optee/ffa_abi.c | 8 ++++++
drivers/tee/optee/smc_abi.c | 8 ++++++
drivers/tee/tee_core.c | 21 +++++++++++++++
include/linux/firmware/qcom/qcom_ice_tee.h | 10 ++++++++
include/linux/tee_core.h | 9 +++++++
9 files changed, 107 insertions(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1132e456488b..06dba76996b8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23013,6 +23013,7 @@ L: linux-arm-msm@vger.kernel.org
L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/firmware/qcom/qcom_ice_tee.c
+F: include/linux/firmware/qcom/qcom_ice_tee.h
QUALCOMM INTERCONNECT BWMON DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
diff --git a/drivers/firmware/qcom/qcom_ice_tee.c b/drivers/firmware/qcom/qcom_ice_tee.c
index 694aa05847f0..39f7094439d9 100644
--- a/drivers/firmware/qcom/qcom_ice_tee.c
+++ b/drivers/firmware/qcom/qcom_ice_tee.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/uuid.h>
@@ -22,6 +23,9 @@
#define TEE_NUM_PARAMS 4
+/* This represents the back-end OPTEE device */
+static struct device *__teedev;
+
struct qcom_ice_tee_private {
struct device *dev;
struct tee_context *ctx;
@@ -281,6 +285,18 @@ static int qcom_ice_tee_import_key(struct device *dev,
lt_key, lt_key_size);
}
+bool qcom_ice_tee_is_available(void)
+{
+ /*
+ * Paired with smp_load_acquire() in qcom_ice_plat_probe().
+ *
+ * This marks the availability of the OPTEE interface informing the
+ * polling ICE driver that the platform supports a TEE.
+ */
+ return !!smp_load_acquire(&__teedev);
+}
+EXPORT_SYMBOL_GPL(qcom_ice_tee_is_available);
+
static bool qcom_ice_tee_has_wrapped_key_support(struct device *dev)
{
struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
@@ -398,5 +414,30 @@ static struct tee_client_driver qcom_ice_tee_driver = {
module_tee_client_driver(qcom_ice_tee_driver);
+static int qcom_ice_plat_probe(struct platform_device *pdev)
+{
+ /* Paired with smp_load_acquire() in qcom_ice_tee_is_available() */
+ smp_store_release(&__teedev, &pdev->dev);
+ return 0;
+}
+
+static const struct platform_device_id qcom_ice_plat_id_table[] = {
+ {
+ .name = "optee",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, qcom_ice_plat_id_table);
+
+static struct platform_driver qcom_ice_plat_driver = {
+ .probe = qcom_ice_plat_probe,
+ .id_table = qcom_ice_plat_id_table,
+ .driver = {
+ .name = "qcom-ice-plat",
+ },
+};
+
+module_platform_driver(qcom_ice_plat_driver);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Qualcomm ICE TEE driver");
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 535c8619197b..8dc75c252f52 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -296,6 +296,7 @@ endif
config QCOM_INLINE_CRYPTO_ENGINE
tristate
select QCOM_SCM
+ select QCOM_ICE_TEE
config QCOM_KRYO_L2_ACCESSORS
bool
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index 452896b1858f..4844621b7497 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/xarray.h>
+#include <linux/firmware/qcom/qcom_ice_tee.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <soc/qcom/ice.h>
@@ -132,7 +133,13 @@ static bool qcom_ice_tz_ops_registered(void)
static bool qcom_ice_tz_is_available(void)
{
- return qcom_scm_is_available();
+ /*
+ * Both back-ends should be available before we check for
+ * qcom_ice_tz_ops_registered(), otherwise we could pre-maturely
+ * assume that TZ does not provide any ICE secure interface.
+ */
+ return (qcom_scm_is_available() & qcom_ice_tee_is_available());
+
}
static int qcom_ice_tz_invalidate_key(u32 index)
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index 702da7d98fbc..2c0065f3d9e4 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -1177,6 +1177,14 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (rc)
goto err_unregister_devices;
+ /*
+ * Device should only be visible on the platform bus once the
+ * child devices have already been enumerated on the TEE bus.
+ */
+ rc = tee_platform_device_register(optee->teedev, "optee");
+ if (rc)
+ goto err_unregister_devices;
+
INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
blocking_notifier_chain_register(&optee_rpmb_intf_added,
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 51624443359b..85ee76173982 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1920,6 +1920,14 @@ static int optee_probe(struct platform_device *pdev)
if (rc)
goto err_disable_shm_cache;
+ /*
+ * Device should only be visible on the platform bus once the
+ * child devices have already been enumerated on the TEE bus.
+ */
+ rc = tee_platform_device_register(optee->teedev, "optee");
+ if (rc)
+ goto err_disable_shm_cache;
+
INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
blocking_notifier_chain_register(&optee_rpmb_intf_added,
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 1aac50c7c1de..0d3c266b29c7 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -10,6 +10,7 @@
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/tee_core.h>
@@ -1225,6 +1226,26 @@ int tee_device_register(struct tee_device *teedev)
}
EXPORT_SYMBOL_GPL(tee_device_register);
+static void plat_tee_free(void *data)
+{
+ struct platform_device *plat_dev = data;
+
+ platform_device_unregister(plat_dev);
+}
+
+int tee_platform_device_register(struct tee_device *teedev, const char *teename)
+{
+ struct platform_device *plat_dev;
+
+ plat_dev = platform_device_register_data(&teedev->dev, teename,
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(plat_dev))
+ return PTR_ERR(plat_dev);
+
+ devm_add_action_or_reset(&teedev->dev, plat_tee_free, plat_dev);
+ return 0;
+}
+
void tee_device_put(struct tee_device *teedev)
{
mutex_lock(&teedev->mutex);
diff --git a/include/linux/firmware/qcom/qcom_ice_tee.h b/include/linux/firmware/qcom/qcom_ice_tee.h
new file mode 100644
index 000000000000..a5906c5d4e39
--- /dev/null
+++ b/include/linux/firmware/qcom/qcom_ice_tee.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#ifndef __QCOM_ICE_TEE
+#define __QCOM_ICE_TEE
+
+bool qcom_ice_tee_is_available(void);
+
+#endif /* __QCOM_ICE_TEE */
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index f993d5118edd..d5310b404c1e 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -205,6 +205,15 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
*/
int tee_device_register(struct tee_device *teedev);
+/**
+ * tee_platform_device_register() - Registers a TEE platform device
+ * @teedev: Device to register
+ * @teename: Name of the TEE the device represents
+ *
+ * @returns: < 0 on failure
+ */
+int tee_platform_device_register(struct tee_device *teedev, const char *teename);
+
/**
* tee_device_unregister() - Removes a TEE device
* @teedev: Device to unregister
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 5/6] soc: qcom: ice: Migrate to the TZ agnostic ICE APIs
2026-11-11 14:17 [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
` (3 preceding siblings ...)
2026-11-11 14:17 ` [PATCH v2 4/6] tee: optee: Register TEE devices on the platform bus Harshal Dev
@ 2026-11-11 14:17 ` 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
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
Migrate the Inline Crypto Engine (ICE) driver to the TZ agnostic ICE APIs
which re-directs calls from the driver to either the SCM or the TEE bus
based back-end driver at runtime.
This makes the ICE driver agnostic of the TEE firmware (QTEE or OPTEE)
running on the Qualcomm platform.
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
drivers/soc/qcom/ice.c | 42 +++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index 4844621b7497..9bab177f7ce5 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -307,7 +307,7 @@ static bool qcom_ice_check_supported(struct qcom_ice *ice)
* advertise support for raw keys or wrapped keys, HWKM cannot be used
* unconditionally. A module parameter is used to opt into using it.
*/
- if (ice->hwkm_version && qcom_scm_has_wrapped_key_support()) {
+ if (ice->hwkm_version && qcom_ice_tz_has_wrapped_key_support()) {
if (qcom_ice_use_wrapped_keys) {
dev_info(dev, "Using HWKM. Supporting wrapped keys only.\n");
ice->use_hwkm = true;
@@ -483,7 +483,7 @@ static int qcom_ice_program_wrapped_key(struct qcom_ice *ice, unsigned int slot,
struct device *dev = ice->dev;
union crypto_cfg cfg = {
.dusize = bkey->crypto_cfg.data_unit_size / 512,
- .capidx = QCOM_SCM_ICE_CIPHER_AES_256_XTS,
+ .capidx = QCOM_ICE_CIPHER_AES_256_XTS,
.cfge = QCOM_ICE_HWKM_CFG_ENABLE_VAL,
};
int err;
@@ -501,11 +501,11 @@ static int qcom_ice_program_wrapped_key(struct qcom_ice *ice, unsigned int slot,
qcom_ice_writel(ice, 0x0, QCOM_ICE_REG_CRYPTOCFG(slot));
/* Call into TrustZone to program the wrapped key using HWKM. */
- err = qcom_scm_ice_set_key(translate_hwkm_slot(ice, slot), bkey->bytes,
- bkey->size, cfg.capidx, cfg.dusize);
+ err = qcom_ice_tz_set_key(translate_hwkm_slot(ice, slot), bkey->bytes,
+ bkey->size, cfg.capidx, cfg.dusize);
if (err) {
dev_err_ratelimited(dev,
- "qcom_scm_ice_set_key failed; err=%d, slot=%u\n",
+ "qcom_ice_tz_set_key failed; err=%d, slot=%u\n",
err, slot);
return err;
}
@@ -553,9 +553,9 @@ int qcom_ice_program_key(struct qcom_ice *ice, unsigned int slot,
for (i = 0; i < ARRAY_SIZE(key.words); i++)
__cpu_to_be32s(&key.words[i]);
- err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
- QCOM_SCM_ICE_CIPHER_AES_256_XTS,
- blk_key->crypto_cfg.data_unit_size / 512);
+ err = qcom_ice_tz_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
+ QCOM_ICE_CIPHER_AES_256_XTS,
+ blk_key->crypto_cfg.data_unit_size / 512);
memzero_explicit(&key, sizeof(key));
@@ -567,7 +567,7 @@ int qcom_ice_evict_key(struct qcom_ice *ice, int slot)
{
if (ice->hwkm_init_complete)
slot = translate_hwkm_slot(ice, slot);
- return qcom_scm_ice_invalidate_key(slot);
+ return qcom_ice_tz_invalidate_key(slot);
}
EXPORT_SYMBOL_GPL(qcom_ice_evict_key);
@@ -604,9 +604,9 @@ int qcom_ice_derive_sw_secret(struct qcom_ice *ice,
const u8 *eph_key, size_t eph_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
{
- int err = qcom_scm_derive_sw_secret(eph_key, eph_key_size,
- sw_secret,
- BLK_CRYPTO_SW_SECRET_SIZE);
+ int err = qcom_ice_tz_derive_sw_secret(eph_key, eph_key_size,
+ sw_secret,
+ BLK_CRYPTO_SW_SECRET_SIZE);
if (err == -EIO || err == -EINVAL)
err = -EBADMSG; /* probably invalid key */
return err;
@@ -627,8 +627,8 @@ int qcom_ice_generate_key(struct qcom_ice *ice,
{
int err;
- err = qcom_scm_generate_ice_key(lt_key,
- QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
+ err = qcom_ice_tz_generate_key(lt_key,
+ QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
if (err)
return err;
@@ -654,8 +654,8 @@ int qcom_ice_prepare_key(struct qcom_ice *ice,
{
int err;
- err = qcom_scm_prepare_ice_key(lt_key, lt_key_size,
- eph_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
+ err = qcom_ice_tz_prepare_key(lt_key, lt_key_size,
+ eph_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
if (err == -EIO || err == -EINVAL)
err = -EBADMSG; /* probably invalid key */
if (err)
@@ -682,8 +682,8 @@ int qcom_ice_import_key(struct qcom_ice *ice,
{
int err;
- err = qcom_scm_import_ice_key(raw_key, raw_key_size,
- lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
+ err = qcom_ice_tz_import_key(raw_key, raw_key_size,
+ lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE(ice->hwkm_version));
if (err)
return err;
@@ -696,11 +696,11 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
{
struct qcom_ice *engine;
- if (!qcom_scm_is_available())
+ if (!qcom_ice_tz_is_available())
return ERR_PTR(-EPROBE_DEFER);
- if (!qcom_scm_ice_available()) {
- dev_warn(dev, "ICE SCM interface not found\n");
+ if (!qcom_ice_tz_ops_registered()) {
+ dev_warn(dev, "ICE firmware service interface not found\n");
return ERR_PTR(-EOPNOTSUPP);
}
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 6/6] firmware: qcom: scm: Drop exported ICE wrapper functions
2026-11-11 14:17 [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
` (4 preceding siblings ...)
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 ` Harshal Dev
2026-11-11 14:21 ` [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:17 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, Harshal Dev
Drop the exported ICE wrapper functions from the SCM driver since the ICE
driver has now migrated to the TZ agnostic ICE APIs which allow runtime
routing of ICE secure calls to the appropriate TEE (QTEE or OPTEE).
Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 117 +++++++++------------------------
include/linux/firmware/qcom/qcom_scm.h | 20 ------
2 files changed, 31 insertions(+), 106 deletions(-)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b66bcedc84f..277334f71178 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -1399,25 +1399,20 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
}
EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock);
-bool __qcom_scm_ice_available(struct device *dev)
-{
- return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
- QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
- __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
- QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
-}
-
/**
* qcom_scm_ice_available() - Is the ICE key programming interface available?
+ * @dev: The SCM device
*
* Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and
* qcom_scm_ice_set_key() are available.
*/
-bool qcom_scm_ice_available(void)
+static bool qcom_scm_ice_available(struct device *dev)
{
- return __qcom_scm_ice_available(__scm->dev);
+ return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
+ QCOM_SCM_ES_INVALIDATE_ICE_KEY) &&
+ __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
+ QCOM_SCM_ES_CONFIG_SET_ICE_KEY);
}
-EXPORT_SYMBOL_GPL(qcom_scm_ice_available);
/**
* qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key
@@ -1431,7 +1426,7 @@ EXPORT_SYMBOL_GPL(qcom_scm_ice_available);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_ice_invalidate_key(struct device *dev, u32 index)
+static int qcom_scm_ice_invalidate_key(struct device *dev, u32 index)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1444,12 +1439,6 @@ static int __qcom_scm_ice_invalidate_key(struct device *dev, u32 index)
return qcom_scm_call(dev, &desc, NULL);
}
-int qcom_scm_ice_invalidate_key(u32 index)
-{
- return __qcom_scm_ice_invalidate_key(__scm->dev, index);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
-
/**
* qcom_scm_ice_set_key() - Set an inline encryption key
* @index: the keyslot into which to set the key
@@ -1471,10 +1460,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_ice_set_key(struct device *dev, u32 index,
- const u8 *key, u32 key_size,
- enum qcom_ice_cipher cipher,
- u32 data_unit_size)
+static int qcom_scm_ice_set_key(struct device *dev, u32 index,
+ const u8 *key, u32 key_size,
+ enum qcom_ice_cipher cipher,
+ u32 data_unit_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1506,16 +1495,7 @@ static int __qcom_scm_ice_set_key(struct device *dev, u32 index,
return ret;
}
-int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
- enum qcom_scm_ice_cipher cipher, u32 data_unit_size)
-{
- return __qcom_scm_ice_set_key(__scm->dev, index, key, key_size,
- (enum qcom_ice_cipher)cipher,
- data_unit_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
-
-static bool __qcom_scm_has_wrapped_key_support(struct device *dev)
+static bool qcom_scm_has_wrapped_key_support(struct device *dev)
{
return __qcom_scm_is_call_available(dev, QCOM_SCM_SVC_ES,
QCOM_SCM_ES_DERIVE_SW_SECRET) &&
@@ -1527,12 +1507,6 @@ static bool __qcom_scm_has_wrapped_key_support(struct device *dev)
QCOM_SCM_ES_IMPORT_ICE_KEY);
}
-bool qcom_scm_has_wrapped_key_support(void)
-{
- return __qcom_scm_has_wrapped_key_support(__scm->dev);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_has_wrapped_key_support);
-
/**
* qcom_scm_derive_sw_secret() - Derive software secret from wrapped key
* @eph_key: an ephemerally-wrapped key
@@ -1549,9 +1523,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_has_wrapped_key_support);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_derive_sw_secret(struct device *dev,
- const u8 *eph_key, size_t eph_key_size,
- u8 *sw_secret, size_t sw_secret_size)
+static int qcom_scm_derive_sw_secret(struct device *dev,
+ const u8 *eph_key, size_t eph_key_size,
+ u8 *sw_secret, size_t sw_secret_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1589,13 +1563,6 @@ static int __qcom_scm_derive_sw_secret(struct device *dev,
return ret;
}
-int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
- u8 *sw_secret, size_t sw_secret_size)
-{
- return __qcom_scm_derive_sw_secret(__scm->dev, eph_key, eph_key_size,
- sw_secret, sw_secret_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_derive_sw_secret);
/**
* qcom_scm_generate_ice_key() - Generate a wrapped key for storage encryption
@@ -1608,8 +1575,8 @@ EXPORT_SYMBOL_GPL(qcom_scm_derive_sw_secret);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_generate_ice_key(struct device *dev,
- u8 *lt_key, size_t lt_key_size)
+static int qcom_scm_generate_ice_key(struct device *dev,
+ u8 *lt_key, size_t lt_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1636,12 +1603,6 @@ static int __qcom_scm_generate_ice_key(struct device *dev,
return ret;
}
-int qcom_scm_generate_ice_key(u8 *lt_key, size_t lt_key_size)
-{
- return __qcom_scm_generate_ice_key(__scm->dev, lt_key, lt_key_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_generate_ice_key);
-
/**
* qcom_scm_prepare_ice_key() - Re-wrap a key with the per-boot ephemeral key
* @lt_key: a long-term wrapped key
@@ -1655,9 +1616,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_generate_ice_key);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_prepare_ice_key(struct device *dev,
- const u8 *lt_key, size_t lt_key_size,
- u8 *eph_key, size_t eph_key_size)
+static int qcom_scm_prepare_ice_key(struct device *dev,
+ const u8 *lt_key, size_t lt_key_size,
+ u8 *eph_key, size_t eph_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1695,14 +1656,6 @@ static int __qcom_scm_prepare_ice_key(struct device *dev,
return ret;
}
-int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
- u8 *eph_key, size_t eph_key_size)
-{
- return __qcom_scm_prepare_ice_key(__scm->dev, lt_key, lt_key_size,
- eph_key, eph_key_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_prepare_ice_key);
-
/**
* qcom_scm_import_ice_key() - Import key for storage encryption
* @raw_key: the raw key to import
@@ -1716,9 +1669,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_prepare_ice_key);
*
* Return: 0 on success; -errno on failure.
*/
-static int __qcom_scm_import_ice_key(struct device *dev,
- const u8 *raw_key, size_t raw_key_size,
- u8 *lt_key, size_t lt_key_size)
+static int qcom_scm_import_ice_key(struct device *dev,
+ const u8 *raw_key, size_t raw_key_size,
+ u8 *lt_key, size_t lt_key_size)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_ES,
@@ -1756,23 +1709,15 @@ static int __qcom_scm_import_ice_key(struct device *dev,
return ret;
}
-int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
- u8 *lt_key, size_t lt_key_size)
-{
- return __qcom_scm_import_ice_key(__scm->dev, raw_key, raw_key_size,
- lt_key, lt_key_size);
-}
-EXPORT_SYMBOL_GPL(qcom_scm_import_ice_key);
-
static struct qcom_ice_tz_ops qcom_ice_scm_ops = {
.drv_name = "qcom_scm",
- .ice_invalidate_key = __qcom_scm_ice_invalidate_key,
- .ice_set_key = __qcom_scm_ice_set_key,
- .ice_has_wrapped_key_support = __qcom_scm_has_wrapped_key_support,
- .ice_derive_sw_secret = __qcom_scm_derive_sw_secret,
- .ice_generate_key = __qcom_scm_generate_ice_key,
- .ice_prepare_key = __qcom_scm_prepare_ice_key,
- .ice_import_key = __qcom_scm_import_ice_key,
+ .ice_invalidate_key = qcom_scm_ice_invalidate_key,
+ .ice_set_key = qcom_scm_ice_set_key,
+ .ice_has_wrapped_key_support = qcom_scm_has_wrapped_key_support,
+ .ice_derive_sw_secret = qcom_scm_derive_sw_secret,
+ .ice_generate_key = qcom_scm_generate_ice_key,
+ .ice_prepare_key = qcom_scm_prepare_ice_key,
+ .ice_import_key = qcom_scm_import_ice_key,
};
/**
@@ -2894,7 +2839,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
* ICE driver could attempt to call the ICE SCM pointers before
* they are registered and assume that they aren't available.
*/
- if (__qcom_scm_ice_available(scm->dev)) {
+ if (qcom_scm_ice_available(scm->dev)) {
qcom_ice_scm_ops.dev = scm->dev;
ret = qcom_ice_tz_ops_register(&qcom_ice_scm_ops);
WARN(ret < 0, "failed to register ice SCM ops: %d\n", ret);
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index a0a6bc0229c4..36b87be82804 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -46,13 +46,6 @@ enum qcom_scm_sec_dev_id {
QCOM_SCM_ICE_DEV_ID = 20,
};
-enum qcom_scm_ice_cipher {
- QCOM_SCM_ICE_CIPHER_AES_128_XTS = 0,
- QCOM_SCM_ICE_CIPHER_AES_128_CBC = 1,
- QCOM_SCM_ICE_CIPHER_AES_256_XTS = 3,
- QCOM_SCM_ICE_CIPHER_AES_256_CBC = 4,
-};
-
#define QCOM_SCM_PERM_READ 0x4
#define QCOM_SCM_PERM_WRITE 0x2
#define QCOM_SCM_PERM_EXEC 0x1
@@ -86,19 +79,6 @@ int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size,
u32 mode);
int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size);
-bool qcom_scm_ice_available(void);
-int qcom_scm_ice_invalidate_key(u32 index);
-int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
- enum qcom_scm_ice_cipher cipher, u32 data_unit_size);
-bool qcom_scm_has_wrapped_key_support(void);
-int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,
- u8 *sw_secret, size_t sw_secret_size);
-int qcom_scm_generate_ice_key(u8 *lt_key, size_t lt_key_size);
-int qcom_scm_prepare_ice_key(const u8 *lt_key, size_t lt_key_size,
- u8 *eph_key, size_t eph_key_size);
-int qcom_scm_import_ice_key(const u8 *raw_key, size_t raw_key_size,
- u8 *lt_key, size_t lt_key_size);
-
bool qcom_scm_hdcp_available(void);
int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service
2026-11-11 14:17 [PATCH v2 0/6] Add support for a TEE agnostic Inline Crypto Engine service Harshal Dev
` (5 preceding siblings ...)
2026-11-11 14:17 ` [PATCH v2 6/6] firmware: qcom: scm: Drop exported ICE wrapper functions Harshal Dev
@ 2026-11-11 14:21 ` Harshal Dev
6 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:21 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan, Jens Wiklander
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, op-tee
+ op-tee@lists.trustedfirmware.org, Jens Wiklander <jens.wiklander@oss.qualcomm.com>
On 11-11-2026 07:47 pm, Harshal Dev wrote:
> Qualcomm platforms such as the IQ-9075 EVK now boot with both Qualcomm
> Trusted Execution Environment (QTEE) and OP-TEE [1]. However, the Qualcomm
> Inline Crypto Engine (ICE) driver currently only makes SCM calls into QTEE.
> With the arrival of ICE support on OPTEE [2,3] for Qualcomm platforms,
> there is a need for TrustZone (TZ) agnostic APIs which re-direct the call
> to the correct TZ at runtime to request programming of the secure interfaces
> on the ICE hardware block.
>
> This patch series adds such TZ agnostic APIs to the ICE driver for
> requesting secure services from either QTEE or OP-TEE depending on whether
> an SCM driver back-end or a TEE client driver back-end is registered with
> it at runtime. This series is logically similar to the one introduced for
> Peripheral Authentication Service (PAS) [3] which was recently merged to
> provide a generic PAS service for both QTEE and OP-TEE.
>
> Apart from introducing a TEE bus based client driver for ICE, this series
> also adds support for optionally registering a TEE device which represents
> a particular TEE (QTEE or OPTEE) on the platform bus to enable drivers such
> as ICE which poll on the availability of a TEE interface to be notified.
>
> The ICE driver is an independent platform driver which polls on the
> availability of a TEE interface (SCM driver) before checking if the ICE
> secure service is supported by QTEE by querying qcom_scm_is_available().
> The TEE subsystem currently does not provide any mechanism to notify the
> availability of a TEE interface, i.e. whether QCOMTEE or OPTEE have probed.
> Without the introduction of such a notification mechanism the ICE driver
> would indefinitely defer probe until the OPTEE ICE Pseudo TA becomes available
> on the TEE bus implying that for OPTEE builds without the ICE PTA enabled,
> the dependent UFS/SDHC storage driver will fail to come online.
>
> The TEE bus based client driver introduced in this series which registers
> with the ICE driver has an ABI with OP-TEE that is 1-1 identical with the
> SCM driver's ICE ABI with QTEE. This is because both TEEs implement logically
> similar drivers with the exception that OP-TEE currently does not have
> Hardware Key Manager v2 support and so the inline encryption functionality
> with OP-TEE is currently limited to Qualcomm platforms with only Hardware
> Key Manager v1.
>
> Validation:
> This series has been validated on a LeMans IQ-9075 EVK device by attempting
> file system encryption via the fscrypt tool.
>
> Merge Strategy:
> All patches in this series except one touch Qualcomm firmware and soc drivers.
> Hence, it should go via the QCOM-SoC tree. Request the TEE subsystem maintainers
> to provide an Acknowledgement for this.
>
> [1] https://ldts.github.io/qcom-buildroot/index.html
> [2] https://github.com/OP-TEE/optee_os/pull/7906 (In review)
> [3] https://github.com/OP-TEE/optee_os/pull/8137 (Draft, depends on PR#7906)
> [4] https://lore.kernel.org/all/20260702115835.167602-1-sumit.garg@kernel.org/
>
> Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> ---
> Changes in v2:
> - Merged the furnctionality of the middleware ICE service into the ICE
> driver as requested by Eric.
> - Introduced tee_platform_device_register() to fix the issue of indefinite
> probe defer in the ICE driver when ICE PTA is not enabled in OPTEE as
> pointed out by Eric.
> - Fixed type "Psuedo" in ICE TEE back-end commit.
> - Fixed space based indentation with tab in ICE back-end Kconfig.
> - Ensure tee_shm_get_va() failure is checked and ERR_PTR() returned.
> - Validate out_shm size everywhere in qcom_ice_tee.c
> - Zero the memory holding the key when TEE invoke call fails.
> - Check failure from qcom_ice_tz_ops_register() during probe.
> - Cross-check out_size and lt_key_size in qcom_ice_tee_generate_key().
> - Always return 0 when qcom_ice_tee_probe() is success.
> - Did not collect tags from Abel since significant rework undertaken.
> - Link to v1: https://lore.kernel.org/r/20261021-ice-migrate-tee-svc-v1-0-b035be7aab0a@oss.qualcomm.com
>
> ---
> Harshal Dev (6):
> soc: qcom: ice: Add TZ agnostic re-direction functions
> firmware: qcom_scm: Transition to ICE TZ agnostic operations
> firmware: qcom: Add a TEE bus based ICE back-end
> tee: optee: Register TEE devices on the platform bus
> soc: qcom: ice: Migrate to the TZ agnostic ICE APIs
> firmware: qcom: scm: Drop exported ICE wrapper functions
>
> MAINTAINERS | 8 +
> drivers/firmware/qcom/Kconfig | 9 +
> drivers/firmware/qcom/Makefile | 1 +
> drivers/firmware/qcom/qcom_ice_tee.c | 443 +++++++++++++++++++++++++++++
> drivers/firmware/qcom/qcom_scm.c | 90 +++---
> drivers/soc/qcom/Kconfig | 1 +
> drivers/soc/qcom/ice.c | 173 +++++++++--
> drivers/tee/optee/ffa_abi.c | 8 +
> drivers/tee/optee/smc_abi.c | 8 +
> drivers/tee/tee_core.c | 21 ++
> include/linux/firmware/qcom/qcom_ice_tee.h | 10 +
> include/linux/firmware/qcom/qcom_scm.h | 20 --
> include/linux/tee_core.h | 9 +
> include/soc/qcom/ice.h | 56 ++++
> 14 files changed, 784 insertions(+), 73 deletions(-)
> ---
> base-commit: 8f7df005a7564bd7ffa75da9c3085d5b996f1e16
> change-id: 20261021-ice-migrate-tee-svc-a5cd6959ae36
>
> Best regards,
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 4/6] tee: optee: Register TEE devices on the platform bus
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
0 siblings, 0 replies; 9+ messages in thread
From: Harshal Dev @ 2026-11-11 14:57 UTC (permalink / raw)
To: Eric Biggers, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Kuldeep Singh, Neeraj Soni, Manivannan Sadhasivam, Sumit Garg,
Selvam Sathappan Periakaruppan, Jens Wiklander
Cc: Basant Kumar, linux-kernel, linux-arm-msm, linux-crypto, op-tee
+ op-tee@lists.trustedfirmware.org, Jens Wiklander <jens.wiklander@oss.qualcomm.com>
On 11-11-2026 07:47 pm, Harshal Dev wrote:
> The Qualcomm Inline Crypto Engine (ICE) driver is an independent platform
> driver which polls on availability of a TEE interface (SCM driver) before
> checking if the ICE secure service is supported by QTEE by querying the
> qcom_scm_ice_available() API.
>
> The TEE subsystem currently does not provide any way to notify regarding
> the availability of a TEE interface, i.e. whether QCOMTEE or OPTEE have
> probed. And so when migrating to the TEE subsystem via an ICE TEE client
> driver, ICE needs to defer probe until the OPTEE ICE Pseudo TA becomes
> available on the TEE bus. This means that for OPTEE builds without the
> ICE PTA, the ICE driver would indefintely defer probe and the dependent
> UFS/SDHC storage driver will fail to come online.
>
> Add support for optionally registering the back-end TEE devices which
> represent a particular TEE (say OPTEE) on the platform bus via the
> tee_platform_device_register() API. This allows drivers which poll on
> TEE secure interface availability to be notified even if the TEE service
> itself is not supported.
>
> Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> ---
> MAINTAINERS | 1 +
> drivers/firmware/qcom/qcom_ice_tee.c | 41 ++++++++++++++++++++++++++++++
> drivers/soc/qcom/Kconfig | 1 +
> drivers/soc/qcom/ice.c | 9 ++++++-
> drivers/tee/optee/ffa_abi.c | 8 ++++++
> drivers/tee/optee/smc_abi.c | 8 ++++++
> drivers/tee/tee_core.c | 21 +++++++++++++++
> include/linux/firmware/qcom/qcom_ice_tee.h | 10 ++++++++
> include/linux/tee_core.h | 9 +++++++
> 9 files changed, 107 insertions(+), 1 deletion(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1132e456488b..06dba76996b8 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23013,6 +23013,7 @@ L: linux-arm-msm@vger.kernel.org
> L: linux-crypto@vger.kernel.org
> S: Maintained
> F: drivers/firmware/qcom/qcom_ice_tee.c
> +F: include/linux/firmware/qcom/qcom_ice_tee.h
>
> QUALCOMM INTERCONNECT BWMON DRIVER
> M: Krzysztof Kozlowski <krzk@kernel.org>
> diff --git a/drivers/firmware/qcom/qcom_ice_tee.c b/drivers/firmware/qcom/qcom_ice_tee.c
> index 694aa05847f0..39f7094439d9 100644
> --- a/drivers/firmware/qcom/qcom_ice_tee.c
> +++ b/drivers/firmware/qcom/qcom_ice_tee.c
> @@ -5,6 +5,7 @@
>
> #include <linux/kernel.h>
> #include <linux/module.h>
> +#include <linux/platform_device.h>
> #include <linux/slab.h>
> #include <linux/tee_drv.h>
> #include <linux/uuid.h>
> @@ -22,6 +23,9 @@
>
> #define TEE_NUM_PARAMS 4
>
> +/* This represents the back-end OPTEE device */
> +static struct device *__teedev;
> +
> struct qcom_ice_tee_private {
> struct device *dev;
> struct tee_context *ctx;
> @@ -281,6 +285,18 @@ static int qcom_ice_tee_import_key(struct device *dev,
> lt_key, lt_key_size);
> }
>
> +bool qcom_ice_tee_is_available(void)
> +{
> + /*
> + * Paired with smp_load_acquire() in qcom_ice_plat_probe().
> + *
> + * This marks the availability of the OPTEE interface informing the
> + * polling ICE driver that the platform supports a TEE.
> + */
> + return !!smp_load_acquire(&__teedev);
> +}
> +EXPORT_SYMBOL_GPL(qcom_ice_tee_is_available);
> +
> static bool qcom_ice_tee_has_wrapped_key_support(struct device *dev)
> {
> struct qcom_ice_tee_private *data = dev_get_drvdata(dev);
> @@ -398,5 +414,30 @@ static struct tee_client_driver qcom_ice_tee_driver = {
>
> module_tee_client_driver(qcom_ice_tee_driver);
>
> +static int qcom_ice_plat_probe(struct platform_device *pdev)
> +{
> + /* Paired with smp_load_acquire() in qcom_ice_tee_is_available() */
> + smp_store_release(&__teedev, &pdev->dev);
> + return 0;
> +}
> +
> +static const struct platform_device_id qcom_ice_plat_id_table[] = {
> + {
> + .name = "optee",
> + },
> + { }
> +};
> +MODULE_DEVICE_TABLE(platform, qcom_ice_plat_id_table);
> +
> +static struct platform_driver qcom_ice_plat_driver = {
> + .probe = qcom_ice_plat_probe,
> + .id_table = qcom_ice_plat_id_table,
> + .driver = {
> + .name = "qcom-ice-plat",
> + },
> +};
> +
> +module_platform_driver(qcom_ice_plat_driver);
> +
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("Qualcomm ICE TEE driver");
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 535c8619197b..8dc75c252f52 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -296,6 +296,7 @@ endif
> config QCOM_INLINE_CRYPTO_ENGINE
> tristate
> select QCOM_SCM
> + select QCOM_ICE_TEE
>
> config QCOM_KRYO_L2_ACCESSORS
> bool
> diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
> index 452896b1858f..4844621b7497 100644
> --- a/drivers/soc/qcom/ice.c
> +++ b/drivers/soc/qcom/ice.c
> @@ -18,6 +18,7 @@
> #include <linux/platform_device.h>
> #include <linux/xarray.h>
>
> +#include <linux/firmware/qcom/qcom_ice_tee.h>
> #include <linux/firmware/qcom/qcom_scm.h>
>
> #include <soc/qcom/ice.h>
> @@ -132,7 +133,13 @@ static bool qcom_ice_tz_ops_registered(void)
>
> static bool qcom_ice_tz_is_available(void)
> {
> - return qcom_scm_is_available();
> + /*
> + * Both back-ends should be available before we check for
> + * qcom_ice_tz_ops_registered(), otherwise we could pre-maturely
> + * assume that TZ does not provide any ICE secure interface.
> + */
> + return (qcom_scm_is_available() & qcom_ice_tee_is_available());
> +
> }
>
> static int qcom_ice_tz_invalidate_key(u32 index)
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index 702da7d98fbc..2c0065f3d9e4 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -1177,6 +1177,14 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (rc)
> goto err_unregister_devices;
>
> + /*
> + * Device should only be visible on the platform bus once the
> + * child devices have already been enumerated on the TEE bus.
> + */
> + rc = tee_platform_device_register(optee->teedev, "optee");
> + if (rc)
> + goto err_unregister_devices;
> +
> INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
> optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
> blocking_notifier_chain_register(&optee_rpmb_intf_added,
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 51624443359b..85ee76173982 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1920,6 +1920,14 @@ static int optee_probe(struct platform_device *pdev)
> if (rc)
> goto err_disable_shm_cache;
>
> + /*
> + * Device should only be visible on the platform bus once the
> + * child devices have already been enumerated on the TEE bus.
> + */
> + rc = tee_platform_device_register(optee->teedev, "optee");
> + if (rc)
> + goto err_disable_shm_cache;
> +
> INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
> optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
> blocking_notifier_chain_register(&optee_rpmb_intf_added,
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index 1aac50c7c1de..0d3c266b29c7 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -10,6 +10,7 @@
> #include <linux/fs.h>
> #include <linux/idr.h>
> #include <linux/module.h>
> +#include <linux/platform_device.h>
> #include <linux/overflow.h>
> #include <linux/slab.h>
> #include <linux/tee_core.h>
> @@ -1225,6 +1226,26 @@ int tee_device_register(struct tee_device *teedev)
> }
> EXPORT_SYMBOL_GPL(tee_device_register);
>
> +static void plat_tee_free(void *data)
> +{
> + struct platform_device *plat_dev = data;
> +
> + platform_device_unregister(plat_dev);
> +}
> +
> +int tee_platform_device_register(struct tee_device *teedev, const char *teename)
> +{
> + struct platform_device *plat_dev;
> +
> + plat_dev = platform_device_register_data(&teedev->dev, teename,
> + PLATFORM_DEVID_NONE, NULL, 0);
> + if (IS_ERR(plat_dev))
> + return PTR_ERR(plat_dev);
> +
> + devm_add_action_or_reset(&teedev->dev, plat_tee_free, plat_dev);
> + return 0;
> +}
> +
> void tee_device_put(struct tee_device *teedev)
> {
> mutex_lock(&teedev->mutex);
> diff --git a/include/linux/firmware/qcom/qcom_ice_tee.h b/include/linux/firmware/qcom/qcom_ice_tee.h
> new file mode 100644
> index 000000000000..a5906c5d4e39
> --- /dev/null
> +++ b/include/linux/firmware/qcom/qcom_ice_tee.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +#ifndef __QCOM_ICE_TEE
> +#define __QCOM_ICE_TEE
> +
> +bool qcom_ice_tee_is_available(void);
> +
> +#endif /* __QCOM_ICE_TEE */
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index f993d5118edd..d5310b404c1e 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -205,6 +205,15 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
> */
> int tee_device_register(struct tee_device *teedev);
>
> +/**
> + * tee_platform_device_register() - Registers a TEE platform device
> + * @teedev: Device to register
> + * @teename: Name of the TEE the device represents
> + *
> + * @returns: < 0 on failure
> + */
> +int tee_platform_device_register(struct tee_device *teedev, const char *teename);
> +
> /**
> * tee_device_unregister() - Removes a TEE device
> * @teedev: Device to unregister
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-11 14:57 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v2 3/6] firmware: qcom: Add a TEE bus based ICE back-end Harshal Dev
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
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®