diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index 11fc5ab44..99526e010 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -28,6 +28,21 @@ #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 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 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.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 1cf345223..077519d7c 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -40,3 +40,6 @@ 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 +CFG_ICE_FS_ENC_PTA ?= 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..82bcaafe1 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,21 @@ #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 */ + +#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/drivers/crypto/qcom/hwkm_huk.c b/core/drivers/crypto/qcom/hwkm/huk.c similarity index 99% rename from core/drivers/crypto/qcom/hwkm_huk.c rename to 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.c b/core/drivers/crypto/qcom/hwkm/hwkm.c similarity index 62% rename from core/drivers/crypto/qcom/hwkm.c rename to core/drivers/crypto/qcom/hwkm/hwkm.c index 6d3d7e01c..d2bd3b65f 100644 --- a/core/drivers/crypto/qcom/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 @@ -21,6 +21,8 @@ #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); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_ICE_BASE, HWKM_ICE_SIZE); static_assert(HW_UNIQUE_KEY_LENGTH <= HWKM_MAX_KEY_SIZE); @@ -208,7 +210,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 +219,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 +240,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; @@ -242,6 +250,182 @@ 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_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 = { }; @@ -253,6 +437,18 @@ 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 = 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); diff --git a/core/drivers/crypto/qcom/hwkm/ice.c b/core/drivers/crypto/qcom/hwkm/ice.c new file mode 100644 index 000000000..6e9fdabd3 --- /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_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/include/hwkm_regs.h b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h similarity index 64% rename from core/drivers/crypto/qcom/include/hwkm_regs.h rename to core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h index 297da7786..62fb5524b 100644 --- a/core/drivers/crypto/qcom/include/hwkm_regs.h +++ b/core/drivers/crypto/qcom/hwkm/include/hwkm_regs.h @@ -10,27 +10,59 @@ #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 + +/* 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 - -/* 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 +/* 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 @@ -39,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. @@ -47,8 +84,22 @@ #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 +/* 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 #define HWKM_TZ_TPKEY_RECEIVE_CTL_EN \ @@ -59,8 +110,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 +121,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 +137,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 +149,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 +176,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/sub.mk b/core/drivers/crypto/qcom/hwkm/sub.mk new file mode 100644 index 000000000..6b9f0cdd2 --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm/sub.mk @@ -0,0 +1,20 @@ +# 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 +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 +# 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 90% rename from core/drivers/crypto/qcom/hwkm_transaction.c rename to core/drivers/crypto/qcom/hwkm/transaction.c index 6155630dd..ea1892d83 100644 --- a/core/drivers/crypto/qcom/hwkm_transaction.c +++ b/core/drivers/crypto/qcom/hwkm/transaction.c @@ -4,8 +4,12 @@ */ #include -#include +#include +#include #include +#include +#include +#include #include #include #include @@ -396,89 +400,142 @@ 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 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 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) { + 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); + 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; } @@ -765,6 +822,8 @@ 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; 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 diff --git a/core/drivers/crypto/qcom/include/hwkm.h b/core/include/drivers/hwkm.h similarity index 97% rename from core/drivers/crypto/qcom/include/hwkm.h rename to core/include/drivers/hwkm.h index ff4c84be1..f66ad021d 100644 --- a/core/drivers/crypto/qcom/include/hwkm.h +++ b/core/include/drivers/hwkm.h @@ -17,6 +17,8 @@ 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 { @@ -258,7 +260,9 @@ 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. */ + 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/include/hwkm_errno.h b/core/include/drivers/hwkm_errno.h similarity index 100% rename from core/drivers/crypto/qcom/include/hwkm_errno.h rename to core/include/drivers/hwkm_errno.h 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..d7e9c9b62 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.c @@ -0,0 +1,363 @@ +// 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, + bool implicit_context, + 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; + + 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) + goto out; + + res = crypto_hash_final(hash_ctx, out_ctx, HWKM_MAX_CTX_SIZE); + +out: + crypto_hash_free_ctx(hash_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. + * + * 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, + true, 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..3d7b685aa --- /dev/null +++ b/core/pta/qcom/ice/hwkm/hwkm_derive_keys.h @@ -0,0 +1,20 @@ +/* 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 + +#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); + +#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..1f44e2857 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * 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 +#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) +{ + 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 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; + +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; +} + +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; +} + +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. + */ +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. + * + * 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. + * + * 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. + * + * 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; +} + +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 new file mode 100644 index 000000000..8eb50eb19 --- /dev/null +++ b/core/pta/qcom/ice/hwkm/ice_hwkm.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __ICE_HWKM_H +#define __ICE_HWKM_H + +#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, + 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); + +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); + +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/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..68ccdcff0 100644 --- a/core/pta/qcom/ice/ice.c +++ b/core/pta/qcom/ice/ice.c @@ -8,28 +8,201 @@ #include #include #include +#include +#include "hwkm/ice_hwkm.h" #include "sw_keys/ice_sw_keys.h" /* * 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]) { - 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, 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, + 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); +} + +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); +} + + +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); +} + +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, uint32_t param_types, @@ -40,6 +213,14 @@ 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); + 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); + case PTA_CMD_ICE_GET_RAW_SECRET: + return cmd_ice_get_raw_secret(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/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]); diff --git a/lib/libutee/include/pta_qcom_ice.h b/lib/libutee/include/pta_qcom_ice.h index 7605067f6..47fa4bba9 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,7 +36,65 @@ * 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 +/* + * 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 + +/* + * 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 + +/* + * 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 + +/* + * 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 */