From 51487deb90f57af69e9067397440d55b98a81e56 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Tue, 25 Aug 2026 10:40:04 +1000 Subject: [PATCH 01/13] drivers: crypto: qcom: move HWKM driver to its own directory Move the HWKM implementation, HUK support, transaction layer, and private headers under qcom/hwkm. Keep qcom as the umbrella for independent Qualcomm crypto IP blocks. No functional change. Signed-off-by: Amirreza Zarrabi --- .../crypto/qcom/{hwkm_huk.c => hwkm/huk.c} | 0 core/drivers/crypto/qcom/{ => hwkm}/hwkm.c | 0 .../crypto/qcom/{ => hwkm}/include/hwkm.h | 0 .../qcom/{ => hwkm}/include/hwkm_errno.h | 0 .../qcom/{ => hwkm}/include/hwkm_regs.h | 0 core/drivers/crypto/qcom/hwkm/sub.mk | 19 +++++++++++++++++++ .../transaction.c} | 0 core/drivers/crypto/qcom/sub.mk | 14 +------------- 8 files changed, 20 insertions(+), 13 deletions(-) rename core/drivers/crypto/qcom/{hwkm_huk.c => hwkm/huk.c} (100%) rename core/drivers/crypto/qcom/{ => hwkm}/hwkm.c (100%) rename core/drivers/crypto/qcom/{ => hwkm}/include/hwkm.h (100%) rename core/drivers/crypto/qcom/{ => hwkm}/include/hwkm_errno.h (100%) rename core/drivers/crypto/qcom/{ => hwkm}/include/hwkm_regs.h (100%) create mode 100644 core/drivers/crypto/qcom/hwkm/sub.mk rename core/drivers/crypto/qcom/{hwkm_transaction.c => hwkm/transaction.c} (100%) diff --git a/core/drivers/crypto/qcom/hwkm_huk.c b/core/drivers/crypto/qcom/hwkm/huk.c similarity index 100% rename from core/drivers/crypto/qcom/hwkm_huk.c rename to core/drivers/crypto/qcom/hwkm/huk.c diff --git a/core/drivers/crypto/qcom/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c similarity index 100% rename from core/drivers/crypto/qcom/hwkm.c rename to core/drivers/crypto/qcom/hwkm/hwkm.c diff --git a/core/drivers/crypto/qcom/include/hwkm.h b/core/drivers/crypto/qcom/hwkm/include/hwkm.h similarity index 100% rename from core/drivers/crypto/qcom/include/hwkm.h rename to core/drivers/crypto/qcom/hwkm/include/hwkm.h diff --git a/core/drivers/crypto/qcom/include/hwkm_errno.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_errno.h similarity index 100% rename from core/drivers/crypto/qcom/include/hwkm_errno.h rename to core/drivers/crypto/qcom/hwkm/include/hwkm_errno.h diff --git a/core/drivers/crypto/qcom/include/hwkm_regs.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h similarity index 100% rename from core/drivers/crypto/qcom/include/hwkm_regs.h rename to core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h diff --git a/core/drivers/crypto/qcom/hwkm/sub.mk b/core/drivers/crypto/qcom/hwkm/sub.mk new file mode 100644 index 000000000..401c06eda --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm/sub.mk @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) 2026, Qualcomm Technologies, Inc. + +incdirs-y += include + +srcs-y += hwkm.c +srcs-y += huk.c +srcs-y += transaction.c + +# Bitmap of fuse regions whose SHA256 digest is folded into the HUK KDF input. +# Each bit corresponds to a fuse region index; set a bit to bind the HUK to +# that region's content. Defaults to 0 (no fuse regions included). +CFG_HWKM_HUK_FUSE_REGION_DIGEST ?= 0x0 + +# Mix TZ_SKDK_L2 into the HUK KDF via the MKS field. When enabled, an SKDK L3 +# key is derived from TZ_SKDK_L2 and passed as the mixing key for the UKDK L3 +# and L4 derivation steps, binding the HUK to the SKDK lineage in addition to +# the UKDK. Defaults to y. +CFG_HWKM_HUK_MIX_SKDK ?= y diff --git a/core/drivers/crypto/qcom/hwkm_transaction.c b/core/drivers/crypto/qcom/hwkm/transaction.c similarity index 100% rename from core/drivers/crypto/qcom/hwkm_transaction.c rename to core/drivers/crypto/qcom/hwkm/transaction.c diff --git a/core/drivers/crypto/qcom/sub.mk b/core/drivers/crypto/qcom/sub.mk index 741e77845..10905c1fc 100644 --- a/core/drivers/crypto/qcom/sub.mk +++ b/core/drivers/crypto/qcom/sub.mk @@ -1,16 +1,4 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2026, Qualcomm Technologies, Inc. -incdirs-y += include -srcs-y += hwkm.c hwkm_huk.c hwkm_transaction.c - -# Bitmap of fuse regions whose SHA256 digest is folded into the HUK KDF input. -# Each bit corresponds to a fuse region index; set a bit to bind the HUK to -# that region's content. Defaults to 0 (no fuse regions included). -CFG_HWKM_HUK_FUSE_REGION_DIGEST ?= 0x0 - -# Mix TZ_SKDK_L2 into the HUK KDF via the MKS field. When enabled, an SKDK L3 -# key is derived from TZ_SKDK_L2 and passed as the mixing key for the UKDK L3 -# and L4 derivation steps, binding the HUK to the SKDK lineage in addition to -# the UKDK. Defaults to y. -CFG_HWKM_HUK_MIX_SKDK ?= y +subdirs-$(CFG_QCOM_HWKM) += hwkm From 31c089ef983a6d4ae00dfaa720479cdef392bcb6 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Tue, 25 Aug 2026 10:40:54 +1000 Subject: [PATCH 02/13] drivers: crypto: qcom: hwkm: refactor register map for multiple instances Change all register offsets in hwkm_regs.h to group-relative and add HWKM_MASTER_*_REGS_OFFSET and HWKM_CRYPTO0_*_REGS_OFFSET constants to locate each register group within its instance's MMIO window. Callers add the appropriate offset at the call site, making the same register definitions reusable across the master and any slave. Also extract run_fifo_transaction() from master_run_transaction() so the FIFO protocol can be reused by any slave. Signed-off-by: Amirreza Zarrabi --- core/drivers/crypto/qcom/hwkm/hwkm.c | 24 ++++--- .../crypto/qcom/hwkm/include/hwkm_regs.h | 54 ++++++++-------- core/drivers/crypto/qcom/hwkm/transaction.c | 64 +++++++++++-------- 3 files changed, 78 insertions(+), 64 deletions(-) diff --git a/core/drivers/crypto/qcom/hwkm/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c index 6d3d7e01c..ac7788d88 100644 --- a/core/drivers/crypto/qcom/hwkm/hwkm.c +++ b/core/drivers/crypto/qcom/hwkm/hwkm.c @@ -208,7 +208,8 @@ static TEE_Result hwkm_init(void) return TEE_ERROR_GENERIC; /* Check the hardware self-test status. */ - status = io_read32_off(base, HWKM_TZ_KM_STATUS); + status = io_read32_off(base + HWKM_MASTER_TZ_REGS_OFFSET, + HWKM_TZ_KM_STATUS); if (status & (HWKM_TZ_KM_STATUS_BIST_ERROR | HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR)) { EMSG("hwkm: BIST failed, status=0x%08"PRIx32, status); @@ -216,15 +217,19 @@ static TEE_Result hwkm_init(void) } /* Disable CRC checking on command packets. */ - io_write32_off_field(base, HWKM_TZ_KM_CTL, + io_write32_off_field(base + HWKM_MASTER_TZ_REGS_OFFSET, HWKM_TZ_KM_CTL, HWKM_TZ_KM_CTL_CRC_CHECK_EN, 0); - io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_0, - HWKM_BANK0_BBAC_0); - io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_1, 0); - io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_2, 0); - io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_3, 0); - io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_4, 0); + io_write32_off(base + HWKM_MASTER_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_0, HWKM_BANK0_BBAC_0); + io_write32_off(base + HWKM_MASTER_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_1, 0); + io_write32_off(base + HWKM_MASTER_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_2, 0); + io_write32_off(base + HWKM_MASTER_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_3, 0); + io_write32_off(base + HWKM_MASTER_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_4, 0); /* * Clear the spurious RSP_FIFO_FULL sticky bit. @@ -233,7 +238,8 @@ static TEE_Result hwkm_init(void) * it does not interfere with CMD_DONE polling in * master_run_transaction(). */ - io_write32_off(base, HWKM_BANK0_KM_IRQ_STATUS, + io_write32_off(base + HWKM_MASTER_BANK0_REGS_OFFSET, + HWKM_BANK0_KM_IRQ_STATUS, HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL); hwkm_ctx.base = base; diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h index 297da7786..de23134ce 100644 --- a/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h @@ -10,27 +10,37 @@ #include /* BIT() and GENMASK_32(). */ -/* SHARED_IPCAT_VERSION (HWKM_REGS_BASE + 0x0000U). */ +/* Master module's base offsets. */ +#define HWKM_MASTER_SHARED_REGS_OFFSET 0x0000U +#define HWKM_MASTER_TZ_REGS_OFFSET 0x1000U +#define HWKM_MASTER_BANK0_REGS_OFFSET 0x2000U +#define HWKM_MASTER_BANK0_AC_REGS_OFFSET 0x6000U +#define HWKM_MASTER_BANK1_AC_REGS_OFFSET 0x7000U +#define HWKM_MASTER_BANK2_AC_REGS_OFFSET 0x8000U +#define HWKM_MASTER_BANK3_AC_REGS_OFFSET 0x9000U + +/* CRYPTO0 module's base offsets. */ +#define HWKM_CRYPTO0_SHARED_REGS_OFFSET 0x30000U +#define HWKM_CRYPTO0_TZ_REGS_OFFSET 0x31000U +#define HWKM_CRYPTO0_BANK0_REGS_OFFSET 0x32000U +#define HWKM_CRYPTO0_BANK0_AC_REGS_OFFSET 0x35000U +#define HWKM_CRYPTO0_BANK1_AC_REGS_OFFSET 0x36000U +#define HWKM_CRYPTO0_BANK2_AC_REGS_OFFSET 0x37000U + /* IP Catalog version: [31:24] major, [23:16] minor, [15:0] step. */ #define HWKM_SHARED_IPCAT_VERSION 0x0000U - -/* SHARED_KEY_POLICY_VERSION (HWKM_REGS_BASE + 0x0004U). */ /* Key-policy format version supported by this instance. */ #define HWKM_SHARED_KEY_POLICY_VERSION 0x0004U - -/* SHARED_KEYTABLE_SIZE (HWKM_REGS_BASE + 0x000cU). */ /* Number of key slots in this instance's key table. */ #define HWKM_SHARED_KEYTABLE_SIZE 0x000cU -/* TZ_KM_CTL (HWKM_REGS_BASE + 0x1000U). */ -#define HWKM_TZ_KM_CTL 0x1000U +#define HWKM_TZ_KM_CTL 0x0000U /* Bit 0 - enable CRC validation on every command packet. */ #define HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT 0U #define HWKM_TZ_KM_CTL_CRC_CHECK_EN \ BIT(HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT) -/* TZ_KM_STATUS (HWKM_REGS_BASE + 0x1004U). */ -#define HWKM_TZ_KM_STATUS 0x1004U +#define HWKM_TZ_KM_STATUS 0x0004U /* * Hardware BIST detected a fault in the key table or crypto logic. * If set, the instance must not be used; the driver treats this as @@ -47,8 +57,7 @@ #define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR \ BIT(HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR_SHIFT) -/* TPKEY_RECEIVE_CTL (HWKM_REGS_BASE + 0x101cU). */ -#define HWKM_TZ_TPKEY_RECEIVE_CTL 0x101cU +#define HWKM_TZ_TPKEY_RECEIVE_CTL 0x001cU /* Arm (1) or disarm (0) the slave for TPKEY reception. */ #define HWKM_TZ_TPKEY_RECEIVE_CTL_EN_SHIFT 0x8U #define HWKM_TZ_TPKEY_RECEIVE_CTL_EN \ @@ -59,8 +68,7 @@ */ #define HWKM_TZ_TPKEY_RECEIVE_CTL_TPKEY_DKS GENMASK_32(7, 0) -/* TZ_TPKEY_RECEIVE_STATUS (HWKM_REGS_BASE + 0x1020U). */ -#define HWKM_TZ_TPKEY_RECEIVE_STATUS 0x1020U +#define HWKM_TZ_TPKEY_RECEIVE_STATUS 0x0020U /* * Set by hardware when the TPKEY has been written into TPKEY_DKS. * Poll this after SET_TPKEY, before disarming the slave. @@ -71,8 +79,7 @@ /* Slot index where the TPKEY was stored (readback of TPKEY_DKS) [7:0]. */ #define HWKM_TZ_TPKEY_RECEIVE_STATUS_TPKEY_DKS GENMASK_32(7, 0) -/* BANK0_KM_CTL (HWKM_REGS_BASE + 0x2000U). */ -#define HWKM_BANK0_KM_CTL 0x2000U +#define HWKM_BANK0_KM_CTL 0x0000U /* * Enable the command FIFO for a new packet. Write 1 after clearing the * FIFO and ESR, before writing the first command word. @@ -88,8 +95,7 @@ #define HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR \ BIT(HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT) -/* BANK0_KM_STATUS (HWKM_REGS_BASE + 0x2004U). */ -#define HWKM_BANK0_KM_STATUS 0x2004U +#define HWKM_BANK0_KM_STATUS 0x0004U /* * Words of data available in the response FIFO [13:9]. * Poll > 0 before reading each response word. @@ -101,8 +107,7 @@ */ #define HWKM_BANK0_KM_STATUS_CMD_AVAIL_SPACE GENMASK_32(18, 14) -/* KM_IRQ_STATUS (HWKM_REGS_BASE + 0x2008U). */ -#define HWKM_BANK0_KM_IRQ_STATUS 0x2008U +#define HWKM_BANK0_KM_IRQ_STATUS 0x0008U /* * Set by hardware when the full command has been processed and the complete * response is in the RSP FIFO. If 0 after reading all expected response @@ -129,29 +134,24 @@ * io_read32_off(HWKM_REGS_BASE, HWKM_BANK0_KM_ESR)) * Do this at the start of every transaction to acknowledge stale errors. */ -#define HWKM_BANK0_KM_ESR 0x2010U +#define HWKM_BANK0_KM_ESR 0x0010U /* * BANK0_KM_CMD_FIFO - Command FIFO write port (HWKM_REGS_BASE + 0x201cU). * * Write one 32-bit command word per store after polling CMD_FIFO_AVAIL_SPACE. */ -#define HWKM_BANK0_KM_CMD_FIFO 0x201cU +#define HWKM_BANK0_KM_CMD_FIFO 0x001cU /* * BANK0_KM_RSP_FIFO - Response FIFO read port (HWKM_REGS_BASE + 0x205cU). * * Read one 32-bit response word per load after polling RSP_FIFO_AVAIL_DATA. */ -#define HWKM_BANK0_KM_RSP_FIFO 0x205cU +#define HWKM_BANK0_KM_RSP_FIFO 0x005cU /* Bank-Based Access Control (BBAC) bitmaps. */ -#define HWKM_BANK0_AC 0x6000U -#define HWKM_BANK1_AC 0x7000U -#define HWKM_BANK2_AC 0x8000U -#define HWKM_BANK3_AC 0x9000U - #define HWKM_BANKn_AC_BBAC_0 0x0000U /* slots 0-31. */ #define HWKM_BANKn_AC_BBAC_1 0x0004U /* slots 32-63. */ #define HWKM_BANKn_AC_BBAC_2 0x0008U /* slots 64-95. */ diff --git a/core/drivers/crypto/qcom/hwkm/transaction.c b/core/drivers/crypto/qcom/hwkm/transaction.c index 6155630dd..8731c08ce 100644 --- a/core/drivers/crypto/qcom/hwkm/transaction.c +++ b/core/drivers/crypto/qcom/hwkm/transaction.c @@ -396,86 +396,94 @@ static int hwkm_fifo_wait(vaddr_t base, uint32_t reg, uint32_t mask) } /* - * master_run_transaction() - Submit one command packet to the master HWKM. - * @cmd: Command packet words to write into the command FIFO. + * run_fifo_transaction() - Submit one command packet via a BANK0 client FIFO. + * @bank0_base: Virtual address of the BANK0 client resource group + * (i.e. instance_base + HWKM_{MASTER,CRYPTO0}_BANK0_REGS_OFFSET). + * @cmd: Command packet words. * @cmd_words: Number of 32-bit words in @cmd. - * @rsp: Response buffer filled from the response FIFO. + * @rsp: Response buffer. * @rsp_words: Number of 32-bit words expected in @rsp. * * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. */ -static int master_run_transaction(const uint32_t *cmd, size_t cmd_words, - uint32_t *rsp, size_t rsp_words) +static int run_fifo_transaction(vaddr_t bank0_base, + const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) { - struct hwkm_drv_ctx *ctx = NULL; int rc = HWKM_ERR_GENERIC; - vaddr_t base = 0; size_t i = 0; - if (!cmd || !cmd_words || !rsp || !rsp_words) - return HWKM_ERR_INVALID_ARG; - - ctx = hwkm_get_context(); - if (!ctx) - return HWKM_ERR_INVALID_ARG; - - base = ctx->base; - /* Flush any stale command FIFO contents. */ - io_write32_off_field(base, HWKM_BANK0_KM_CTL, + io_write32_off_field(bank0_base, HWKM_BANK0_KM_CTL, HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, 1); - io_write32_off_field(base, HWKM_BANK0_KM_CTL, + io_write32_off_field(bank0_base, HWKM_BANK0_KM_CTL, HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, 0); /* Clear stale error state from the previous transaction. */ - io_write32_off(base, HWKM_BANK0_KM_ESR, - io_read32_off(base, HWKM_BANK0_KM_ESR)); + io_write32_off(bank0_base, HWKM_BANK0_KM_ESR, + io_read32_off(bank0_base, HWKM_BANK0_KM_ESR)); /* Enable command processing. */ - io_write32_off_field(base, HWKM_BANK0_KM_CTL, + io_write32_off_field(bank0_base, HWKM_BANK0_KM_CTL, HWKM_BANK0_KM_CTL_CMD_ENABLE, 1); /* Confirm the FIFO clear bit has deasserted. */ - if (io_read32_off_field(base, HWKM_BANK0_KM_CTL, + if (io_read32_off_field(bank0_base, HWKM_BANK0_KM_CTL, HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR)) return HWKM_ERR_FIFO_NOT_EMPTY; /* Push the command packet one word at a time. */ for (i = 0; i < cmd_words; i++) { - rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + rc = hwkm_fifo_wait(bank0_base, HWKM_BANK0_KM_STATUS, HWKM_BANK0_KM_STATUS_CMD_AVAIL_SPACE); if (rc) return rc; - io_write32_off(base, HWKM_BANK0_KM_CMD_FIFO, cmd[i]); + io_write32_off(bank0_base, HWKM_BANK0_KM_CMD_FIFO, cmd[i]); } /* Pull the response packet one word at a time. */ for (i = 0; i < rsp_words; i++) { - rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + rc = hwkm_fifo_wait(bank0_base, HWKM_BANK0_KM_STATUS, HWKM_BANK0_KM_STATUS_RSP_AVAIL_DATA); if (rc) return rc; - rsp[i] = io_read32_off(base, HWKM_BANK0_KM_RSP_FIFO); + rsp[i] = io_read32_off(bank0_base, HWKM_BANK0_KM_RSP_FIFO); } /* The hardware must report completion after the response is read. */ - if (!io_read32_off_field(base, HWKM_BANK0_KM_IRQ_STATUS, + if (!io_read32_off_field(bank0_base, HWKM_BANK0_KM_IRQ_STATUS, HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE)) return HWKM_ERR_RSP_OVERFLOW; /* Acknowledge completion. */ - io_write32_off(base, HWKM_BANK0_KM_IRQ_STATUS, + io_write32_off(bank0_base, HWKM_BANK0_KM_IRQ_STATUS, HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE); return HWKM_SUCCESS; } +static int master_run_transaction(const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + struct hwkm_drv_ctx *ctx = NULL; + + ctx = hwkm_get_context(); + if (!ctx) + return HWKM_ERR_INVALID_ARG; + + return run_fifo_transaction(ctx->base + HWKM_MASTER_BANK0_REGS_OFFSET, + cmd, cmd_words, rsp, rsp_words); +} + static int run_transaction(const struct hwkm_transaction *t, const uint32_t *cmd, size_t cmd_words, uint32_t *rsp, size_t rsp_words) { + if (!cmd || !cmd_words || !rsp || !rsp_words) + return HWKM_ERR_INVALID_ARG; + switch (t->hdl->dest) { case HWKM_KEY_DEST_KM_MASTER: return master_run_transaction(cmd, cmd_words, rsp, rsp_words); From 5a6f2f8ecdc72cec768bbd5a63813c09c5de8f34 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Tue, 25 Aug 2026 10:41:20 +1000 Subject: [PATCH 03/13] drivers: crypto: qcom: hwkm: add CRYPTO0 key-manager slave support Enable the CRYPTO0 general-purpose crypto engine (GPCE) key-manager slave so that keys can be provisioned into CRYPTO0 key slots using the existing HWKM transaction protocol. Map the CRYPTO0 MMIO window, configure the key-manager slave at boot, and extend the transaction layer to dispatch to the GPCE slave alongside the existing HWKM master. Signed-off-by: Amirreza Zarrabi --- core/arch/arm/plat-qcom/hoya/arch_config.h | 3 ++ core/drivers/crypto/qcom/hwkm/hwkm.c | 50 ++++++++++++++++++++ core/drivers/crypto/qcom/hwkm/include/hwkm.h | 4 +- core/drivers/crypto/qcom/hwkm/transaction.c | 17 +++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index 11fc5ab44..ffb39e9fb 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -28,6 +28,9 @@ #define HWKM_MASTER_BASE UL(0x010c0000) #define HWKM_MASTER_SIZE UL(0x00020000) +#define HWKM_CRYPTO0_BASE UL(0x01dc0000) +#define HWKM_CRYPTO0_SIZE UL(0x00040000) + #define IMEM_DIAG_OFFSET UL(0x720) #define DIAG_SIZE UL(0x3000) #define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) diff --git a/core/drivers/crypto/qcom/hwkm/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c index ac7788d88..34b2d6253 100644 --- a/core/drivers/crypto/qcom/hwkm/hwkm.c +++ b/core/drivers/crypto/qcom/hwkm/hwkm.c @@ -21,6 +21,7 @@ #include register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_MASTER_BASE, HWKM_MASTER_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_CRYPTO0_BASE, HWKM_CRYPTO0_SIZE); static_assert(HW_UNIQUE_KEY_LENGTH <= HWKM_MAX_KEY_SIZE); @@ -248,6 +249,49 @@ static TEE_Result hwkm_init(void) return TEE_SUCCESS; } +static TEE_Result gpce_init(void) +{ + uint32_t status = 0; + vaddr_t base = 0; + + base = (vaddr_t)phys_to_virt(HWKM_CRYPTO0_BASE, MEM_AREA_IO_SEC, + HWKM_CRYPTO0_SIZE); + if (!base) + return TEE_ERROR_GENERIC; + + /* Check the hardware self-test status. */ + status = io_read32_off(base + HWKM_CRYPTO0_TZ_REGS_OFFSET, + HWKM_TZ_KM_STATUS); + if (status & (HWKM_TZ_KM_STATUS_BIST_ERROR | + HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR)) { + EMSG("hwkm: gpce BIST failed, status=0x%08"PRIx32, status); + return TEE_ERROR_GENERIC; + } + + /* Disable CRC checking on command packets. */ + io_write32_off_field(base + HWKM_CRYPTO0_TZ_REGS_OFFSET, HWKM_TZ_KM_CTL, + HWKM_TZ_KM_CTL_CRC_CHECK_EN, 0); + + /* Grant TZ (BANK0) unrestricted access to all key slots. */ + io_write32_off(base + HWKM_CRYPTO0_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_0, 0xFFFFFFFF); + io_write32_off(base + HWKM_CRYPTO0_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_1, 0xFFFFFFFF); + io_write32_off(base + HWKM_CRYPTO0_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_2, 0xFFFFFFFF); + io_write32_off(base + HWKM_CRYPTO0_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_3, 0xFFFFFFFF); + + /* Clear spurious RSP_FIFO_FULL sticky bit (HW errata QCTDD06252768). */ + io_write32_off(base + HWKM_CRYPTO0_BANK0_REGS_OFFSET, + HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL); + + hwkm_ctx.crypto0_base = base; + + return TEE_SUCCESS; +} + static TEE_Result hwkm_driver_init(void) { struct tee_hw_unique_key huk = { }; @@ -259,6 +303,12 @@ static TEE_Result hwkm_driver_init(void) return res; } + res = gpce_init(); + if (res) { + EMSG("hwkm: gpce init failed: 0x%08"PRIx32, res); + return res; + } + res = tee_otp_get_hw_unique_key(&huk); if (res) { EMSG("hwkm: HUK derivation failed: 0x%08"PRIx32, res); diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm.h b/core/drivers/crypto/qcom/hwkm/include/hwkm.h index ff4c84be1..76f23c616 100644 --- a/core/drivers/crypto/qcom/hwkm/include/hwkm.h +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm.h @@ -17,6 +17,7 @@ enum hwkm_key_destination { HWKM_KEY_DEST_KM_MASTER = 0, + HWKM_KEY_DEST_GPCE_SLAVE = 1, /* CRYPTO0 CE slave. */ }; enum hwkm_key_security_lvl { @@ -258,7 +259,8 @@ static inline TEE_Result hwkm_to_optee(int rc) struct hwkm_handle; struct hwkm_drv_ctx { - vaddr_t base; + vaddr_t base; /* HWKM master MMIO base. */ + vaddr_t crypto0_base; /* CRYPTO0 top MMIO base. */ struct mutex hwkm_lock; /* Serializes all HWKM operations. */ bool hwkm_huk_ready; uint8_t hwkm_huk[HWKM_MAX_KEY_SIZE]; diff --git a/core/drivers/crypto/qcom/hwkm/transaction.c b/core/drivers/crypto/qcom/hwkm/transaction.c index 8731c08ce..a645e3089 100644 --- a/core/drivers/crypto/qcom/hwkm/transaction.c +++ b/core/drivers/crypto/qcom/hwkm/transaction.c @@ -477,6 +477,20 @@ static int master_run_transaction(const uint32_t *cmd, size_t cmd_words, cmd, cmd_words, rsp, rsp_words); } +static int gpce_run_transaction(const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + struct hwkm_drv_ctx *ctx = NULL; + + ctx = hwkm_get_context(); + if (!ctx || !ctx->crypto0_base) + return HWKM_ERR_INVALID_ARG; + + return run_fifo_transaction(ctx->crypto0_base + + HWKM_CRYPTO0_BANK0_REGS_OFFSET, + cmd, cmd_words, rsp, rsp_words); +} + static int run_transaction(const struct hwkm_transaction *t, const uint32_t *cmd, size_t cmd_words, uint32_t *rsp, size_t rsp_words) @@ -487,6 +501,8 @@ static int run_transaction(const struct hwkm_transaction *t, switch (t->hdl->dest) { case HWKM_KEY_DEST_KM_MASTER: return master_run_transaction(cmd, cmd_words, rsp, rsp_words); + case HWKM_KEY_DEST_GPCE_SLAVE: + return gpce_run_transaction(cmd, cmd_words, rsp, rsp_words); default: return HWKM_ERR_INVALID_DEST; } @@ -773,6 +789,7 @@ int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest) { switch (dest) { case HWKM_KEY_DEST_KM_MASTER: + case HWKM_KEY_DEST_GPCE_SLAVE: break; default: return HWKM_ERR_INVALID_DEST; From 87a366279dd961d7fb60a9a9c35ea154ec315823 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Tue, 28 Jul 2026 18:49:23 +0530 Subject: [PATCH 04/13] drivers: crypto: qcom: hwkm: Generate SWAP and TPKEY Add support for generating the SWAP and TPKEY during driver init. The TPKEY is used to wrap keys before transporting them to the HWMK slaves, such as the General Purpose Crypto Engine (GPCE) and the Inline Crypto Engine (ICE). The SWAP key is used for wrapping and exporting keys from the hardware key manager to software. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/drivers/crypto/qcom/hwkm/hwkm.c | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/core/drivers/crypto/qcom/hwkm/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c index 34b2d6253..25c5b2b65 100644 --- a/core/drivers/crypto/qcom/hwkm/hwkm.c +++ b/core/drivers/crypto/qcom/hwkm/hwkm.c @@ -292,6 +292,139 @@ static TEE_Result gpce_init(void) return TEE_SUCCESS; } +static TEE_Result hwkm_generate_tp_and_swap_keys(void) +{ + const struct hwkm_key_policy base_kdk_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_key_policy tpkey_policy = { + .km_by_tz_allowed = true, + .km_by_nsec_allowed = true, + .km_by_modem_allowed = true, + .km_by_spu_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_SIV, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_TPKEY, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_key_policy swap_key_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_SIV, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_KSK, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_bsve bsve = { + .enabled = true, + .km_swc_en = true, + }; + struct hwkm_transaction t_keygen = { + .cmd = { + .op = HWKM_OP_NIST_KEYGEN, + .keygen = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .policy = base_kdk_policy, + }, + }, + }; + struct hwkm_transaction t_tpkey_kdf = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TPKEY_SLOT, + .kdk = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .policy = tpkey_policy, + .bsve = bsve, + .ctx_len = 16, + .ctx = { + 0x54, 0x52, 0x41, 0x4e, + 0x53, 0x50, 0x4f, 0x52, + 0x54, 0x20, 0x4b, 0x45, + 0x59, 0x00, 0x00, 0x00, + }, + }, + }, + }; + struct hwkm_transaction t_swap_kdf = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TZ_SWAP_KEY_SLOT, + .kdk = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .policy = swap_key_policy, + .bsve = bsve, + .ctx_len = 8, + .ctx = { + 0x53, 0x57, 0x41, 0x50, + 0x20, 0x4b, 0x45, 0x59, + }, + }, + }, + }; + struct hwkm_transaction t_clear = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_double_key = false, + }, + }, + }; + TEE_Result res = TEE_ERROR_GENERIC; + int rc = HWKM_ERR_GENERIC; + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 4, + (struct hwkm_transaction *const[]){ + &t_keygen, &t_tpkey_kdf, + &t_swap_kdf, &t_clear }); + if (rc) + return hwkm_to_optee(rc); + + if (t_keygen.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: initial NIST_KEYGEN failed: %s", + hwkm_err2str(t_keygen.rsp.status)); + res = TEE_ERROR_GENERIC; + goto out; + } + + if (t_tpkey_kdf.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: initial TPKEY SYSTEM_KDF failed: %s", + hwkm_err2str(t_tpkey_kdf.rsp.status)); + res = TEE_ERROR_GENERIC; + goto out; + } + + if (t_swap_kdf.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: initial SWAP SYSTEM_KDF failed: %s", + hwkm_err2str(t_swap_kdf.rsp.status)); + res = TEE_ERROR_GENERIC; + goto out; + } + + if (t_clear.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("hwkm: initial KEY_SLOT_CLEAR failed: %s", + hwkm_err2str(t_clear.rsp.status)); + res = TEE_ERROR_GENERIC; + goto out; + } + + res = TEE_SUCCESS; + +out: + return res; +} + static TEE_Result hwkm_driver_init(void) { struct tee_hw_unique_key huk = { }; @@ -309,6 +442,12 @@ static TEE_Result hwkm_driver_init(void) return res; } + res = hwkm_generate_tp_and_swap_keys(); + if (res) { + EMSG("hwkm: TP and SWAP key generation failed: 0x%08"PRIx32, res); + return res; + } + res = tee_otp_get_hw_unique_key(&huk); if (res) { EMSG("hwkm: HUK derivation failed: 0x%08"PRIx32, res); From 7366479c41b385418e3ce81a8472f0e8ae21ac38 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Tue, 28 Jul 2026 19:51:58 +0530 Subject: [PATCH 05/13] drivers: crypto: qcom: hwkm: Add ICE slave support to keymanager Add support for the Inline Crypto Engine (ICE) slave to the hardware key manager (HWKM). This allows HWKM to issue commands to ICE and provision keys via its existing transaction protocol. Since the clock and power for ICE are controlled by Linux, it must be (re)configured before dispatching any transactions to it. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/arch/arm/plat-qcom/hoya/arch_config.h | 6 + core/arch/arm/plat-qcom/hoya/lemans/target.mk | 2 + .../arm/plat-qcom/hoya/lemans/target_config.h | 9 + core/drivers/crypto/qcom/hwkm/hwkm.c | 1 + core/drivers/crypto/qcom/hwkm/ice.c | 330 ++++++++++++++++++ core/drivers/crypto/qcom/hwkm/include/hwkm.h | 2 + .../crypto/qcom/hwkm/include/hwkm_ice.h | 11 + .../crypto/qcom/hwkm/include/hwkm_regs.h | 42 +++ core/drivers/crypto/qcom/hwkm/sub.mk | 1 + core/drivers/crypto/qcom/hwkm/transaction.c | 34 ++ 10 files changed, 438 insertions(+) create mode 100644 core/drivers/crypto/qcom/hwkm/ice.c create mode 100644 core/drivers/crypto/qcom/hwkm/include/hwkm_ice.h diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index ffb39e9fb..590cce073 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -31,6 +31,12 @@ #define HWKM_CRYPTO0_BASE UL(0x01dc0000) #define HWKM_CRYPTO0_SIZE UL(0x00040000) +#define HWKM_UFS_MEM_ICE_BASE UL(0x01d88000) +#define HWKM_UFS_MEM_ICE_SIZE UL(0x00018000) + +#define HWKM_SDC1_SDCC_ICE_BASE UL(0x087c8000) +#define HWKM_SDC1_SDCC_ICE_SIZE UL(0x00018000) + #define IMEM_DIAG_OFFSET UL(0x720) #define DIAG_SIZE UL(0x3000) #define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 1cf345223..be77ebfab 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -40,3 +40,5 @@ ifeq ($(CFG_QCOM_CLK_CFG),y) $(call force,CFG_QCOM_CMD_DB,y) $(call force,CFG_QCOM_RPMH_CLIENT,y) endif + +CFG_QCOM_STORAGE_UFS ?= y diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h index 757c67c61..0a315e7a0 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -94,4 +94,13 @@ #define TITAN_SS_BASE UL(0x0ac00000) #define TITAN_SS_SIZE UL(0x00200000) + +#if defined(CFG_QCOM_STORAGE_UFS) +#define HWKM_ICE_BASE HWKM_UFS_MEM_ICE_BASE +#define HWKM_ICE_SIZE HWKM_UFS_MEM_ICE_SIZE +#else /* CFG_QCOM_STORAGE_UFS */ +#define HWKM_ICE_BASE HWKM_SDC1_SDCC_ICE_BASE +#define HWKM_ICE_SIZE HWKM_SDC1_SDCC_ICE_SIZE +#endif /* CFG_QCOM_STORAGE_UFS */ + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/crypto/qcom/hwkm/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c index 25c5b2b65..9ae1093e5 100644 --- a/core/drivers/crypto/qcom/hwkm/hwkm.c +++ b/core/drivers/crypto/qcom/hwkm/hwkm.c @@ -22,6 +22,7 @@ register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_MASTER_BASE, HWKM_MASTER_SIZE); register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_CRYPTO0_BASE, HWKM_CRYPTO0_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_ICE_BASE, HWKM_ICE_SIZE); static_assert(HW_UNIQUE_KEY_LENGTH <= HWKM_MAX_KEY_SIZE); diff --git a/core/drivers/crypto/qcom/hwkm/ice.c b/core/drivers/crypto/qcom/hwkm/ice.c new file mode 100644 index 000000000..54b995c37 --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm/ice.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include + +#define HWKM_ICE_MAX_RETRIES 100000U + +/* + * HWKM_ICE_DEFAULT_TPKEY_SLOT - destination key slot used by the ICE slave + * to receive the TPKEY. + */ +#define HWKM_ICE_DEFAULT_TPKEY_SLOT 0x92U + +/* + * ice_set_standard_mode() - Put the ICE HWKM instance into standard mode. + * @base: MMIO base address of the ICE module. + * + * Programs the TZ control register to the standard-mode value expected by + * the ICE init flow before status polling and access-control setup. + */ +static void ice_set_standard_mode(vaddr_t base) +{ + io_write32_off(base + HWKM_ICE_TZ_REGS_OFFSET, HWKM_TZ_KM_CTL, 0x7); +} + +/* + * ice_set_legacy_mode() - Put the ICE HWKM instance into legacy mode. + * @base: MMIO base address of the ICE module. + * + * Programs the TZ control register to the legacy-mode. ICE operates in legacy + * mode out of reset. + */ +static void ice_set_legacy_mode(vaddr_t base) +{ + io_write32_off(base + HWKM_ICE_TZ_REGS_OFFSET, HWKM_TZ_KM_CTL, 0x27); +} + +/* + * ice_wait_init_done() - Poll a status bit until the ICE init step completes. + * @base: MMIO base used for register access. + * @reg: Register offset to poll. + * @mask: Bit mask of the completion field. + * + * Uses bounded retries with udelay(10) to avoid indefinite waits during + * bring-up sequencing. + * + * Return: HWKM_SUCCESS when the field clears, or HWKM_ERR_FIFO_TIMEOUT. + */ +static int ice_wait_init_done(vaddr_t base, uint32_t reg, uint32_t mask) +{ + uint32_t retries = 0; + + /* BIT set to 1 implies INIT done. */ + while (!io_read32_off_field(base, reg, mask)) { + if (++retries > HWKM_ICE_MAX_RETRIES) + return HWKM_ERR_FIFO_TIMEOUT; + udelay(10); + } + + return HWKM_SUCCESS; +} + +/* + * ice_wait_init() - Wait for all ICE bring-up completion status bits. + * @base: MMIO base address of the ICE module. + * + * Waits for BIST, crypto-lib BIST, boot command list 0/1, and key-table clear + * completion indicators before allowing command traffic. + * + * Return: HWKM_SUCCESS on success, or HWKM_ERR_FIFO_TIMEOUT. + */ +static int ice_wait_init(vaddr_t base) +{ + uint32_t done_bits[] = { + HWKM_TZ_KM_STATUS_BIST_DONE, + HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_DONE, + HWKM_TZ_KM_STATUS_BOOT_CMD_LIST0_DONE, + HWKM_TZ_KM_STATUS_BOOT_CMD_LIST1_DONE, + HWKM_TZ_KM_STATUS_KT_CLEAR_DONE, + }; + size_t i = 0; + int rc = HWKM_SUCCESS; + + for (i = 0; i < ARRAY_SIZE(done_bits); i++) { + rc = ice_wait_init_done(base + HWKM_ICE_TZ_REGS_OFFSET, + HWKM_TZ_KM_STATUS, done_bits[i]); + if (rc) + return rc; + } + + return rc; +} + +/* + * ice_setup_finalize() - Finalize ICE HWKM register state after init done. + * @base: MMIO base address of the ICE module. + * + * Disables CRC checking, grants TZ BANK0 key-slot access, and clears the + * spurious RSP_FIFO_FULL sticky interrupt condition (QCTDD06252768). + */ +static void ice_setup_finalize(vaddr_t base) +{ + /* Disable CRC checking on command packets. */ + io_write32_off_field(base + HWKM_ICE_TZ_REGS_OFFSET, HWKM_TZ_KM_CTL, + HWKM_TZ_KM_CTL_CRC_CHECK_EN, 0x0); + + /* Grant TZ (BANK0) unrestricted access to all key slots. */ + io_write32_off(base + HWKM_ICE_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_0, 0xFFFFFFFF); + io_write32_off(base + HWKM_ICE_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_1, 0xFFFFFFFF); + io_write32_off(base + HWKM_ICE_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_2, 0xFFFFFFFF); + io_write32_off(base + HWKM_ICE_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_3, 0xFFFFFFFF); + io_write32_off(base + HWKM_ICE_BANK0_AC_REGS_OFFSET, + HWKM_BANKn_AC_BBAC_4, 0xFFFFFFFF); + + /* Clear spurious RSP_FIFO_FULL sticky bit (HW errata QCTDD06252768). */ + io_write32_off(base + HWKM_ICE_BANK0_REGS_OFFSET, + HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL); +} + +/* + * ice_init() - Execute the ICE one-time HWKM initialization sequence. + * @base: MMIO base address of the ICE module. + * + * Transitions to standard mode, waits for init completion status bits, then + * applies post-init register configuration required for transactions. + * + * Return: HWKM_SUCCESS on success, or an HWKM_ERR_* code on failure. + */ +static int ice_init(vaddr_t base) +{ + int rc = HWKM_SUCCESS; + + ice_set_standard_mode(base); + rc = ice_wait_init(base); + if (rc) { + /* Revert ICE to legacy mode in case of failure */ + ice_set_legacy_mode(base); + return rc; + } + + ice_setup_finalize(base); + + return rc; +} + +/* + * is_ice_in_standard_mode() - Check whether ICE HWKM is in standard mode. + * @base: MMIO base address of the ICE module. + * + * Reads TZ_KM_CTL and validates that the mode value matches the expected + * standard-mode configuration. + * + * Return: true if standard mode is active, false otherwise. + */ +static bool is_ice_in_standard_mode(vaddr_t base) +{ + uint32_t mode = io_read32_off_field(base + HWKM_ICE_TZ_REGS_OFFSET, + HWKM_TZ_KM_CTL, + HWKM_TZ_KM_CTL_ICE_LEGACY_MODE_EN_OTP); + + /* Standard mode */ + if (mode == 0x0) + return true; + + return false; +} + +/* + * hwkm_ice_enable_tpkey_receive() - Arm ICE to receive an incoming TPKEY. + * @base: MMIO base address of the ICE module. + * + * Clears EN, programs TPKEY_DKS to the ICE default TPKEY slot, and re-enables + * EN to open the receive window before issuing SET_TPKEY on KM_MASTER. + */ +static void hwkm_ice_enable_tpkey_receive(vaddr_t base) +{ + vaddr_t tz_base = base + HWKM_ICE_TZ_REGS_OFFSET; + + /* Deassert EN first to reset any stale receive state. */ + io_write32_off_field(tz_base, HWKM_TZ_TPKEY_RECEIVE_CTL, + HWKM_TZ_TPKEY_RECEIVE_CTL_EN, 0); + + /* Open the receive window. */ + io_write32_off_field(tz_base, HWKM_TZ_TPKEY_RECEIVE_CTL, + HWKM_TZ_TPKEY_RECEIVE_CTL_EN, 1); + + /* Program the destination key slot. */ + io_write32_off_field(tz_base, HWKM_TZ_TPKEY_RECEIVE_CTL, + HWKM_TZ_TPKEY_RECEIVE_CTL_TPKEY_DKS, + HWKM_ICE_DEFAULT_TPKEY_SLOT); +} + +/* + * ice_wait_tpkey_done() - Poll until ICE reports TPKEY receive completion. + * @base: MMIO base address of the ICE module. + * + * Waits for TZ_TPKEY_RECEIVE_STATUS.DONE with bounded retries to ensure TPKEY + * handoff completion is observed before disarming receive. + * + * Return: HWKM_SUCCESS when DONE is set, or HWKM_ERR_FIFO_TIMEOUT. + */ +static int ice_wait_tpkey_done(vaddr_t base) +{ + uint32_t retries = 0; + vaddr_t tz_base = base + HWKM_ICE_TZ_REGS_OFFSET; + + while (!io_read32_off_field(tz_base, HWKM_TZ_TPKEY_RECEIVE_STATUS, + HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE)) { + if (++retries > HWKM_ICE_MAX_RETRIES) + return HWKM_ERR_FIFO_TIMEOUT; + udelay(10); + } + + return HWKM_SUCCESS; +} + +/* + * hwkm_ice_disable_tpkey_receive() - Disarm ICE TPKEY receive after completion. + * @base: MMIO base address of the ICE module. + * + * Waits for DONE and then clears TPKEY_RECEIVE_CTL.EN to close the receive + * window and avoid stale state across transactions. + * + * Return: HWKM_SUCCESS on success, or HWKM_ERR_FIFO_TIMEOUT. + */ +static int hwkm_ice_disable_tpkey_receive(vaddr_t base) +{ + int rc = ice_wait_tpkey_done(base); + + if (rc) + return rc; + + io_write32_off_field(base + HWKM_ICE_TZ_REGS_OFFSET, + HWKM_TZ_TPKEY_RECEIVE_CTL, + HWKM_TZ_TPKEY_RECEIVE_CTL_EN, 0); + + return HWKM_SUCCESS; +} + +/* + * is_hwkm_ice_tpkey_set() - Check whether ICE already has a TPKEY installed. + * @base: MMIO base address of the ICE module. + * + * Uses TZ_TPKEY_RECEIVE_STATUS.DONE as the persisted hardware indicator that a + * prior SET_TPKEY transfer completed successfully. + * + * Return: true when TPKEY is present, false otherwise. + */ +static bool is_hwkm_ice_tpkey_set(vaddr_t base) +{ + return io_read32_off_field(base + HWKM_ICE_TZ_REGS_OFFSET, + HWKM_TZ_TPKEY_RECEIVE_STATUS, + HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE) == 1U; +} + +/* + * hwkm_ice_set_tpkey() - Push TPKEY from KM_MASTER to the ICE slave. + * @base: MMIO base address of the ICE module. + * + * Arms ICE TPKEY receive, issues SET_TPKEY on KM_MASTER using + * HWKM_SLOT_TPKEY_SLOT, then waits for DONE and disarms receive. + * + * Return: HWKM_SUCCESS on success, or an HWKM_ERR_* code on failure. + */ +static int hwkm_ice_set_tpkey(vaddr_t base) +{ + struct hwkm_transaction t = { + .cmd = { + .op = HWKM_OP_SET_TPKEY, + .set_tpkey = { .sks = HWKM_SLOT_TPKEY_SLOT }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + /* Step 1: Arm the ICE slave to receive the incoming TPKEY. */ + hwkm_ice_enable_tpkey_receive(base); + + /* Step 2: Issue SET_TPKEY on the master -- this pushes the key to the slave. */ + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t); + if (rc) + goto out; + + if (t.rsp.status) { + rc = HWKM_ERR_GENERIC; + goto out; + } + +out: + /* Step 3: Wait for DONE and disarm regardless of SET_TPKEY outcome. */ + if (hwkm_ice_disable_tpkey_receive(base) && !rc) + rc = HWKM_ERR_FIFO_TIMEOUT; + + return rc; +} + +/* + * hwkm_ice_configure() - Ensure ICE HWKM is ready and TPKEY is installed. + * @base: MMIO base address of the ICE module. + * + * Runs ICE init if not already in standard mode. If TPKEY is not yet set, + * performs the TPKEY handoff sequence before allowing ICE transactions. + * + * Return: HWKM_SUCCESS on success, or an HWKM_ERR_* code on failure. + */ +int hwkm_ice_configure(vaddr_t base) +{ + int rc; + + if (!is_ice_in_standard_mode(base)) { + rc = ice_init(base); + if (rc) + return rc; + } + + if (is_hwkm_ice_tpkey_set(base)) + return HWKM_SUCCESS; + + return hwkm_ice_set_tpkey(base); +} diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm.h b/core/drivers/crypto/qcom/hwkm/include/hwkm.h index 76f23c616..f66ad021d 100644 --- a/core/drivers/crypto/qcom/hwkm/include/hwkm.h +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm.h @@ -18,6 +18,7 @@ enum hwkm_key_destination { HWKM_KEY_DEST_KM_MASTER = 0, HWKM_KEY_DEST_GPCE_SLAVE = 1, /* CRYPTO0 CE slave. */ + HWKM_KEY_DEST_ICE_SLAVE = 2, /* ICE slave */ }; enum hwkm_key_security_lvl { @@ -261,6 +262,7 @@ struct hwkm_handle; struct hwkm_drv_ctx { vaddr_t base; /* HWKM master MMIO base. */ vaddr_t crypto0_base; /* CRYPTO0 top MMIO base. */ + vaddr_t ice_base; /* ICE MMIO base */ struct mutex hwkm_lock; /* Serializes all HWKM operations. */ bool hwkm_huk_ready; uint8_t hwkm_huk[HWKM_MAX_KEY_SIZE]; diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm_ice.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_ice.h new file mode 100644 index 000000000..299658943 --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm_ice.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#ifndef __HWKM_ICE_H__ +#define __HWKM_ICE_H__ + +int hwkm_ice_configure(vaddr_t base); + +#endif /* __HWKM_ICE_H__ */ diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h index de23134ce..62fb5524b 100644 --- a/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h @@ -27,6 +27,14 @@ #define HWKM_CRYPTO0_BANK1_AC_REGS_OFFSET 0x36000U #define HWKM_CRYPTO0_BANK2_AC_REGS_OFFSET 0x37000U +/* ICE module's base offsets. */ +#define HWKM_ICE_SHARED_REGS_OFFSET 0x8000U +#define HWKM_ICE_TZ_REGS_OFFSET 0x9000U +#define HWKM_ICE_BANK0_REGS_OFFSET 0xa000U +#define HWKM_ICE_BANK0_AC_REGS_OFFSET 0xd000U +#define HWKM_ICE_BANK1_AC_REGS_OFFSET 0xe000U +#define HWKM_ICE_BANK2_AC_REGS_OFFSET 0xf000U + /* IP Catalog version: [31:24] major, [23:16] minor, [15:0] step. */ #define HWKM_SHARED_IPCAT_VERSION 0x0000U /* Key-policy format version supported by this instance. */ @@ -40,7 +48,21 @@ #define HWKM_TZ_KM_CTL_CRC_CHECK_EN \ BIT(HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT) +/* Bit 5 - Enable ICE Legacy mode. + * 1 = ICE operating in Legacy mode. + * 0 = ICE operating in Standard mode. + */ +#define HWKM_TZ_KM_CTL_ICE_LEGACY_MODE_EN_OTP_SHIFT 0x05U +#define HWKM_TZ_KM_CTL_ICE_LEGACY_MODE_EN_OTP \ + BIT(HWKM_TZ_KM_CTL_ICE_LEGACY_MODE_EN_OTP_SHIFT) + #define HWKM_TZ_KM_STATUS 0x0004U + +/* Hardware BIST completion status. */ +#define HWKM_TZ_KM_STATUS_BIST_DONE_SHIFT 0x10U +#define HWKM_TZ_KM_STATUS_BIST_DONE \ + BIT(HWKM_TZ_KM_STATUS_BIST_DONE_SHIFT) + /* * Hardware BIST detected a fault in the key table or crypto logic. * If set, the instance must not be used; the driver treats this as @@ -49,6 +71,11 @@ #define HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT 0xfU #define HWKM_TZ_KM_STATUS_BIST_ERROR \ BIT(HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT) + +/* Crypto-library BIST completion status. */ +#define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_DONE_SHIFT 0xeU +#define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_DONE \ + BIT(HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_DONE_SHIFT) /* * Internal crypto-library self-test failed. Treated identically to * BIST_ERROR by the driver; either bit causes bist_failed to be set. @@ -57,6 +84,21 @@ #define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR \ BIT(HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR_SHIFT) +/* Boot command list 1 completion status. */ +#define HWKM_TZ_KM_STATUS_BOOT_CMD_LIST1_DONE_SHIFT 0x2U +#define HWKM_TZ_KM_STATUS_BOOT_CMD_LIST1_DONE \ + BIT(HWKM_TZ_KM_STATUS_BOOT_CMD_LIST1_DONE_SHIFT) + +/* Boot command list 0 completion status. */ +#define HWKM_TZ_KM_STATUS_BOOT_CMD_LIST0_DONE_SHIFT 0x1U +#define HWKM_TZ_KM_STATUS_BOOT_CMD_LIST0_DONE \ + BIT(HWKM_TZ_KM_STATUS_BOOT_CMD_LIST0_DONE_SHIFT) + +/* Key-table clear sequence completion status. */ +#define HWKM_TZ_KM_STATUS_KT_CLEAR_DONE_SHIFT 0x0U +#define HWKM_TZ_KM_STATUS_KT_CLEAR_DONE \ + BIT(HWKM_TZ_KM_STATUS_KT_CLEAR_DONE_SHIFT) + #define HWKM_TZ_TPKEY_RECEIVE_CTL 0x001cU /* Arm (1) or disarm (0) the slave for TPKEY reception. */ #define HWKM_TZ_TPKEY_RECEIVE_CTL_EN_SHIFT 0x8U diff --git a/core/drivers/crypto/qcom/hwkm/sub.mk b/core/drivers/crypto/qcom/hwkm/sub.mk index 401c06eda..6b9f0cdd2 100644 --- a/core/drivers/crypto/qcom/hwkm/sub.mk +++ b/core/drivers/crypto/qcom/hwkm/sub.mk @@ -6,6 +6,7 @@ incdirs-y += include srcs-y += hwkm.c srcs-y += huk.c srcs-y += transaction.c +srcs-y += ice.c # Bitmap of fuse regions whose SHA256 digest is folded into the HUK KDF input. # Each bit corresponds to a fuse region index; set a bit to bind the HUK to diff --git a/core/drivers/crypto/qcom/hwkm/transaction.c b/core/drivers/crypto/qcom/hwkm/transaction.c index a645e3089..550f9c4bf 100644 --- a/core/drivers/crypto/qcom/hwkm/transaction.c +++ b/core/drivers/crypto/qcom/hwkm/transaction.c @@ -5,7 +5,11 @@ #include #include +#include #include +#include +#include +#include #include #include #include @@ -491,6 +495,33 @@ static int gpce_run_transaction(const uint32_t *cmd, size_t cmd_words, cmd, cmd_words, rsp, rsp_words); } +static int ice_run_transaction(const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + struct hwkm_drv_ctx *ctx; + int rc = HWKM_ERR_GENERIC; + + ctx = hwkm_get_context(); + if (!ctx) + return HWKM_ERR_INVALID_ARG; + + if (!ctx->ice_base) + ctx->ice_base = (vaddr_t)phys_to_virt(HWKM_ICE_BASE, + MEM_AREA_IO_SEC, + HWKM_ICE_SIZE); + + /* ICE clocks and power are handled by Linux, so we need to re-configure + * the hardware before submitting any new transactions. + */ + rc = hwkm_ice_configure(ctx->ice_base); + if (rc) + return rc; + + return run_fifo_transaction(ctx->ice_base + + HWKM_ICE_BANK0_REGS_OFFSET, + cmd, cmd_words, rsp, rsp_words); +} + static int run_transaction(const struct hwkm_transaction *t, const uint32_t *cmd, size_t cmd_words, uint32_t *rsp, size_t rsp_words) @@ -503,6 +534,8 @@ static int run_transaction(const struct hwkm_transaction *t, return master_run_transaction(cmd, cmd_words, rsp, rsp_words); case HWKM_KEY_DEST_GPCE_SLAVE: return gpce_run_transaction(cmd, cmd_words, rsp, rsp_words); + case HWKM_KEY_DEST_ICE_SLAVE: + return ice_run_transaction(cmd, cmd_words, rsp, rsp_words); default: return HWKM_ERR_INVALID_DEST; } @@ -790,6 +823,7 @@ int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest) switch (dest) { case HWKM_KEY_DEST_KM_MASTER: case HWKM_KEY_DEST_GPCE_SLAVE: + case HWKM_KEY_DEST_ICE_SLAVE: break; default: return HWKM_ERR_INVALID_DEST; From 0c6f0c745acc3716fc45531fd9a689091e33afde Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Sun, 30 Aug 2026 21:05:04 +0530 Subject: [PATCH 06/13] include: drivers: Export Qualcomm HWKM interface Export the interface to the Qualcomm hardware key manager (HWKM) drivers by moving the hwkm.h and hwkm_errno.h files to include/drivers/ path. Signed-off-by: Harshal Dev --- core/drivers/crypto/qcom/hwkm/huk.c | 4 ++-- core/drivers/crypto/qcom/hwkm/hwkm.c | 4 ++-- core/drivers/crypto/qcom/hwkm/ice.c | 2 +- core/drivers/crypto/qcom/hwkm/transaction.c | 2 +- .../crypto/qcom/hwkm/include => include/drivers}/hwkm.h | 0 .../crypto/qcom/hwkm/include => include/drivers}/hwkm_errno.h | 0 6 files changed, 6 insertions(+), 6 deletions(-) rename core/{drivers/crypto/qcom/hwkm/include => include/drivers}/hwkm.h (100%) rename core/{drivers/crypto/qcom/hwkm/include => include/drivers}/hwkm_errno.h (100%) diff --git a/core/drivers/crypto/qcom/hwkm/huk.c b/core/drivers/crypto/qcom/hwkm/huk.c index 81d71e642..67b9ae4a1 100644 --- a/core/drivers/crypto/qcom/hwkm/huk.c +++ b/core/drivers/crypto/qcom/hwkm/huk.c @@ -9,8 +9,8 @@ #include #include -#include -#include +#include +#include #define HWKM_HUK_MKS_CTX "OPTEE_HUK_HWKM_V1_MKS" #define HWKM_HUK_L3_CTX "OPTEE_HUK_HWKM_V1_L3" diff --git a/core/drivers/crypto/qcom/hwkm/hwkm.c b/core/drivers/crypto/qcom/hwkm/hwkm.c index 9ae1093e5..d2bd3b65f 100644 --- a/core/drivers/crypto/qcom/hwkm/hwkm.c +++ b/core/drivers/crypto/qcom/hwkm/hwkm.c @@ -3,8 +3,8 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. */ -#include -#include +#include +#include #include #include #include diff --git a/core/drivers/crypto/qcom/hwkm/ice.c b/core/drivers/crypto/qcom/hwkm/ice.c index 54b995c37..6e9fdabd3 100644 --- a/core/drivers/crypto/qcom/hwkm/ice.c +++ b/core/drivers/crypto/qcom/hwkm/ice.c @@ -3,7 +3,7 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. */ -#include +#include #include #include #include diff --git a/core/drivers/crypto/qcom/hwkm/transaction.c b/core/drivers/crypto/qcom/hwkm/transaction.c index 550f9c4bf..ea1892d83 100644 --- a/core/drivers/crypto/qcom/hwkm/transaction.c +++ b/core/drivers/crypto/qcom/hwkm/transaction.c @@ -4,7 +4,7 @@ */ #include -#include +#include #include #include #include diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm.h b/core/include/drivers/hwkm.h similarity index 100% rename from core/drivers/crypto/qcom/hwkm/include/hwkm.h rename to core/include/drivers/hwkm.h diff --git a/core/drivers/crypto/qcom/hwkm/include/hwkm_errno.h b/core/include/drivers/hwkm_errno.h similarity index 100% rename from core/drivers/crypto/qcom/hwkm/include/hwkm_errno.h rename to core/include/drivers/hwkm_errno.h From cf4636caa0396e5f77f1810ff289eaac3f0b23f0 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Mon, 27 Jul 2026 12:28:28 +0530 Subject: [PATCH 07/13] pta: qcom: ice: Add ICE PTA for generating a wrapped HW-derived L4 key Add a pseudo-TA for the Qualcomm Inline Crypto Engine (ICE) that lets the kernel inline-crypt path generate a wrapped L4 key derived from the unique key derivation key (UKDK) available with the Hardware Key manager for inline storage encryption. Only the REE kernel may open a session on this PTA. The PTA is gated by CFG_ICE_FS_ENC_PTA and is not built unless a platform enables it. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/pta/qcom/ice/hwkm/hwkm_derive_keys.c | 200 ++++++++++++++++++++++ core/pta/qcom/ice/hwkm/hwkm_derive_keys.h | 14 ++ core/pta/qcom/ice/hwkm/ice_hwkm.c | 108 ++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 13 ++ core/pta/qcom/ice/hwkm/sub.mk | 3 + core/pta/qcom/ice/ice.c | 32 ++++ core/pta/qcom/ice/sub.mk | 1 + lib/libutee/include/pta_qcom_ice.h | 15 ++ 8 files changed, 386 insertions(+) create mode 100644 core/pta/qcom/ice/hwkm/hwkm_derive_keys.c create mode 100644 core/pta/qcom/ice/hwkm/hwkm_derive_keys.h create mode 100644 core/pta/qcom/ice/hwkm/ice_hwkm.c create mode 100644 core/pta/qcom/ice/hwkm/ice_hwkm.h create mode 100644 core/pta/qcom/ice/hwkm/sub.mk diff --git a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c new file mode 100644 index 000000000..c8f8d9ba1 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "hwkm_derive_keys.h" + +static const uint8_t l4_wrap_ctx[] = + "HWKM : Hardware Key Manager provides hardware-based cryptography"; +static const uint8_t optee_ctx[] = { + 0x51, 0x54, 0x45, 0x45, 0x20, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x65, 0x64, 0x20, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x54, 0x58, +}; + +static TEE_Result build_wrapping_kdf_ctx(const uint8_t *wrap_ctx, + size_t wrap_ctx_len, + uint8_t out_ctx[HWKM_MAX_CTX_SIZE]) +{ + void *hash_ctx = NULL; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!wrap_ctx || !wrap_ctx_len) + return TEE_ERROR_BAD_PARAMETERS; + + res = crypto_hash_alloc_ctx(&hash_ctx, TEE_ALG_SHA512); + if (res) + return res; + + res = crypto_hash_init(hash_ctx); + if (res) + goto out; + + res = crypto_hash_update(hash_ctx, optee_ctx, sizeof(optee_ctx)); + if (res) + goto out; + + res = crypto_hash_update(hash_ctx, wrap_ctx, wrap_ctx_len); + if (res) + goto out; + + res = crypto_hash_final(hash_ctx, out_ctx, HWKM_MAX_CTX_SIZE); + +out: + crypto_hash_free_ctx(hash_ctx); + return res; +} + +/* + * derive_l4_wrapping_key() - Derive L3 and L4 wrapping keys from UKDK_L2. + * + * Derives L3 into TZ_GENERAL_PURPOSE_SLOT2 and L4 into TZ_WRAP_KEY_SLOT. + */ +TEE_Result derive_l4_wrapping_key(void) +{ + uint8_t kdf_ctx[HWKM_MAX_CTX_SIZE] = { }; + struct hwkm_transaction t_kdf_l3 = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .kdk = HWKM_SLOT_TZ_UKDK_L2, + .policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }, + .bsve = { + .enabled = true, + .km_swc_en = true, + .km_key_policy_ver_en = true, + .km_apps_secure_en = true, + .km_msa_secure_en = true, + .km_child_key_policy_en = true, + }, + .ctx_len = 0, + }, + }, + }; + struct hwkm_transaction t_kdf_l4 = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + .kdk = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_SIV, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_KWK, + .kdf_depth = 0, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }, + .bsve = { + .enabled = true, + .km_swc_en = true, + .km_key_policy_ver_en = true, + .km_apps_secure_en = true, + .km_msa_secure_en = true, + .km_child_key_policy_en = true, + }, + .ctx_len = 0, + }, + }, + }; + struct hwkm_transaction t_clear_gp2 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + res = build_wrapping_kdf_ctx(l4_wrap_ctx, sizeof(l4_wrap_ctx) - 1, + kdf_ctx); + if (res != TEE_SUCCESS) + return res; + + memcpy(t_kdf_l3.cmd.kdf.ctx, kdf_ctx, sizeof(kdf_ctx)); + t_kdf_l3.cmd.kdf.ctx_len = sizeof(kdf_ctx); + memcpy(t_kdf_l4.cmd.kdf.ctx, kdf_ctx, sizeof(kdf_ctx)); + t_kdf_l4.cmd.kdf.ctx_len = sizeof(kdf_ctx); + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 2, + (struct hwkm_transaction *const[]){ + &t_kdf_l3, &t_kdf_l4 }); + + memset(t_kdf_l3.cmd.kdf.ctx, 0, sizeof(t_kdf_l3.cmd.kdf.ctx)); + memset(t_kdf_l4.cmd.kdf.ctx, 0, sizeof(t_kdf_l4.cmd.kdf.ctx)); + memset(kdf_ctx, 0, sizeof(kdf_ctx)); + + if (rc) + return hwkm_to_optee(rc); + + if (t_kdf_l3.rsp.status != HWKM_RSP_ERR_SUCCESS || + t_kdf_l4.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE derive L4: l3=0x%x l4=0x%x", + (unsigned int)t_kdf_l3.rsp.status, + (unsigned int)t_kdf_l4.rsp.status); + return TEE_ERROR_GENERIC; + } + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_gp2); + if (rc) + return hwkm_to_optee(rc); + + if (t_clear_gp2.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_gp2.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("ICE derive L4 clear L3: gp2=0x%x", + (unsigned int)t_clear_gp2.rsp.status); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +/* clear_l4_wrapping_key() - Clear the L4 wrapping slot. */ +TEE_Result clear_l4_wrapping_key(void) +{ + struct hwkm_transaction t_clear_wrap = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + .is_double_key = true, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_wrap); + if (rc) + return hwkm_to_optee(rc); + + if (t_clear_wrap.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_wrap.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("ICE clear L4: wrap=0x%x", + (unsigned int)t_clear_wrap.rsp.status); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} diff --git a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h new file mode 100644 index 000000000..5c1256383 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __HWKM_DERIVE_KEYS_H +#define __HWKM_DERIVE_KEYS_H + +#include + +TEE_Result derive_l4_wrapping_key(void); +TEE_Result clear_l4_wrapping_key(void); + +#endif /* __HWKM_DERIVE_KEYS_H */ diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c new file mode 100644 index 000000000..6440586a6 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "hwkm_derive_keys.h" +#include "ice_hwkm.h" + +/* + * generate_hw_wrapped_key() - Generate and wrap a key with UKDK-derived L4 key. + * + * Generates key material in TZ_GENERAL_PURPOSE_SLOT1, derives L3 then L4 + * wrapping keys from TZ_UKDK_L2, exports wrapped blob, and clears transient + * slots. + */ +TEE_Result generate_hw_wrapped_key(uint8_t *out_blob, size_t *out_blob_len) +{ + const struct hwkm_key_policy key_policy = { + .km_by_tz_allowed = true, + .km_by_nsec_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .wrap_export_allowed = true, + .swap_export_allowed = true, + .wrap_with_tpkey_allowed = true, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_ICE_SLAVE, + }; + struct hwkm_transaction t_keygen = { + .cmd = { + .op = HWKM_OP_NIST_KEYGEN, + .keygen = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .policy = key_policy, + }, + }, + }; + struct hwkm_transaction t_wrap = { + .cmd = { + .op = HWKM_OP_KEY_WRAP_EXPORT, + .wrap = { + .sks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_clear_gp1 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!out_blob || !out_blob_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (*out_blob_len < HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_SHORT_BUFFER; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_keygen); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_keygen.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE gen L4: keygen=0x%x", (unsigned int)t_keygen.rsp.status); + goto cleanup; + } + + res = derive_l4_wrapping_key(); + if (res != TEE_SUCCESS) + goto cleanup; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_wrap); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_wrap.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE gen L4: wrap=0x%x", (unsigned int)t_wrap.rsp.status); + goto cleanup; + } + + memcpy(out_blob, t_wrap.rsp.wrap.wkb, HWKM_MAX_BLOB_SIZE); + *out_blob_len = HWKM_MAX_BLOB_SIZE; + res = TEE_SUCCESS; + +cleanup: + (void)clear_l4_wrapping_key(); + (void)hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_gp1); + + memset(t_wrap.rsp.wrap.wkb, 0, sizeof(t_wrap.rsp.wrap.wkb)); + return res; +} diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h new file mode 100644 index 000000000..27e807c64 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __ICE_HWKM_H +#define __ICE_HWKM_H + +#include + +TEE_Result generate_hw_wrapped_key(uint8_t *out_blob, size_t *out_blob_len); + +#endif /* __ICE_HWKM_H */ diff --git a/core/pta/qcom/ice/hwkm/sub.mk b/core/pta/qcom/ice/hwkm/sub.mk new file mode 100644 index 000000000..9c97104c7 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/sub.mk @@ -0,0 +1,3 @@ +srcs-y += ice_hwkm.c +srcs-y += hwkm_derive_keys.c +incdirs-y += . diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index 28b18b057..4fe941295 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -8,7 +8,9 @@ #include #include #include +#include +#include "hwkm/ice_hwkm.h" #include "sw_keys/ice_sw_keys.h" /* @@ -30,6 +32,34 @@ static TEE_Result cmd_ice_set_config_key(uint32_t param_types, return sw_cmd_ice_set_config_key(param_types, params); } +static TEE_Result cmd_ice_generate_key(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[0].memref.buffer) { + if (params[0].memref.size == 0) { + params[0].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + return TEE_ERROR_BAD_PARAMETERS; + } + + if (params[0].memref.size < HWKM_MAX_BLOB_SIZE) { + params[0].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + return generate_hw_wrapped_key(params[0].memref.buffer, + ¶ms[0].memref.size); +} + /* PTA command dispatcher */ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, uint32_t param_types, @@ -40,6 +70,8 @@ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, return cmd_ice_invalidate_key(param_types, params); case PTA_CMD_ICE_SET_CONFIG_KEY: return cmd_ice_set_config_key(param_types, params); + case PTA_CMD_ICE_GENERATE_KEY: + return cmd_ice_generate_key(param_types, params); default: break; } diff --git a/core/pta/qcom/ice/sub.mk b/core/pta/qcom/ice/sub.mk index 5d8e15910..f625497de 100644 --- a/core/pta/qcom/ice/sub.mk +++ b/core/pta/qcom/ice/sub.mk @@ -1,3 +1,4 @@ srcs-y += ice.c incdirs-y += . subdirs-y += sw_keys +subdirs-y += hwkm diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index 7605067f6..1f0bd82fa 100644 --- a/lib/libutee/include/pta_qcom_ice.h +++ b/lib/libutee/include/pta_qcom_ice.h @@ -37,4 +37,19 @@ */ #define PTA_CMD_ICE_SET_CONFIG_KEY 1 +/* + * Generate a hardware-wrapped ICE storage key blob. + * + * Caller supplies an output buffer and its size. + * + * [out] params[0].memref.buffer Output wrapped key blob buffer + * [in/out] params[0].memref.size Input: buffer capacity + * Output: actual blob size + * Required size: 68 bytes (HWKM_MAX_BLOB_SIZE) + * If too small, returns TEE_ERROR_SHORT_BUFFER + * and updates size with required length. + */ +#define PTA_CMD_ICE_GENERATE_KEY 2 + + #endif /* __PTA_QCOM_ICE_H */ From 366e85545959e9098e4c5dd2517d0937fff66d88 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Wed, 29 Jul 2026 20:08:50 +0530 Subject: [PATCH 08/13] pta: qcom: ice: Import and wrap a key with HW-derived L4 key Add support for importing and wrapping a key with a UKDK-derived L4 key. The wrapped key is returned to the REE which can use it as a storage key. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/pta/qcom/ice/hwkm/ice_hwkm.c | 105 +++++++++++++++++++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 3 + core/pta/qcom/ice/ice.c | 37 ++++++++++ lib/libutee/include/pta_qcom_ice.h | 15 +++++ 4 files changed, 160 insertions(+) diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c index 6440586a6..736008e12 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.c +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -11,6 +11,111 @@ #include "hwkm_derive_keys.h" #include "ice_hwkm.h" +/* + * import_and_wrap_with_hw_key() - Import caller key bytes then wrap under L4 key. + * + * Writes caller key material into TZ_GENERAL_PURPOSE_SLOT1, derives L3 then + * L4 wrapping keys from TZ_UKDK_L2, exports wrapped blob, and clears + * transient slots. + */ +TEE_Result import_and_wrap_with_hw_key(const uint8_t *in_key, size_t in_key_len, + uint8_t *out_blob, size_t *out_blob_len) +{ + const struct hwkm_key_policy import_policy = { + .km_by_tz_allowed = true, + .km_by_nsec_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .wrap_export_allowed = true, + .swap_export_allowed = true, + .wrap_with_tpkey_allowed = true, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_ICE_SLAVE, + }; + struct hwkm_transaction t_import = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_RDWR, + .rdwr = { + .slot = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_write = true, + .policy = import_policy, + }, + }, + }; + struct hwkm_transaction t_wrap = { + .cmd = { + .op = HWKM_OP_KEY_WRAP_EXPORT, + .wrap = { + .sks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_clear_gp1 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!in_key || !in_key_len || !out_blob || !out_blob_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (in_key_len > HWKM_MAX_KEY_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (*out_blob_len < HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_SHORT_BUFFER; + + memcpy(t_import.cmd.rdwr.key, in_key, in_key_len); + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_import); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_import.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE import L4: import=0x%x", (unsigned int)t_import.rsp.status); + goto cleanup; + } + + res = derive_l4_wrapping_key(); + if (res != TEE_SUCCESS) + goto cleanup; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_wrap); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_wrap.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE import L4: wrap=0x%x", (unsigned int)t_wrap.rsp.status); + goto cleanup; + } + + memcpy(out_blob, t_wrap.rsp.wrap.wkb, HWKM_MAX_BLOB_SIZE); + *out_blob_len = HWKM_MAX_BLOB_SIZE; + res = TEE_SUCCESS; + +cleanup: + (void)clear_l4_wrapping_key(); + (void)hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_gp1); + + memset(t_import.cmd.rdwr.key, 0, sizeof(t_import.cmd.rdwr.key)); + memset(t_wrap.rsp.wrap.wkb, 0, sizeof(t_wrap.rsp.wrap.wkb)); + + return res; +} + /* * generate_hw_wrapped_key() - Generate and wrap a key with UKDK-derived L4 key. * diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h index 27e807c64..4a12a6de4 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.h +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -8,6 +8,9 @@ #include +TEE_Result import_and_wrap_with_hw_key(const uint8_t *in_key, size_t in_key_len, + uint8_t *out_blob, size_t *out_blob_len); + TEE_Result generate_hw_wrapped_key(uint8_t *out_blob, size_t *out_blob_len); #endif /* __ICE_HWKM_H */ diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index 4fe941295..b970231a2 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -60,6 +60,41 @@ static TEE_Result cmd_ice_generate_key(uint32_t param_types, ¶ms[0].memref.size); } +static TEE_Result cmd_ice_import_key(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[0].memref.buffer || !params[0].memref.size || + params[0].memref.size > HWKM_MAX_KEY_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[1].memref.buffer) { + if (params[1].memref.size == 0) { + params[1].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + return TEE_ERROR_BAD_PARAMETERS; + } + + if (params[1].memref.size < HWKM_MAX_BLOB_SIZE) { + params[1].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + return import_and_wrap_with_hw_key(params[0].memref.buffer, + params[0].memref.size, + params[1].memref.buffer, + ¶ms[1].memref.size); +} + /* PTA command dispatcher */ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, uint32_t param_types, @@ -72,6 +107,8 @@ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, return cmd_ice_set_config_key(param_types, params); case PTA_CMD_ICE_GENERATE_KEY: return cmd_ice_generate_key(param_types, params); + case PTA_CMD_ICE_IMPORT_KEY: + return cmd_ice_import_key(param_types, params); default: break; } diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index 1f0bd82fa..0ae65e13b 100644 --- a/lib/libutee/include/pta_qcom_ice.h +++ b/lib/libutee/include/pta_qcom_ice.h @@ -49,7 +49,22 @@ * If too small, returns TEE_ERROR_SHORT_BUFFER * and updates size with required length. */ + #define PTA_CMD_ICE_GENERATE_KEY 2 +/* + * Import key material and return a hardware-wrapped ICE storage key blob. + * + * [in] params[0].memref.buffer Input key material to import + * [in] params[0].memref.size Input key length (1..HWKM_MAX_KEY_SIZE) + * [out] params[1].memref.buffer Output wrapped key blob buffer + * [in/out] params[1].memref.size Input: buffer capacity + * Output: actual blob size + * Required size: 68 bytes (HWKM_MAX_BLOB_SIZE) + * If too small, returns TEE_ERROR_SHORT_BUFFER + * and updates size with required length. + */ +#define PTA_CMD_ICE_IMPORT_KEY 3 + #endif /* __PTA_QCOM_ICE_H */ From 6080f58a7127e38b05b963171ae435a085f6e9f3 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Wed, 29 Jul 2026 23:22:42 +0530 Subject: [PATCH 09/13] pta: qcom: ice: Export a key by wrapping with HW ephemeral key Add support for exporting a key after un-wrapping it with a UKDK-derived L4 key and re-wrapping with an ephemeral key (also a HW derived UKDK L4 key). This ties the storage key with a per boot generated random seed. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/pta/qcom/ice/hwkm/hwkm_derive_keys.c | 171 +++++++++++++++++++++- core/pta/qcom/ice/hwkm/hwkm_derive_keys.h | 6 + core/pta/qcom/ice/hwkm/ice_hwkm.c | 133 +++++++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 3 + core/pta/qcom/ice/ice.c | 38 +++++ lib/libutee/include/pta_qcom_ice.h | 13 ++ 6 files changed, 360 insertions(+), 4 deletions(-) diff --git a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c index c8f8d9ba1..d7e9c9b62 100644 --- a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c @@ -22,6 +22,7 @@ static const uint8_t optee_ctx[] = { static TEE_Result build_wrapping_kdf_ctx(const uint8_t *wrap_ctx, size_t wrap_ctx_len, + bool implicit_context, uint8_t out_ctx[HWKM_MAX_CTX_SIZE]) { void *hash_ctx = NULL; @@ -38,9 +39,11 @@ static TEE_Result build_wrapping_kdf_ctx(const uint8_t *wrap_ctx, if (res) goto out; - res = crypto_hash_update(hash_ctx, optee_ctx, sizeof(optee_ctx)); - if (res) - goto out; + if (implicit_context) { + res = crypto_hash_update(hash_ctx, optee_ctx, sizeof(optee_ctx)); + if (res) + goto out; + } res = crypto_hash_update(hash_ctx, wrap_ctx, wrap_ctx_len); if (res) @@ -53,6 +56,166 @@ static TEE_Result build_wrapping_kdf_ctx(const uint8_t *wrap_ctx, return res; } +/* + * derive_ephemeral_wrapping_key() - Derive ephemeral KWK into + * TZ_GENERAL_PURPOSE_SLOT2. + * + * Uses SYSTEM_KDF from UKDK_L2 into the TZ_GENERAL_PURPOSE_SLOT2. + * The caller-provided 64-byte ephemeral context is mixed with + * enabled bindings in the derivation state so the resulting wrapping + * key is bound to a random ephemeral seed/context and cannot be reused + * across sessions. + * + * Caller must invoke clear_ephemeral_key() when done with the key. + */ +TEE_Result derive_ephemeral_wrapping_key(const uint8_t *ephemeral_ctx, + size_t ctx_len) +{ + uint8_t hashed_ctx[HWKM_MAX_CTX_SIZE] = { }; + struct hwkm_transaction t_kdf_eph_l3 = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .kdk = HWKM_SLOT_TZ_UKDK_L2, + .policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }, + .bsve = { + .enabled = true, + .km_key_policy_ver_en = true, + .km_apps_secure_en = true, + .km_msa_secure_en = true, + .km_child_key_policy_en = true, + }, + .ctx_len = HWKM_MAX_CTX_SIZE, + }, + }, + }; + struct hwkm_transaction t_kdf_eph_l4 = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + .kdk = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_SIV, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_KWK, + .kdf_depth = 0, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }, + .bsve = { + .enabled = true, + .km_key_policy_ver_en = true, + .km_apps_secure_en = true, + .km_msa_secure_en = true, + .km_child_key_policy_en = true, + }, + .ctx_len = HWKM_MAX_CTX_SIZE, + }, + }, + }; + struct hwkm_transaction t_clear_eph_l3 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!ephemeral_ctx || ctx_len != HWKM_EPHEMERAL_CTX_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + res = build_wrapping_kdf_ctx(ephemeral_ctx, ctx_len, false, hashed_ctx); + if (res != TEE_SUCCESS) + goto out; + + memcpy(t_kdf_eph_l3.cmd.kdf.ctx, hashed_ctx, sizeof(hashed_ctx)); + memcpy(t_kdf_eph_l4.cmd.kdf.ctx, hashed_ctx, sizeof(hashed_ctx)); + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 2, + (struct hwkm_transaction *const[]){ + &t_kdf_eph_l3, &t_kdf_eph_l4 }); + if (rc) { + res = hwkm_to_optee(rc); + goto out; + } + + if (t_kdf_eph_l3.rsp.status != HWKM_RSP_ERR_SUCCESS || + t_kdf_eph_l4.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE eph-kdf: l3=0x%x l4=0x%x", + (unsigned int)t_kdf_eph_l3.rsp.status, + (unsigned int)t_kdf_eph_l4.rsp.status); + res = TEE_ERROR_GENERIC; + goto out; + } + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_eph_l3); + if (rc) { + res = hwkm_to_optee(rc); + goto out; + } + + if (t_clear_eph_l3.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_eph_l3.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("ICE eph-kdf clear l3: status=0x%x", + (unsigned int)t_clear_eph_l3.rsp.status); + res = TEE_ERROR_GENERIC; + goto out; + } + + res = TEE_SUCCESS; +out: + memset(hashed_ctx, 0, sizeof(hashed_ctx)); + memset(t_kdf_eph_l3.cmd.kdf.ctx, 0, sizeof(t_kdf_eph_l3.cmd.kdf.ctx)); + memset(t_kdf_eph_l4.cmd.kdf.ctx, 0, sizeof(t_kdf_eph_l4.cmd.kdf.ctx)); + return res; +} + +/* + * clear_ephemeral_key() - Clear the L4 slot used for ephemeral wrapping key. + */ +TEE_Result clear_ephemeral_key(void) +{ + struct hwkm_transaction t_clear_ephemeral = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + .is_double_key = true, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_ephemeral); + if (rc) + return hwkm_to_optee(rc); + + if (t_clear_ephemeral.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_ephemeral.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("ICE clear ephemeral failed: status=0x%x", + (unsigned int)t_clear_ephemeral.rsp.status); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + /* * derive_l4_wrapping_key() - Derive L3 and L4 wrapping keys from UKDK_L2. * @@ -129,7 +292,7 @@ TEE_Result derive_l4_wrapping_key(void) TEE_Result res = TEE_ERROR_GENERIC; res = build_wrapping_kdf_ctx(l4_wrap_ctx, sizeof(l4_wrap_ctx) - 1, - kdf_ctx); + true, kdf_ctx); if (res != TEE_SUCCESS) return res; diff --git a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h index 5c1256383..3d7b685aa 100644 --- a/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h @@ -8,6 +8,12 @@ #include +#define HWKM_EPHEMERAL_CTX_SIZE 64U + +TEE_Result derive_ephemeral_wrapping_key(const uint8_t *ephemeral_ctx, + size_t ctx_len); +TEE_Result clear_ephemeral_key(void); + TEE_Result derive_l4_wrapping_key(void); TEE_Result clear_l4_wrapping_key(void); diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c index 736008e12..ae9ba7e32 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.c +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -5,12 +5,145 @@ #include #include +#include #include #include #include "hwkm_derive_keys.h" #include "ice_hwkm.h" +static uint8_t g_ephemeral_ctx[HWKM_EPHEMERAL_CTX_SIZE] = { 0 }; +static bool g_ephemeral_ctx_set = false; + +static TEE_Result get_or_init_ephemeral_ctx(const uint8_t **ctx, + size_t *ctx_len) +{ + if (!ctx || !ctx_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (!g_ephemeral_ctx_set) { + if (crypto_rng_read(g_ephemeral_ctx, + sizeof(g_ephemeral_ctx)) != TEE_SUCCESS) + return TEE_ERROR_GENERIC; + + g_ephemeral_ctx_set = true; + } + + *ctx = g_ephemeral_ctx; + *ctx_len = sizeof(g_ephemeral_ctx); + return TEE_SUCCESS; +} + +/* + * export_hw_wrapped_key() - Rewrap wrapped key blob under an ephemeral key. + * + * Sequence: + * 1) Unwrap input blob with base wrapping key. + * 2) Derive ephemeral wrapping key from RNG-provided context. + * 3) Wrap key under ephemeral wrapping key. + * 4) Clear ephemeral key slot. + */ +TEE_Result export_hw_wrapped_key(const uint8_t *in_blob, size_t in_blob_len, + uint8_t *out_blob, size_t *out_blob_len) +{ + const uint8_t *ephemeral_ctx = NULL; + size_t ephemeral_ctx_len = 0; + struct hwkm_transaction t_unwrap = { + .cmd = { + .op = HWKM_OP_KEY_UNWRAP_IMPORT, + .unwrap = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_wrap_ephemeral = { + .cmd = { + .op = HWKM_OP_KEY_WRAP_EXPORT, + .wrap = { + .sks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_clear_gp1 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!in_blob || !out_blob || !out_blob_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (in_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (*out_blob_len < HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_SHORT_BUFFER; + + res = get_or_init_ephemeral_ctx(&ephemeral_ctx, &ephemeral_ctx_len); + if (res != TEE_SUCCESS) + return res; + + res = derive_l4_wrapping_key(); + if (res != TEE_SUCCESS) + goto cleanup; + + memcpy(t_unwrap.cmd.unwrap.wkb, in_blob, HWKM_MAX_BLOB_SIZE); + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_unwrap); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_unwrap.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE export unwrap: unwrap=0x%x", + (unsigned int)t_unwrap.rsp.status); + goto cleanup; + } + + res = clear_l4_wrapping_key(); + if (res != TEE_SUCCESS) + goto cleanup; + + res = derive_ephemeral_wrapping_key(ephemeral_ctx, ephemeral_ctx_len); + if (res != TEE_SUCCESS) + goto cleanup; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_wrap_ephemeral); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_wrap_ephemeral.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE export rewrap: wrap=0x%x", + (unsigned int)t_wrap_ephemeral.rsp.status); + goto cleanup; + } + + memcpy(out_blob, t_wrap_ephemeral.rsp.wrap.wkb, HWKM_MAX_BLOB_SIZE); + *out_blob_len = HWKM_MAX_BLOB_SIZE; + res = TEE_SUCCESS; + +cleanup: + (void)clear_ephemeral_key(); + (void)clear_l4_wrapping_key(); + (void)hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_gp1); + + memset(t_unwrap.cmd.unwrap.wkb, 0, sizeof(t_unwrap.cmd.unwrap.wkb)); + memset(t_wrap_ephemeral.rsp.wrap.wkb, 0, sizeof(t_wrap_ephemeral.rsp.wrap.wkb)); + + return res; +} + /* * import_and_wrap_with_hw_key() - Import caller key bytes then wrap under L4 key. * diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h index 4a12a6de4..cc79f4643 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.h +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -8,6 +8,9 @@ #include +TEE_Result export_hw_wrapped_key(const uint8_t *in_blob, size_t in_blob_len, + uint8_t *out_blob, size_t *out_blob_len); + TEE_Result import_and_wrap_with_hw_key(const uint8_t *in_key, size_t in_key_len, uint8_t *out_blob, size_t *out_blob_len); diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index b970231a2..e106a2395 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -95,6 +95,42 @@ static TEE_Result cmd_ice_import_key(uint32_t param_types, ¶ms[1].memref.size); } + +static TEE_Result cmd_ice_export_key(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[0].memref.buffer || params[0].memref.size != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[1].memref.buffer) { + if (params[1].memref.size == 0) { + params[1].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + return TEE_ERROR_BAD_PARAMETERS; + } + + if (params[1].memref.size < HWKM_MAX_BLOB_SIZE) { + params[1].memref.size = HWKM_MAX_BLOB_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + return export_hw_wrapped_key(params[0].memref.buffer, + params[0].memref.size, + params[1].memref.buffer, + ¶ms[1].memref.size); +} + + /* PTA command dispatcher */ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, uint32_t param_types, @@ -109,6 +145,8 @@ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, return cmd_ice_generate_key(param_types, params); case PTA_CMD_ICE_IMPORT_KEY: return cmd_ice_import_key(param_types, params); + case PTA_CMD_ICE_EXPORT_KEY: + return cmd_ice_export_key(param_types, params); default: break; } diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index 0ae65e13b..b82c8e0dc 100644 --- a/lib/libutee/include/pta_qcom_ice.h +++ b/lib/libutee/include/pta_qcom_ice.h @@ -66,5 +66,18 @@ */ #define PTA_CMD_ICE_IMPORT_KEY 3 +/* + * Export a wrapped ICE storage key using an ephemeral wrapping key. + * + * [in] params[0].memref.buffer Input wrapped key blob + * [in] params[0].memref.size Input blob size (must be 68 bytes) + * [out] params[1].memref.buffer Output wrapped key blob buffer + * [in/out] params[1].memref.size Input: buffer capacity + * Output: actual blob size + * Required size: 68 bytes (HWKM_MAX_BLOB_SIZE) + * If too small, returns TEE_ERROR_SHORT_BUFFER + * and updates size with required length. + */ +#define PTA_CMD_ICE_EXPORT_KEY 4 #endif /* __PTA_QCOM_ICE_H */ From 401df55ed2afb5970d014c36e132ad5721ab8bf3 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Sun, 2 Aug 2026 12:37:18 +0530 Subject: [PATCH 10/13] pta: qcom: ice: Add support for programming a key into ICE Add support for programming an ephemerally wrapped key into a specified inline crypto engine (ICE) key slot. The key is first unwrapped via the ephemeral key, and then wrapped by a TP (transport) key before being imported into the ICE hardware block via the hardware key manager. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/arch/arm/plat-qcom/hoya/arch_config.h | 6 + .../arm/plat-qcom/hoya/lemans/target_config.h | 8 + core/pta/qcom/ice/hwkm/ice_hwkm.c | 316 ++++++++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 4 + core/pta/qcom/ice/ice.c | 34 +- lib/libutee/include/pta_qcom_ice.h | 9 +- 6 files changed, 372 insertions(+), 5 deletions(-) diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index 590cce073..99526e010 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -37,6 +37,12 @@ #define HWKM_SDC1_SDCC_ICE_BASE UL(0x087c8000) #define HWKM_SDC1_SDCC_ICE_SIZE UL(0x00018000) +#define PERIPH_SS_SDC1_SDCC_ICE_LUT_KEYS UL(0x087cc000) +#define PERIPH_SS_SDC1_SDCC_ICE_LUT_KEYS_SIZE UL(0x00003000) + +#define UFS_MEM_ICE_LUT_KEYS UL(0x01d8c000) +#define UFS_MEM_ICE_LUT_KEYS_SIZE UL(0x00003000) + #define IMEM_DIAG_OFFSET UL(0x720) #define DIAG_SIZE UL(0x3000) #define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h index 0a315e7a0..82bcaafe1 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -103,4 +103,12 @@ #define HWKM_ICE_SIZE HWKM_SDC1_SDCC_ICE_SIZE #endif /* CFG_QCOM_STORAGE_UFS */ +#if defined(CFG_QCOM_STORAGE_UFS) +#define ICE_LUT_KEYS UFS_MEM_ICE_LUT_KEYS +#define ICE_LUT_KEYS_SIZE UFS_MEM_ICE_LUT_KEYS_SIZE +#else /* CFG_QCOM_STORAGE_UFS */ +#define ICE_LUT_KEYS PERIPH_SS_SDC1_SDCC_ICE_LUT_KEYS +#define ICE_LUT_KEYS_SIZE PERIPH_SS_SDC1_SDCC_ICE_LUT_KEYS_SIZE +#endif /* CFG_QCOM_STORAGE_UFS */ + #endif /* TARGET_CONFIG_H */ diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c index ae9ba7e32..ce578be4e 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.c +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -3,15 +3,34 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include #include #include +#include +#include #include #include +#include "../config.h" #include "hwkm_derive_keys.h" #include "ice_hwkm.h" +#define HWKM_ICE_SLAVE_GP_SLOT 140U +#define HWKM_ICE_DEFAULT_TPKEY_SLOT 146U +#define HWKM_ICE_MAP_SLOT(pipe) ((uint8_t)(((pipe) * 2U) + 10U)) +#define HWKM_CTX_ALIGN_BYTES 8U + +static inline size_t round_up_ctx_len(size_t len) +{ + return (len + (HWKM_CTX_ALIGN_BYTES - 1U)) & ~(HWKM_CTX_ALIGN_BYTES - 1U); +} + +/* ICE register access for HWKM-based key programming orchestration */ +register_phys_mem_pgdir(MEM_AREA_IO_SEC, ICE_LUT_KEYS, ICE_LUT_KEYS_SIZE); + +static const uint8_t inlinecrypt_ctx[] = "inline encryption key"; + static uint8_t g_ephemeral_ctx[HWKM_EPHEMERAL_CTX_SIZE] = { 0 }; static bool g_ephemeral_ctx_set = false; @@ -34,6 +53,303 @@ static TEE_Result get_or_init_ephemeral_ctx(const uint8_t **ctx, return TEE_SUCCESS; } +/* + * export_tpkey_wrapped_blob_from_ephemeral() - Unwrap input blob under + * ephemeral wrapping key, then re-wrap/export under TPKEY. + */ +static TEE_Result export_tpkey_wrapped_blob_from_ephemeral(const uint8_t *in_blob, + size_t in_blob_len, + uint8_t *out_blob, + size_t *out_blob_len) +{ + const uint8_t *ephemeral_ctx = NULL; + size_t ephemeral_ctx_len = 0; + struct hwkm_transaction t_unwrap = { + .cmd = { + .op = HWKM_OP_KEY_UNWRAP_IMPORT, + .unwrap = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_wrap_tpkey = { + .cmd = { + .op = HWKM_OP_KEY_WRAP_EXPORT, + .wrap = { + .sks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .kwk = HWKM_SLOT_TPKEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_clear_gp1 = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!in_blob || !out_blob || !out_blob_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (in_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (*out_blob_len < HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_SHORT_BUFFER; + + res = get_or_init_ephemeral_ctx(&ephemeral_ctx, &ephemeral_ctx_len); + if (res != TEE_SUCCESS) + return res; + + res = derive_ephemeral_wrapping_key(ephemeral_ctx, ephemeral_ctx_len); + if (res != TEE_SUCCESS) + goto cleanup; + + memcpy(t_unwrap.cmd.unwrap.wkb, in_blob, HWKM_MAX_BLOB_SIZE); + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_unwrap); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_unwrap.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE tpwrap unwrap failed: status=0x%x", + (unsigned int)t_unwrap.rsp.status); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_wrap_tpkey); + if (rc) { + res = hwkm_to_optee(rc); + goto cleanup; + } + + if (t_wrap_tpkey.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE tpwrap export failed: status=0x%x", + (unsigned int)t_wrap_tpkey.rsp.status); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + memcpy(out_blob, t_wrap_tpkey.rsp.wrap.wkb, HWKM_MAX_BLOB_SIZE); + *out_blob_len = HWKM_MAX_BLOB_SIZE; + res = TEE_SUCCESS; + +cleanup: + (void)clear_ephemeral_key(); + (void)hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear_gp1); + memset(t_unwrap.cmd.unwrap.wkb, 0, sizeof(t_unwrap.cmd.unwrap.wkb)); + memset(t_wrap_tpkey.rsp.wrap.wkb, 0, sizeof(t_wrap_tpkey.rsp.wrap.wkb)); + return res; +} + +/* + * program_ice_slave_from_tpkey_blob() - On ICE_SLAVE, import TPKEY-wrapped + * blob into staging slot and derive final key into mapped ICE key slot. + */ +static TEE_Result program_ice_slave_from_tpkey_blob(uint32_t pipe, + const uint8_t *tpkey_blob, + size_t tpkey_blob_len) +{ + uint8_t mapped_slot = 0; + size_t ctx_len = round_up_ctx_len(sizeof(inlinecrypt_ctx) - 1); + struct hwkm_transaction t_clear_mapped = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = 0, + .is_double_key = true, + }, + }, + }; + struct hwkm_transaction t_clear_gp = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = HWKM_ICE_SLAVE_GP_SLOT, + .is_double_key = false, + }, + }, + }; + struct hwkm_transaction t_import = { + .cmd = { + .op = HWKM_OP_KEY_UNWRAP_IMPORT, + .unwrap = { + .dks = HWKM_ICE_SLAVE_GP_SLOT, + .kwk = HWKM_ICE_DEFAULT_TPKEY_SLOT, + }, + }, + }; + struct hwkm_transaction t_kdf_ice = { + .cmd = { + .op = HWKM_OP_SYSTEM_KDF, + .kdf = { + .dks = 0, + .kdk = HWKM_ICE_SLAVE_GP_SLOT, + .policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_XTS, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_GENERIC_KEY, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_ICE_SLAVE, + }, + .bsve = { + .enabled = true, + .km_swc_en = true, + .km_child_key_policy_en = true, + }, + .ctx_len = 0, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + if (!tpkey_blob || tpkey_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (pipe >= ICE_MAX_KEY_IDX) + return TEE_ERROR_BAD_PARAMETERS; + + mapped_slot = HWKM_ICE_MAP_SLOT(pipe); + t_clear_mapped.cmd.clear.dks = mapped_slot; + t_kdf_ice.cmd.kdf.dks = mapped_slot; + + memset(t_kdf_ice.cmd.kdf.ctx, 0, sizeof(t_kdf_ice.cmd.kdf.ctx)); + memcpy(t_kdf_ice.cmd.kdf.ctx, inlinecrypt_ctx, sizeof(inlinecrypt_ctx) - 1); + t_kdf_ice.cmd.kdf.ctx_len = ctx_len; + + memcpy(t_import.cmd.unwrap.wkb, tpkey_blob, HWKM_MAX_BLOB_SIZE); + + rc = hwkm_run_transactions(HWKM_KEY_DEST_ICE_SLAVE, 4, + (struct hwkm_transaction *const[]){ + &t_clear_gp, &t_clear_mapped, + &t_import, &t_kdf_ice }); + + memset(t_import.cmd.unwrap.wkb, 0, sizeof(t_import.cmd.unwrap.wkb)); + memset(t_kdf_ice.cmd.kdf.ctx, 0, sizeof(t_kdf_ice.cmd.kdf.ctx)); + + if (rc) + return hwkm_to_optee(rc); + + if ((t_clear_gp.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_gp.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) || + (t_clear_mapped.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_mapped.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) || + t_import.rsp.status != HWKM_RSP_ERR_SUCCESS || + t_kdf_ice.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE slave prog failed: clear_gp=0x%x clear_dst=0x%x import=0x%x kdf=0x%x", + (unsigned int)t_clear_gp.rsp.status, + (unsigned int)t_clear_mapped.rsp.status, + (unsigned int)t_import.rsp.status, + (unsigned int)t_kdf_ice.rsp.status); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +/* + * program_content_key_using_hwkm() - Top-level helper for ICE key programming. + * + * Chains: + * wrapped blob (ephemeral) -> TPKEY-wrapped blob -> ICE_SLAVE import + KDF. + */ +static TEE_Result program_content_key_using_hwkm(uint32_t pipe, + const uint8_t *wrapped_blob, + size_t wrapped_blob_len) +{ + uint8_t tpkey_blob[HWKM_MAX_BLOB_SIZE] = { }; + size_t tpkey_blob_len = sizeof(tpkey_blob); + TEE_Result res = TEE_ERROR_GENERIC; + + res = export_tpkey_wrapped_blob_from_ephemeral(wrapped_blob, + wrapped_blob_len, + tpkey_blob, + &tpkey_blob_len); + if (res != TEE_SUCCESS) + goto out; + + res = program_ice_slave_from_tpkey_blob(pipe, tpkey_blob, tpkey_blob_len); + +out: + memset(tpkey_blob, 0, sizeof(tpkey_blob)); + return res; +} + +/* + * set_config_ice_key_using_hwkm() - Top-level HWKM + ICE register sequence. + * + * Sequence: + * 1) Disable ICE slot configuration. + * 2) Program key to mapped ICE slot through HWKM helper chain. + * 3) Program fixed data-unit configuration. + * 4) Enable ICE slot configuration. + * + * Fixed UFS settings: + * - CAPIDX = AES-XTS-256 + * - DUSIZE = 4096-byte data unit for UFS + * = 512-byte data unit for eMMC + */ +TEE_Result set_config_ice_key_using_hwkm(uint32_t slot, + const uint8_t *wrapped_blob, + size_t wrapped_blob_len) +{ + const uint32_t cap_index = ICE_CIPHER_MODE_XTS_256; + uint32_t data_unit_size = ICE_DATA_UNIT_SIZE_4096; + TEE_Result res = TEE_ERROR_GENERIC; + vaddr_t base = 0; + + if (!wrapped_blob || wrapped_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (slot >= ICE_MAX_KEY_IDX) + return TEE_ERROR_BAD_PARAMETERS; + + base = (vaddr_t)phys_to_virt(ICE_LUT_KEYS, MEM_AREA_IO_SEC, + ICE_LUT_KEYS_SIZE); + + /* Disable slot first to prevent use of stale key/config. */ + io_write32_off_field(base, ICE_CRYPTOCFG_r_16_OFF(slot), + ICE_CRYPTOCFG_r_16_CFGE_BMSK, + 0x0); + + res = program_content_key_using_hwkm(slot, wrapped_blob, wrapped_blob_len); + if (res != TEE_SUCCESS) + return res; + + /* Program fixed capability index (CAPIDX) and data unit size (DUSIZE). */ + io_write32_off_field(base, ICE_CRYPTOCFG_r_16_OFF(slot), + ICE_CRYPTOCFG_r_16_CAPIDX_BMSK, + cap_index); + dsb(); + + if (ICE_LUT_KEYS == PERIPH_SS_SDC1_SDCC_ICE_LUT_KEYS) + data_unit_size = ICE_DATA_UNIT_SIZE_512; + + io_write32_off_field(base, ICE_CRYPTOCFG_r_16_OFF(slot), + ICE_CRYPTOCFG_r_16_DUSIZE_BMSK, + data_unit_size); + dsb(); + + /* Enable configuration (CFGE=1). */ + io_write32_off_field(base, ICE_CRYPTOCFG_r_16_OFF(slot), + ICE_CRYPTOCFG_r_16_CFGE_BMSK, + 0x1); + dsb(); + + return TEE_SUCCESS; +} + /* * export_hw_wrapped_key() - Rewrap wrapped key blob under an ephemeral key. * diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h index cc79f4643..beb740d18 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.h +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -8,6 +8,10 @@ #include +TEE_Result set_config_ice_key_using_hwkm(uint32_t slot, + const uint8_t *wrapped_blob, + size_t wrapped_blob_len); + TEE_Result export_hw_wrapped_key(const uint8_t *in_blob, size_t in_blob_len, uint8_t *out_blob, size_t *out_blob_len); diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index e106a2395..36e0882d4 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -16,9 +16,8 @@ /* * Controller-agnostic entry points. These wrap the per-controller * implementation so the dispatcher stays independent of the underlying ICE - * block. Only software-key programming exists today; a future hardware-key - * (HWKM) path can be selected here at runtime, based on the key size passed - * to the PTA, dispatching to hwkm_cmd_ice_*() instead of sw_cmd_ice_*(). + * block. A hardware-key (HWKM) path is selected here at runtime, based on + * whether HWKM context is initialized and available. */ static TEE_Result cmd_ice_invalidate_key(uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) @@ -29,7 +28,34 @@ static TEE_Result cmd_ice_invalidate_key(uint32_t param_types, static TEE_Result cmd_ice_set_config_key(uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) { - return sw_cmd_ice_set_config_key(param_types, params); + struct hwkm_drv_ctx *ctx = NULL; + uint32_t slot = 0; + const uint8_t *wrapped_blob = NULL; + size_t wrapped_blob_len = 0; + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + ctx = hwkm_get_context(); + if (!ctx) + return sw_cmd_ice_set_config_key(param_types, params); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + slot = params[0].value.a; + wrapped_blob = params[1].memref.buffer; + wrapped_blob_len = params[1].memref.size; + + if (!wrapped_blob) + return TEE_ERROR_BAD_PARAMETERS; + + if (wrapped_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + return set_config_ice_key_using_hwkm(slot, wrapped_blob, + wrapped_blob_len); } static TEE_Result cmd_ice_generate_key(uint32_t param_types, diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index b82c8e0dc..77fb69b57 100644 --- a/lib/libutee/include/pta_qcom_ice.h +++ b/lib/libutee/include/pta_qcom_ice.h @@ -24,7 +24,9 @@ #define PTA_CMD_ICE_INVALIDATE_KEY 0 /* - * Set ICE key slot with raw key material and full configuration + * Program ICE key slot with material and full configuration + * + * Parameter format when a raw key format is used: * [in] params[0].value.a Key slot index (0..ICE_MAX_KEY_IDX-1) * [in] params[0].value.b Cap index (ice_capability_index_type) * [in] params[1].value.a Data unit size (ice_data_unit_type) @@ -34,6 +36,11 @@ * CBC-128: 16B key * CBC-256: 32B key * [in] params[2].memref.size Total key data size + * + * Parameter format when a wrapped key format is used: + * [in] params[0].value.a Key slot index (0..ICE_MAX_KEY_IDX-1) + * [in] params[1].memref.buffer Wrapped key blob + * [in] params[1].memref.size Wrapped key blob size */ #define PTA_CMD_ICE_SET_CONFIG_KEY 1 From bd1e217eb8173a3366eeb49407c3590d8d84bd1a Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Sat, 1 Aug 2026 21:37:58 +0530 Subject: [PATCH 11/13] pta: qcom: ice: Add support for invalidating ICE slot key Add support for invalidating a previously programmed key from the inline crypto engine's (ICE) key slot via the hardware key manager. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/pta/qcom/ice/hwkm/ice_hwkm.c | 40 +++++++++++++++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 2 ++ core/pta/qcom/ice/ice.c | 15 +++++++++- core/pta/qcom/ice/sw_keys/ice_sw_keys.c | 12 +------- core/pta/qcom/ice/sw_keys/ice_sw_keys.h | 3 +- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c index ce578be4e..a88ffb488 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.c +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -53,6 +53,46 @@ static TEE_Result get_or_init_ephemeral_ctx(const uint8_t **ctx, return TEE_SUCCESS; } +TEE_Result clear_ice_slave_slot_hwkm(uint32_t slot) +{ + struct hwkm_transaction t_clear = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = 0, + .is_double_key = true, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + vaddr_t base = (vaddr_t)phys_to_virt(ICE_LUT_KEYS, MEM_AREA_IO_SEC, + ICE_LUT_KEYS_SIZE); + + if (slot >= ICE_MAX_KEY_IDX) + return TEE_ERROR_BAD_PARAMETERS; + + /* Disable slot first so stale key/config cannot be used. */ + io_write32_off_field(base, ICE_CRYPTOCFG_r_16_OFF(slot), + ICE_CRYPTOCFG_r_16_CFGE_BMSK, + 0x0); + + t_clear.cmd.clear.dks = HWKM_ICE_MAP_SLOT(slot); + + rc = hwkm_run_transaction(HWKM_KEY_DEST_ICE_SLAVE, &t_clear); + if (rc) + return hwkm_to_optee(rc); + + if (t_clear.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + EMSG("ICE invalidate clear failed: slot=%u mapped=%u status=0x%x", + slot, (unsigned int)t_clear.cmd.clear.dks, + (unsigned int)t_clear.rsp.status); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + /* * export_tpkey_wrapped_blob_from_ephemeral() - Unwrap input blob under * ephemeral wrapping key, then re-wrap/export under TPKEY. diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h index beb740d18..4007d08c1 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.h +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -8,6 +8,8 @@ #include +TEE_Result clear_ice_slave_slot_hwkm(uint32_t slot); + TEE_Result set_config_ice_key_using_hwkm(uint32_t slot, const uint8_t *wrapped_blob, size_t wrapped_blob_len); diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index 36e0882d4..685d59903 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -22,7 +22,20 @@ static TEE_Result cmd_ice_invalidate_key(uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) { - return sw_cmd_ice_invalidate_key(param_types, params); + struct hwkm_drv_ctx *ctx = NULL; + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + ctx = hwkm_get_context(); + if (!ctx) + return sw_cmd_ice_invalidate_key(params); + + return clear_ice_slave_slot_hwkm(params[0].value.a); } static TEE_Result cmd_ice_set_config_key(uint32_t param_types, diff --git a/core/pta/qcom/ice/sw_keys/ice_sw_keys.c b/core/pta/qcom/ice/sw_keys/ice_sw_keys.c index 3093427a7..d02d3c5ad 100644 --- a/core/pta/qcom/ice/sw_keys/ice_sw_keys.c +++ b/core/pta/qcom/ice/sw_keys/ice_sw_keys.c @@ -122,22 +122,12 @@ static void ice_configure_slot(vaddr_t base, uint32_t index, * Command: tzbsp_es_invalidate_ice_key port. * [in] params[0].value.a key slot index */ -TEE_Result sw_cmd_ice_invalidate_key(uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]) +TEE_Result sw_cmd_ice_invalidate_key(TEE_Param params[TEE_NUM_PARAMS]) { - uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE, - TEE_PARAM_TYPE_NONE); uint32_t index; vaddr_t base; TEE_Result res; - if (param_types != exp_pt) { - EMSG("ICE invalidate: bad param types 0x%x", param_types); - return TEE_ERROR_BAD_PARAMETERS; - } - index = params[0].value.a; if (index >= ICE_MAX_KEY_IDX) { EMSG("ICE invalidate: invalid slot %u (max %u)", index, diff --git a/core/pta/qcom/ice/sw_keys/ice_sw_keys.h b/core/pta/qcom/ice/sw_keys/ice_sw_keys.h index 8e5ee1b41..6a7d551af 100644 --- a/core/pta/qcom/ice/sw_keys/ice_sw_keys.h +++ b/core/pta/qcom/ice/sw_keys/ice_sw_keys.h @@ -10,8 +10,7 @@ #include "../config.h" -TEE_Result sw_cmd_ice_invalidate_key(uint32_t param_types, - TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result sw_cmd_ice_invalidate_key(TEE_Param params[TEE_NUM_PARAMS]); TEE_Result sw_cmd_ice_set_config_key(uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]); From eff6003e32f1243b0b2402ad82a6a58188e51b00 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Wed, 19 Aug 2026 15:27:41 +0530 Subject: [PATCH 12/13] pta: qcom: ice: Add support for deriving raw secret for ICE Add support for deriving a software secret from the ephemerally wrapped key. The key is unwrapped via HWKM and a CMAC operation is performed on it to generate and return a raw secret to the REE. Assisted-by: Codex:gpt-5.3-codex Signed-off-by: Harshal Dev --- core/pta/qcom/ice/hwkm/ice_hwkm.c | 256 +++++++++++++++++++++++++++++ core/pta/qcom/ice/hwkm/ice_hwkm.h | 7 + core/pta/qcom/ice/ice.c | 35 ++++ lib/libutee/include/pta_qcom_ice.h | 10 ++ 4 files changed, 308 insertions(+) diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.c b/core/pta/qcom/ice/hwkm/ice_hwkm.c index a88ffb488..1f44e2857 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.c +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -20,6 +20,9 @@ #define HWKM_ICE_DEFAULT_TPKEY_SLOT 146U #define HWKM_ICE_MAP_SLOT(pipe) ((uint8_t)(((pipe) * 2U) + 10U)) #define HWKM_CTX_ALIGN_BYTES 8U +#define HWKM_DONT_CARE_START 64U +#define HWKM_DONT_CARE_END 68U +#define HWKM_CMAC_BLOCK_SIZE 16U static inline size_t round_up_ctx_len(size_t len) { @@ -31,6 +34,18 @@ register_phys_mem_pgdir(MEM_AREA_IO_SEC, ICE_LUT_KEYS, ICE_LUT_KEYS_SIZE); static const uint8_t inlinecrypt_ctx[] = "inline encryption key"; +static const uint8_t raw_secret_label[] = { + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, +}; + +static const uint8_t raw_secret_context[] = { + 'r', 'a', 'w', ' ', 's', 'e', 'c', 'r', 'e', 't', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x17, 0x00, 0x80, 0x50, + 0x00, 0x00, 0x00, 0x00, +}; + static uint8_t g_ephemeral_ctx[HWKM_EPHEMERAL_CTX_SIZE] = { 0 }; static bool g_ephemeral_ctx_set = false; @@ -93,6 +108,204 @@ TEE_Result clear_ice_slave_slot_hwkm(uint32_t slot) return TEE_SUCCESS; } +static TEE_Result check_dont_care_bytes(const uint8_t *blob, size_t blob_len) +{ + size_t i = 0; + + if (!blob || blob_len < HWKM_DONT_CARE_END) + return TEE_ERROR_BAD_PARAMETERS; + + for (i = HWKM_DONT_CARE_START; i < HWKM_DONT_CARE_END; i++) { + if (blob[i] != 0U) { + EMSG("ICE raw secret: dont-care byte[%zu]=0x%x", + i, blob[i]); + return TEE_ERROR_SECURITY; + } + } + + return TEE_SUCCESS; +} + +static TEE_Result hwkm_clear_slot(uint8_t slot) +{ + struct hwkm_transaction t_clear = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_CLEAR, + .clear = { + .dks = 0, + .is_double_key = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + t_clear.cmd.clear.dks = slot; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_clear); + if (rc) + return hwkm_to_optee(rc); + + if (t_clear.rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear.rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) + return TEE_ERROR_GENERIC; + + return TEE_SUCCESS; +} + +static TEE_Result hwkm_unwrap_ephemeral_to_slot(const uint8_t *wrapped_blob, + size_t wrapped_blob_len, + uint8_t slot) +{ + const uint8_t *ephemeral_ctx = NULL; + size_t ephemeral_ctx_len = 0; + struct hwkm_transaction t_unwrap = { + .cmd = { + .op = HWKM_OP_KEY_UNWRAP_IMPORT, + .unwrap = { + .dks = 0, + .kwk = HWKM_SLOT_TZ_WRAP_KEY_SLOT, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!wrapped_blob || wrapped_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + res = get_or_init_ephemeral_ctx(&ephemeral_ctx, &ephemeral_ctx_len); + if (res != TEE_SUCCESS) + return res; + + res = derive_ephemeral_wrapping_key(ephemeral_ctx, ephemeral_ctx_len); + if (res != TEE_SUCCESS) + goto out; + + t_unwrap.cmd.unwrap.dks = slot; + memcpy(t_unwrap.cmd.unwrap.wkb, wrapped_blob, HWKM_MAX_BLOB_SIZE); + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_unwrap); + if (rc) { + res = hwkm_to_optee(rc); + goto out; + } + + if (t_unwrap.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE raw secret unwrap failed: status=0x%x", + (unsigned int)t_unwrap.rsp.status); + res = TEE_ERROR_GENERIC; + goto out; + } + + res = TEE_SUCCESS; +out: + (void)clear_ephemeral_key(); + memset(t_unwrap.cmd.unwrap.wkb, 0, sizeof(t_unwrap.cmd.unwrap.wkb)); + return res; +} + +static TEE_Result hwkm_read_slot_key(uint8_t slot, + uint8_t key[HWKM_MAX_KEY_SIZE]) +{ + struct hwkm_transaction t_read = { + .cmd = { + .op = HWKM_OP_KEY_SLOT_RDWR, + .rdwr = { + .slot = 0, + .is_write = false, + }, + }, + }; + int rc = HWKM_ERR_GENERIC; + + if (!key) + return TEE_ERROR_BAD_PARAMETERS; + + t_read.cmd.rdwr.slot = slot; + + rc = hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, &t_read); + if (rc) + return hwkm_to_optee(rc); + + if (t_read.rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("ICE raw secret read failed: status=0x%x", + (unsigned int)t_read.rsp.status); + return TEE_ERROR_GENERIC; + } + + memcpy(key, t_read.rsp.rdwr.key, HWKM_MAX_KEY_SIZE); + return TEE_SUCCESS; +} + +static TEE_Result cmac_counter_kdf(const uint8_t key[HWKM_MAX_KEY_SIZE], + uint8_t *out, size_t out_len, + const uint8_t *label, size_t label_len, + const uint8_t *ctx, size_t ctx_len) +{ + void *mac_ctx = NULL; + uint8_t out_block[HWKM_CMAC_BLOCK_SIZE] = { 0 }; + uint8_t fixed_data[64] = { 0 }; + size_t fixed_len = 0; + size_t copied = 0; + size_t off = 0; + uint32_t counter = 1; + uint32_t be_out_len_bits = TEE_U32_TO_BIG_ENDIAN((uint32_t)(out_len * 8U)); + TEE_Result res = TEE_ERROR_GENERIC; + + if (!key || !out || !out_len || !label || !label_len || !ctx || !ctx_len) + return TEE_ERROR_BAD_PARAMETERS; + + fixed_len = sizeof(uint32_t) + label_len + 1U + ctx_len + sizeof(uint32_t); + if (fixed_len > sizeof(fixed_data)) + return TEE_ERROR_BAD_PARAMETERS; + + off = sizeof(uint32_t); + memcpy(fixed_data + off, label, label_len); + off += label_len; + fixed_data[off++] = 0x00; + memcpy(fixed_data + off, ctx, ctx_len); + off += ctx_len; + memcpy(fixed_data + off, &be_out_len_bits, sizeof(be_out_len_bits)); + + res = crypto_mac_alloc_ctx(&mac_ctx, TEE_ALG_AES_CMAC); + if (res) + goto exit; + + while (copied < out_len) { + uint32_t be_counter = TEE_U32_TO_BIG_ENDIAN(counter); + size_t n = HWKM_CMAC_BLOCK_SIZE; + + memcpy(fixed_data, &be_counter, sizeof(be_counter)); + + res = crypto_mac_init(mac_ctx, key, HWKM_MAX_KEY_SIZE); + if (res) + goto exit; + + res = crypto_mac_update(mac_ctx, fixed_data, fixed_len); + if (res) + goto exit; + + res = crypto_mac_final(mac_ctx, out_block, sizeof(out_block)); + if (res) + goto exit; + + if (n > out_len - copied) + n = out_len - copied; + + memcpy(out + copied, out_block, n); + copied += n; + counter++; + } + + res = TEE_SUCCESS; +exit: + if (mac_ctx) + crypto_mac_free_ctx(mac_ctx); + memset(fixed_data, 0, sizeof(fixed_data)); + memset(out_block, 0, sizeof(out_block)); + return res; +} + /* * export_tpkey_wrapped_blob_from_ephemeral() - Unwrap input blob under * ephemeral wrapping key, then re-wrap/export under TPKEY. @@ -700,3 +913,46 @@ TEE_Result generate_hw_wrapped_key(uint8_t *out_blob, size_t *out_blob_len) memset(t_wrap.rsp.wrap.wkb, 0, sizeof(t_wrap.rsp.wrap.wkb)); return res; } + +TEE_Result get_raw_secret_from_wrapped_key(const uint8_t *wrapped_blob, + size_t wrapped_blob_len, + uint8_t *raw_secret, + size_t *raw_secret_len) +{ + uint8_t derivation_key[HWKM_MAX_KEY_SIZE] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + + if (!wrapped_blob || !raw_secret || !raw_secret_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (wrapped_blob_len != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (*raw_secret_len != ICE_RAW_SECRET_SIZE_BYTES) + return TEE_ERROR_BAD_PARAMETERS; + + res = check_dont_care_bytes(wrapped_blob, wrapped_blob_len); + if (res != TEE_SUCCESS) + return res; + + res = hwkm_unwrap_ephemeral_to_slot(wrapped_blob, wrapped_blob_len, + HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1); + if (res != TEE_SUCCESS) + goto out; + + res = hwkm_read_slot_key(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1, derivation_key); + if (res != TEE_SUCCESS) + goto out; + + res = cmac_counter_kdf(derivation_key, raw_secret, + *raw_secret_len, + raw_secret_label, + sizeof(raw_secret_label), + raw_secret_context, + sizeof(raw_secret_context)); + +out: + (void)hwkm_clear_slot(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1); + memset(derivation_key, 0, sizeof(derivation_key)); + return res; +} diff --git a/core/pta/qcom/ice/hwkm/ice_hwkm.h b/core/pta/qcom/ice/hwkm/ice_hwkm.h index 4007d08c1..8eb50eb19 100644 --- a/core/pta/qcom/ice/hwkm/ice_hwkm.h +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -8,6 +8,8 @@ #include +#define ICE_RAW_SECRET_SIZE_BYTES 32U + TEE_Result clear_ice_slave_slot_hwkm(uint32_t slot); TEE_Result set_config_ice_key_using_hwkm(uint32_t slot, @@ -22,4 +24,9 @@ TEE_Result import_and_wrap_with_hw_key(const uint8_t *in_key, size_t in_key_len, TEE_Result generate_hw_wrapped_key(uint8_t *out_blob, size_t *out_blob_len); +TEE_Result get_raw_secret_from_wrapped_key(const uint8_t *wrapped_blob, + size_t wrapped_blob_len, + uint8_t *raw_secret, + size_t *raw_secret_len); + #endif /* __ICE_HWKM_H */ diff --git a/core/pta/qcom/ice/ice.c b/core/pta/qcom/ice/ice.c index 685d59903..68ccdcff0 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -169,6 +169,39 @@ static TEE_Result cmd_ice_export_key(uint32_t param_types, ¶ms[1].memref.size); } +static TEE_Result cmd_ice_get_raw_secret(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[0].memref.buffer || params[0].memref.size != HWKM_MAX_BLOB_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[1].memref.buffer) { + if (params[1].memref.size == 0) { + params[1].memref.size = ICE_RAW_SECRET_SIZE_BYTES; + return TEE_ERROR_SHORT_BUFFER; + } + return TEE_ERROR_BAD_PARAMETERS; + } + + if (params[1].memref.size != ICE_RAW_SECRET_SIZE_BYTES) { + params[1].memref.size = ICE_RAW_SECRET_SIZE_BYTES; + return TEE_ERROR_SHORT_BUFFER; + } + + return get_raw_secret_from_wrapped_key(params[0].memref.buffer, + params[0].memref.size, + params[1].memref.buffer, + ¶ms[1].memref.size); +} + /* PTA command dispatcher */ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, @@ -186,6 +219,8 @@ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, return cmd_ice_import_key(param_types, params); case PTA_CMD_ICE_EXPORT_KEY: return cmd_ice_export_key(param_types, params); + case PTA_CMD_ICE_GET_RAW_SECRET: + return cmd_ice_get_raw_secret(param_types, params); default: break; } diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index 77fb69b57..47fa4bba9 100644 --- a/lib/libutee/include/pta_qcom_ice.h +++ b/lib/libutee/include/pta_qcom_ice.h @@ -87,4 +87,14 @@ */ #define PTA_CMD_ICE_EXPORT_KEY 4 +/* + * Derive raw secret from an ephemeral wrapped key blob. + * + * [in] params[0].memref.buffer Input wrapped key blob + * [in] params[0].memref.size Input blob size (must be 68 bytes) + * [out] params[1].memref.buffer Output raw secret buffer + * [in/out] params[1].memref.size Must be exactly 32 bytes + */ +#define PTA_CMD_ICE_GET_RAW_SECRET 5 + #endif /* __PTA_QCOM_ICE_H */ From a8f077d2367869f0b4aa15489e08efc443427ac6 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Wed, 16 Sep 2026 15:26:36 +0530 Subject: [PATCH 13/13] plat-qcom: lemans: Enable Inline Crypto Engine Psuedo-TA Enable the in-line crypto engine Psuedo TA for Lemans to allow encryption of storage contents with key programming done via the Hardware key manager. Signed-off-by: Harshal Dev --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index be77ebfab..077519d7c 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -42,3 +42,4 @@ $(call force,CFG_QCOM_RPMH_CLIENT,y) endif CFG_QCOM_STORAGE_UFS ?= y +CFG_ICE_FS_ENC_PTA ?= y