diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index 76d3baab0..ec21b872a 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -22,43 +22,15 @@ CFG_QCOM_GENI_UART_RDY_WAIT_USEC ?= 1000 ta-targets = ta_arm64 supported-ta-targets ?= ta_arm64 -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak lemans)) -include core/arch/arm/cpu/cortex-armv8-0.mk -$(call force,CFG_TEE_CORE_NB_CORE,8) +# Architecture family mapping +HOYA_ARCH_CHIPSETS := kodiak lemans -$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) -CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 - -$(call force,CFG_QCOM_PRNG,y) - -CFG_TZDRAM_START ?= 0x1c300000 -CFG_TEE_RAM_VA_SIZE ?= 0x200000 -CFG_TA_RAM_VA_SIZE ?= 0x1c00000 -CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) -CFG_NUM_THREADS ?= 8 - -# Clock driver -CFG_DRIVERS_CLK ?= y -CFG_DRIVERS_QCOM_CLK ?= y - -# QFPROM Fuse Provisioning: disabled in insecure mode, enabled otherwise -CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) - -# QFPROM dependencies: all enabled/disabled together based on FUSEPROV -_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) -CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) -CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) -CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) -endif - -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) -CFG_QCOM_PAS_PTA ?= y -# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing -$(call force,CFG_QFPROM_MX_RAIL_WA,y) +ifneq (,$(filter $(PLATFORM_FLAVOR),$(HOYA_ARCH_CHIPSETS))) +QCOM_ARCH_FAMILY := hoya +else +$(error Unsupported PLATFORM_FLAVOR: $(PLATFORM_FLAVOR)) endif -ifeq ($(CFG_QCOM_PAS_PTA),y) -# Increase late mappings to cover all PAS resources -CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) -CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe -endif +# Include architecture-specific configuration +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/arch.mk +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/target.mk diff --git a/core/arch/arm/plat-qcom/diag_log.c b/core/arch/arm/plat-qcom/diag_log.c new file mode 100644 index 000000000..0594adc13 --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "diag_log.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, DIAG_BASE, DIAG_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (DIAG_LOG_START_INFO & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (TCSR_BOOT_MISC_DETECT & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); + +#define DIAG_VERSION_V1 1 +#define DIAG_MAGIC_INIT 0x47414944 +#define DIAG_MAGIC_FAILED 0xDEADBEEF +#define DIAG_MAGIC_DLOAD 0xD15AB1ED +#define DLOAD_MAGIC_COOKIE 0x10 + +struct diag_hdr { + uint32_t version; + uint32_t magic; +}; + +struct diag_conf { + uint32_t buf_offset; + uint32_t buf_size; +}; + +struct circ_wo_buf { + uint32_t wrap; + uint32_t head; + uint8_t buf[]; +}; + +struct diag { + struct diag_hdr hdr; + struct diag_conf conf; + struct circ_wo_buf wo_cbuf; +}; + +static struct diag *global_diag; + +static struct diag *get_diag_region(void) +{ + struct diag *diag; + uint32_t *tcsr_reg; + + tcsr_reg = phys_to_virt(TCSR_BOOT_MISC_DETECT, MEM_AREA_IO_SEC, + sizeof(uint32_t)); + diag = phys_to_virt(DIAG_BASE, MEM_AREA_IO_SEC, DIAG_SIZE); + + if (!tcsr_reg || !diag) { + EMSG("DIAG: Failed to map regions"); + return NULL; + } + + if (io_read32((vaddr_t)tcsr_reg) == DLOAD_MAGIC_COOKIE) { + diag->hdr.magic = DIAG_MAGIC_DLOAD; + dsb(); + return NULL; + } + + return diag; +} + +void qcom_diag_log_init(void) +{ + struct diag *diag; + uint32_t *diag_info_addr; + + if (!IS_ENABLED(CFG_QCOM_DIAG_LOG)) { + IMSG("DIAG: Feature not available"); + return; + } + + diag = get_diag_region(); + if (!diag) + return; + + memset(diag, 0, DIAG_SIZE); + + diag->hdr.version = DIAG_VERSION_V1; + diag->hdr.magic = DIAG_MAGIC_INIT; + diag->conf.buf_offset = offsetof(struct diag, wo_cbuf.buf); + diag->conf.buf_size = ROUNDDOWN2(DIAG_SIZE - diag->conf.buf_offset, 16); + + if (diag->conf.buf_offset >= DIAG_SIZE || diag->conf.buf_size == 0) { + EMSG("DIAG: Invalid buffer configuration (offset=%u, size=%u)", + diag->conf.buf_offset, diag->conf.buf_size); + diag->hdr.magic = DIAG_MAGIC_FAILED; + return; + } + + diag_info_addr = phys_to_virt(DIAG_LOG_START_INFO, MEM_AREA_IO_SEC, + 2 * sizeof(uint32_t)); + if (diag_info_addr) { + io_write32((vaddr_t)&diag_info_addr[0], DIAG_BASE); + io_write32((vaddr_t)&diag_info_addr[1], DIAG_SIZE); + } + + dsb(); + + global_diag = diag; +} + +void qcom_diag_log_puts(const char *str) +{ + struct diag *diag = global_diag; + const char *p; + + if (!diag || !str) + return; + + for (p = str; *p; p++) { + diag->wo_cbuf.buf[diag->wo_cbuf.head++] = *p; + if (diag->wo_cbuf.head >= diag->conf.buf_size) { + diag->wo_cbuf.head = 0; + if (diag->wo_cbuf.wrap < UINT32_MAX) + diag->wo_cbuf.wrap++; + } + } +} diff --git a/core/arch/arm/plat-qcom/diag_log.h b/core/arch/arm/plat-qcom/diag_log.h new file mode 100644 index 000000000..c92bb8ebc --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DIAG_LOG_H +#define __DIAG_LOG_H + +#include +#include +#include +#include + +#define IMEM_DIAG_OFFSET UL(0x720) +#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) +#define TCSR_BOOT_MISC_DETECT UL(0x1FD3000) +#define DIAG_SIZE UL(0x3000) +#define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) + +void qcom_diag_log_init(void); +void qcom_diag_log_puts(const char *str); + +#endif /* __DIAG_LOG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/arch.mk b/core/arch/arm/plat-qcom/hoya/arch.mk new file mode 100644 index 000000000..9ee039dde --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch.mk @@ -0,0 +1,15 @@ +# HOYA architecture configuration + +include core/arch/arm/cpu/cortex-armv8-0.mk +$(call force,CFG_TEE_CORE_NB_CORE,8) + +$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) +CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 + +$(call force,CFG_QCOM_PRNG,y) + +CFG_TZDRAM_START ?= 0x1c300000 +CFG_TEE_RAM_VA_SIZE ?= 0x200000 +CFG_TA_RAM_VA_SIZE ?= 0x1c00000 +CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) +CFG_NUM_THREADS ?= 8 diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h new file mode 100644 index 000000000..b0459613b --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef ARCH_CONFIG_H +#define ARCH_CONFIG_H + +#define GICD_BASE UL(0x17a00000) +#define GICR_BASE UL(0x17a60000) + +#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) +#define SEC_PRNG_REG_BASE UL(0x010D1000) + +#define AOP_MSG_RAM_BASE UL(0x0C300000) +#define AOP_MSG_RAM_SIZE UL(0x00100000) + +#define RPMH_BASE_ADDR UL(0x18200000) +#define RPMH_RSC_SIZE UL(0x40000) + +#define SECURITY_CONTROL_BASE UL(0x00780000) +#define SECURITY_CONTROL_SIZE UL(0x10000) + +#define TCSR_MUTEX_BASE UL(0x01F40000) +#define TCSR_MUTEX_SIZE UL(0x40000) + +#endif /* ARCH_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target.mk b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk new file mode 100644 index 000000000..b91c1b014 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk @@ -0,0 +1,21 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) + +CFG_QCOM_PAS_PTA ?= y +# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing +$(call force,CFG_QFPROM_MX_RAIL_WA,y) + +ifeq ($(CFG_QCOM_PAS_PTA),y) +# Increase late mappings to cover all PAS resources +CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) +CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +endif diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h new file mode 100644 index 000000000..57fca614b --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x100000) +#define GCC_SIZE UL(0x100000) + +#define AOP_CMD_DB_BASE UL(0x80860000) +#define AOP_CMD_DB_SIZE UL(0x00020000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x80000000) +#define DRAM1_BASE ULL(0x100000000) +#define DRAM1_SIZE ULL(0x100000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0x994000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x19000) + +#define WPSS_BASE UL(0x8a00000) +#define WPSS_SIZE UL(0x200000) +#define TURING_BASE UL(0x09800000) +#define TURING_SIZE ULL(0x00e00000) +#define LPASS_BASE UL(0x02c00000) +#define LPASS_SIZE ULL(0x01080000) +#define IRIS_BASE UL(0x0aa00000) +#define IRIS_SIZE ULL(0x00200000) + +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_VENUS 9 +#define PAS_ID_TURING 18 + +#define HWKM_MASTER_BASE UL(0x010c0000) +#define HWKM_MASTER_SIZE UL(0x00020000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk new file mode 100644 index 000000000..b26021c1e --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -0,0 +1,13 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) + +CFG_QCOM_HWKM ?= 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 new file mode 100644 index 000000000..1808a56c3 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x110000) +#define GCC_SIZE UL(0x100000) + +#define AOP_CMD_DB_BASE UL(0x90860000) +#define AOP_CMD_DB_SIZE UL(0x00020000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x380000000) +#define DRAM1_BASE ULL(0x800000000) +#define DRAM1_SIZE ULL(0x800000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0xa8c000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) + +#define HWKM_MASTER_BASE UL(0x010c0000) +#define HWKM_MASTER_SIZE UL(0x00020000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/main.c b/core/arch/arm/plat-qcom/main.c index 3ed3670c2..b620c150e 100644 --- a/core/arch/arm/plat-qcom/main.c +++ b/core/arch/arm/plat-qcom/main.c @@ -11,6 +11,8 @@ #include #include +#include "diag_log.h" + /* * Register the physical memory area for peripherals etc. Here we are * registering the UART console. @@ -28,6 +30,16 @@ register_ddr(DRAM1_BASE, DRAM1_SIZE); static struct qcom_geni_uart_data console_data; +void plat_trace_ext_puts(const char *str) +{ + qcom_diag_log_puts(str); +} + +void plat_trace_init(void) +{ + qcom_diag_log_init(); +} + void plat_console_init(void) { qcom_geni_uart_init(&console_data, GENI_UART_REG_BASE); diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 34b59cdd0..bce35cdfd 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -8,6 +8,8 @@ #define PLATFORM_CONFIG_H #include +#include +#include /* Make stacks aligned to data cache line length */ #define STACK_ALIGNMENT 64 @@ -15,93 +17,4 @@ #define MAX_XLAT_TABLES (40 + (CFG_RESERVED_VASPACE_SIZE) / \ (CORE_MMU_PGDIR_SIZE) + 5) -#if defined(PLATFORM_FLAVOR_kodiak) || defined(PLATFORM_FLAVOR_lemans) -/* GIC related constants */ -#define GICD_BASE UL(0x17a00000) -#define GICR_BASE UL(0x17a60000) - -#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) -#define SEC_PRNG_REG_BASE UL(0x010D1000) - -/* AOP MSG RAM */ -#define AOP_MSG_RAM_BASE UL(0x0C300000) -#define AOP_MSG_RAM_SIZE UL(0x00100000) - -/* RPMH RSC */ -#define RPMH_BASE_ADDR UL(0x18200000) -#define RPMH_RSC_SIZE UL(0x40000) - -/* QFPROM and Security Control */ -#define SECURITY_CONTROL_BASE UL(0x00780000) -#define SECURITY_CONTROL_SIZE UL(0x10000) - -/* TCSR Hardware Mutex */ -#define TCSR_MUTEX_BASE UL(0x01F40000) -#define TCSR_MUTEX_SIZE UL(0x40000) -#endif - -#if defined(PLATFORM_FLAVOR_kodiak) -/* Clock controller */ -#define GCC_BASE UL(0x100000) -#define GCC_SIZE UL(0x100000) - -/* AOP CMD_DB */ -#define AOP_CMD_DB_BASE UL(0x80860000) -#define AOP_CMD_DB_SIZE UL(0x00020000) - -/* QFPROM Fuse Provisioning */ -#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) -#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) - -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x80000000) -#define DRAM1_BASE ULL(0x100000000) -#define DRAM1_SIZE ULL(0x100000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0x994000) - -#define WPSS_BASE UL(0x8a00000) -#define WPSS_SIZE UL(0x200000) -#define TURING_BASE UL(0x09800000) -#define TURING_SIZE ULL(0x00e00000) -#define LPASS_BASE UL(0x02c00000) -#define LPASS_SIZE ULL(0x01080000) -#define IRIS_BASE UL(0x0aa00000) -#define IRIS_SIZE ULL(0x00200000) -#endif - -#if defined(PLATFORM_FLAVOR_lemans) -/* Clock controller */ -#define GCC_BASE UL(0x110000) -#define GCC_SIZE UL(0x100000) - -/* AOP CMD_DB */ -#define AOP_CMD_DB_BASE UL(0x90860000) -#define AOP_CMD_DB_SIZE UL(0x00020000) - -/* QFPROM Fuse Provisioning */ -#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) -#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) - -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x380000000) -#define DRAM1_BASE ULL(0x800000000) -#define DRAM1_SIZE ULL(0x800000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0xa8c000) -#endif - -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_VENUS 9 -#define PAS_ID_TURING 18 - #endif /*PLATFORM_CONFIG_H*/ diff --git a/core/arch/arm/plat-qcom/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 87297fa96..e9faeb6f9 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,5 +1,8 @@ global-incdirs-y += . +global-incdirs-y += $(QCOM_ARCH_FAMILY) +global-incdirs-y += $(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR) srcs-y += main.c +srcs-y += diag_log.c $(eval $(call cfg-depends-all,CFG_QFPROM_PROGRAMMING,CFG_QCOM_QFPROM)) subdirs-$(CFG_QFPROM_PROGRAMMING) += provision diff --git a/core/drivers/crypto/hwkm/hwkm.c b/core/drivers/crypto/hwkm/hwkm.c new file mode 100644 index 000000000..ae22e70d6 --- /dev/null +++ b/core/drivers/crypto/hwkm/hwkm.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clock_group_qcom.h" + +static_assert(HW_UNIQUE_KEY_LENGTH <= HWKM_MAX_KEY_SIZE); + +#define HWKM_HUK_CTX "OPTEE_HUK_HWKM_V1" +#define HWKM_HUK_KDK_SLOT HWKM_SLOT_TZ_UKDK_L2 +#define HWKM_SCRATCH_SLOT_A HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1 +#define HWKM_SCRATCH_SLOT_B HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2 + +/* + * BANK0 BBAC bitmap for the slots managed by this driver. + * + * BBAC_0 covers slots 0..31. Slots not present in this bitmap remain + * inaccessible from BANK0. + */ +#define HWKM_BBAC_BIT(_slot) BIT((_slot) % 32) + +#define HWKM_BANK0_BBAC_0 \ + (HWKM_BBAC_BIT(HWKM_SLOT_NKDK_L1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PKDK_L1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_SKDK_L1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_UKDK_L1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_NKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_PKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_UKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TPKEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TPKEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SWAP_KEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SWAP_KEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_WRAP_KEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_WRAP_KEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1_ODD) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2_ODD) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_MIXING_KEY_SLOT)) + +static struct hwkm_drv_ctx g_hwkm_ctx = { + .hwkm_lock = MUTEX_INITIALIZER, +}; + +struct hwkm_drv_ctx *hwkm_get_context(void) +{ + return g_hwkm_ctx.initialized ? &g_hwkm_ctx : NULL; +} + +/** + * hwkm_transaction_alloc() - Allocate and zero one HWKM transaction. + * + * Return: zero-initialized transaction on success, or NULL on failure. + */ +struct hwkm_transaction *hwkm_transaction_alloc(void) +{ + return calloc(1, sizeof(struct hwkm_transaction)); +} + +/** + * hwkm_transaction_free() - Wipe and free one HWKM transaction. + * @t: Transaction to release. + */ +void hwkm_transaction_free(struct hwkm_transaction *t) +{ + if (t) { + memzero_explicit(t, sizeof(*t)); + free(t); + } +} + +/** + * hwkm_setup_kdf() - Populate a KDF transaction. + * @t: Transaction to fill. + * @dks: Destination key slot. + * @kdk: Source KDK slot. + * @policy: Key policy for the derived key. + * @bsve: Binding state vector. + * @ctx: Software context bytes. + * @ctx_len: Length of @ctx in bytes. + */ +static void hwkm_setup_kdf(struct hwkm_transaction *t, + uint8_t dks, uint8_t kdk, + const struct hwkm_key_policy *policy, + const struct hwkm_bsve *bsve, + const char *ctx, size_t ctx_len) +{ + t->cmd.op = HWKM_OP_SYSTEM_KDF; + t->cmd.kdf.dks = dks; + t->cmd.kdf.kdk = kdk; + t->cmd.kdf.policy = *policy; + t->cmd.kdf.bsve = *bsve; + t->cmd.kdf.ctx_len = ctx_len; + memcpy(t->cmd.kdf.ctx, ctx, ctx_len); +} + +/** + * tee_otp_get_hw_unique_key() - Return the platform hardware unique key. + * @hwkey: Output buffer for the derived hardware unique key. + * + * The HUK is derived once through HWKM and then cached in the driver + * context. Subsequent calls return the cached value. + * + * Slow path: + * Phase 1: Clear scratch slots A and B, derive the L3 KDK into slot A, + * then derive the L4 HUK from slot A into slot B. + * Phase 2: Read the L4 HUK from slot B, then clear both scratch slots. + * + * Return: TEE_SUCCESS on success, or a TEE_ERROR_* code on failure. + */ +TEE_Result tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey) +{ + struct hwkm_drv_ctx *drv; + struct hwkm_transaction *t_clear_a = NULL; + struct hwkm_transaction *t_clear_b = NULL; + struct hwkm_transaction *t_kdf_l3 = NULL; + struct hwkm_transaction *t_kdf_l4 = NULL; + struct hwkm_transaction *t_read = NULL; + /* L3: intermediate KDK derived from the root UKDK. */ + const struct hwkm_key_policy l3_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .dec_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, + }; + /* L4: final HUK, software-readable generic key. */ + const struct hwkm_key_policy l4_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_GENERIC_KEY, + .kdf_depth = 0, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_bsve huk_bsve = { + .enabled = true, + .km_swc_en = true, + .km_apps_secure_en = true, + }; + TEE_Result res = TEE_SUCCESS; + int rc = 0; + + if (!hwkey) + return TEE_ERROR_BAD_PARAMETERS; + + drv = hwkm_get_context(); + if (!drv) + return TEE_ERROR_NOT_SUPPORTED; + + mutex_lock(&drv->hwkm_lock); + /* Fast path: return the cached HUK if it is already available. */ + if (drv->hwkm_huk_ready) { + memcpy(hwkey->data, drv->hwkm_huk, HW_UNIQUE_KEY_LENGTH); + goto out; + } + + t_clear_a = hwkm_transaction_alloc(); + t_clear_b = hwkm_transaction_alloc(); + t_kdf_l3 = hwkm_transaction_alloc(); + t_kdf_l4 = hwkm_transaction_alloc(); + t_read = hwkm_transaction_alloc(); + if (!t_clear_a || !t_clear_b || !t_kdf_l3 || !t_kdf_l4 || !t_read) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto out; + } + + t_clear_a->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear_a->cmd.clear.dks = HWKM_SCRATCH_SLOT_A; + t_clear_a->cmd.clear.is_double_key = false; + + t_clear_b->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear_b->cmd.clear.dks = HWKM_SCRATCH_SLOT_B; + t_clear_b->cmd.clear.is_double_key = false; + + /* Phase 1: clear A and B, derive L3 KDK into A, derive L4 HUK into B. */ + + hwkm_setup_kdf(t_kdf_l3, HWKM_SCRATCH_SLOT_A, HWKM_HUK_KDK_SLOT, + &l3_policy, &huk_bsve, HWKM_HUK_CTX, + sizeof(HWKM_HUK_CTX) - 1); + hwkm_setup_kdf(t_kdf_l4, HWKM_SCRATCH_SLOT_B, HWKM_SCRATCH_SLOT_A, + &l4_policy, &huk_bsve, HWKM_HUK_CTX, + sizeof(HWKM_HUK_CTX) - 1); + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 4, + (struct hwkm_transaction *const[]){ + t_clear_a, t_clear_b, t_kdf_l3, t_kdf_l4 }); + if (rc) { + res = hwkm_to_tee(rc); + goto out_clear; + } + + /* Check for transactions status. */ + + if (t_clear_a->rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_a->rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + res = TEE_ERROR_GENERIC; + goto out; + } + + if (t_clear_b->rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_b->rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + res = TEE_ERROR_GENERIC; + goto out; + } + + if (t_kdf_l3->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: SYSTEM_KDF L3 failed: %#"PRIx32, + t_kdf_l3->rsp.status); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_kdf_l4->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: SYSTEM_KDF L4 failed: %#"PRIx32, + t_kdf_l4->rsp.status); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + /* Phase 2: read L4 HUK from slot B, clear both scratch slots. */ + + t_read->cmd.op = HWKM_OP_KEY_SLOT_RDWR; + t_read->cmd.rdwr.slot = HWKM_SCRATCH_SLOT_B; + t_read->cmd.rdwr.is_write = false; + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 3, + (struct hwkm_transaction *const[]){ + t_read, t_clear_a, t_clear_b }); + if (rc) { + res = hwkm_to_tee(rc); + goto out_clear; + } + + /* Check for transactions status. */ + + if (t_read->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: KEY_SLOT_RDWR read failed: %#"PRIx32, + t_read->rsp.status); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_clear_a->rsp.status != HWKM_RSP_ERR_SUCCESS || + t_clear_b->rsp.status != HWKM_RSP_ERR_SUCCESS) { + res = TEE_ERROR_GENERIC; + goto out; + } + + memcpy(drv->hwkm_huk, t_read->rsp.rdwr.key, HW_UNIQUE_KEY_LENGTH); + drv->hwkm_huk_ready = true; + memcpy(hwkey->data, drv->hwkm_huk, HW_UNIQUE_KEY_LENGTH); + +out_clear: + if (res) { + t_clear_a->hdl = NULL; + t_clear_b->hdl = NULL; + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear_a); + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear_b); + } + +out: + hwkm_transaction_free(t_clear_a); + hwkm_transaction_free(t_clear_b); + hwkm_transaction_free(t_kdf_l3); + hwkm_transaction_free(t_kdf_l4); + hwkm_transaction_free(t_read); + + mutex_unlock(&drv->hwkm_lock); + + return res; +} + +/** + * hwkm_map() - Map the KM_MASTER MMIO window. + * + * Return: mapped virtual base on success, or 0 on failure. + */ +static vaddr_t hwkm_map(void) +{ + void *va; + + va = core_mmu_add_mapping(MEM_AREA_IO_SEC, HWKM_MASTER_BASE, + HWKM_MASTER_SIZE); + if (!va) + return 0; + + return (vaddr_t)va; +} + +/** + * hwkm_init() - Initialize the HWKM instance. + * + * Initialization sequence: + * 1. Enable the four GCC clock branches required by the HWKM Master. + * 2. Map the master MMIO window. + * 3. Check the hardware self-test status. + * 4. Disable packet CRC checking. + * 5. Program BANK0 BBAC for the slots managed by this driver. + * 6. Clear the documented spurious RSP_FIFO_FULL sticky bit. + * 7. Publish the mapped base and mark the driver initialized. + * + * Return: TEE_SUCCESS on success, or a TEE_ERROR_* code on failure. + */ +TEE_Result hwkm_init(void) +{ + uint32_t status; + vaddr_t base; + + if (g_hwkm_ctx.initialized) + return TEE_SUCCESS; + + base = hwkm_map(); + if (!base) + return TEE_ERROR_GENERIC; + + /* Check the hardware self-test status. */ + status = HWKM_REG_READ(base, HWKM_TZ_KM_STATUS); + if (status & (HWKM_TZ_KM_STATUS_BIST_ERROR | + HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR)) + return TEE_ERROR_GENERIC; + + /* Disable CRC checking on command packets. */ + hwkm_reg_set_field(base, HWKM_TZ_KM_CTL, + HWKM_TZ_KM_CTL_CRC_CHECK_EN, + HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT, 0); + + HWKM_REG_WRITE(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_0, + HWKM_BANK0_BBAC_0); + HWKM_REG_WRITE(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_1, 0); + HWKM_REG_WRITE(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_2, 0); + HWKM_REG_WRITE(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_3, 0); + HWKM_REG_WRITE(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_4, 0); + + /* + * Clear the spurious RSP_FIFO_FULL sticky bit. + * HW errata QCTDD06252768: RSP_FIFO_FULL may be set after reset even + * though the FIFO is empty. Write 1 to clear it unconditionally so + * it does not interfere with CMD_DONE polling in + * master_run_transaction(). + */ + HWKM_REG_WRITE(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL); + + g_hwkm_ctx.base = base; + g_hwkm_ctx.initialized = true; + + return TEE_SUCCESS; +} + +static TEE_Result hwkm_driver_init(void) +{ + struct tee_hw_unique_key huk = { }; + TEE_Result res; + + res = hwkm_init(); + if (res) { + EMSG("hwkm: 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); + panic("HWKM HUK derivation failure"); + } + + memset(&huk, 0, sizeof(huk)); + + return TEE_SUCCESS; +} + +driver_init(hwkm_driver_init); diff --git a/core/drivers/crypto/hwkm/hwkm_transaction.c b/core/drivers/crypto/hwkm/hwkm_transaction.c new file mode 100644 index 000000000..ae0a1acf2 --- /dev/null +++ b/core/drivers/crypto/hwkm/hwkm_transaction.c @@ -0,0 +1,1070 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include + +/* + * Command packet format: + * Each command starts with a CMD[0] operation-info word that encodes the + * opcode, slot indices, flags, and total packet length. Subsequent words + * carry the policy, BSVE, software context, and a CRC word (always 0 while + * CRC checking is disabled). + */ + +static_assert(HWKM_OP_NIST_KEYGEN == 0x0); +static_assert(HWKM_OP_SYSTEM_KDF == 0x1); +static_assert(HWKM_OP_KEY_WRAP_EXPORT == 0x3); +static_assert(HWKM_OP_KEY_UNWRAP_IMPORT == 0x4); +static_assert(HWKM_OP_KEY_SLOT_CLEAR == 0x5); +static_assert(HWKM_OP_KEY_SLOT_RDWR == 0x6); +static_assert(HWKM_OP_SET_TPKEY == 0x7); + +struct hwkm_operation_info { + unsigned op:4; /* [3:0] opcode. */ + unsigned irq_en:1; /* [4] 0 = polling (always used by this driver). */ + unsigned slot1_desc:8; /* [12:5] DKS or SKS depending on opcode. */ + unsigned slot2_desc:8; /* [20:13] KDK, KWK, or 0. */ + unsigned op_flag:1; /* [21] flag depending on opcode. */ + unsigned context_len:5; /* [26:22] Software context length (KDF only). */ + unsigned len:5; /* [31:27] total cmd words. */ +} __packed; + +#define HWKM_OPERATION_INFO_WORDS 1 + +/* Command and response formats. */ + +/* Shared macros: */ + +#define HWKM_KEY_POLICY_WORDS 2 +#define HWKM_BSVE_WORDS 3 +#define HWKM_KEY_BLOB_WORDS 17 +#define HWKM_SOFTWARE_CONTEXT_WORDS 16 +#define HWKM_KEY_RDWR_WORDS 8 + +static_assert(HWKM_MAX_BLOB_SIZE == HWKM_KEY_BLOB_WORDS * sizeof(uint32_t)); +static_assert(HWKM_MAX_CTX_SIZE == + HWKM_SOFTWARE_CONTEXT_WORDS * sizeof(uint32_t)); +static_assert(HWKM_MAX_KEY_SIZE == HWKM_KEY_RDWR_WORDS * sizeof(uint32_t)); + +/* + * HWKM_OP_NIST_KEYGEN - Generate a fresh key into a slot via hardware PRNG. + * + * CMD[0] = Operation info. + * CMD[1:2] = Policy. + * CMD[3] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_NIST_KEYGEN_CMD_WORDS 4 /* CMD[0] + policy + CRC. */ +#define HWKM_OP_NIST_KEYGEN_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX 1 + +/* + * HWKM_OP_SYSTEM_KDF - Derive a child key from a KDK slot via hardware KDF. + * + * CMD[0] = Operation info. + * CMD[1:2] = Policy. + * CMD[3] = BSVE[0] if bsve is enabled, 0 otherwise. + * CMD[4:5] = BSVE[1:2] only if bsve is enabled. + * CMD[n:n + m] = Software Context (SC), where n = 4 or 6 depending on BSVE, + * and m is SC length. + * CMD[n + m + 1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_SYSTEM_KDF_CMD_WORDS 4 /* CMD[0] + policy + CRC. */ +#define HWKM_OP_SYSTEM_KDF_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_WRAP_EXPORT - Encrypt a slot under a KWK/KSK and return the blob. + * + * CMD[0] = Operation info. + * CMD[1:3] = BSVE. + * CMD[4] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[1] = Error status. + * RSP[HWKM_OP_KEY_WRAP_EXPORT_RSP_WRAPPED_KEY_IDX: + * HWKM_OP_KEY_WRAP_EXPORT_RSP_WRAPPED_KEY_IDX + HWKM_KEY_BLOB_WORDS - 1] + * = Wrapped Key Blob. + */ +#define HWKM_OP_KEY_WRAP_EXPORT_CMD_WORDS 5 /* CMD[0] + BSVE + CRC. */ +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_WORDS 19 /* RSP[0:1] + blob. */ +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_ERR_IDX 1 +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_WRAPPED_KEY_IDX 2 + +/* + * HWKM_OP_KEY_UNWRAP_IMPORT - Decrypt a wrapped blob and write the key + * into a slot. + * + * CMD[0] = Operation info. + * CMD[1:17] = Wrapped Key Blob. + * CMD[18] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_KEY_UNWRAP_IMPORT_CMD_WORDS 19 /* CMD[0] + blob + CRC. */ +#define HWKM_OP_KEY_UNWRAP_IMPORT_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_SLOT_CLEAR - Zeroize a slot and invalidate its policy word. + * + * CMD[0] = Operation info. + * CMD[1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX] = Error status + */ +#define HWKM_OP_KEY_SLOT_CLEAR_CMD_WORDS 2 /* CMD[0] + CRC. */ +#define HWKM_OP_KEY_SLOT_CLEAR_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_SLOT_RDWR - Read or write raw key material for a SW_KEY slot. + * + * CMD[0] = Operation info. + * CMD[1:2] = Written policy (0 if read). + * CMD[3:10] = Written key value (0 if read). + * CMD[11] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX] = Error status. + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX: + * HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX + HWKM_KEY_POLICY_WORDS - 1] + * = Read policy (0 if write). + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX: + * HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX + HWKM_KEY_RDWR_WORDS -1] + * = Read key value (0 if write). + */ +#define HWKM_OP_KEY_SLOT_RDWR_CMD_WORDS 12 /* CMD[0] + policy + key + CRC. */ +#define HWKM_OP_KEY_SLOT_RDWR_RSP_WORDS 12 /* RSP[0:1] + policy + key. */ +#define HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX 1 +#define HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX 2 +#define HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX 4 + +/* + * HWKM_OP_SET_TPKEY - Install a slot as the active transport protection key. + * + * CMD[0] = Operation info. + * CMD[1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_SET_TPKEY_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_SET_TPKEY_CMD_WORDS 2 /* CMD[0] + CRC. */ +#define HWKM_OP_SET_TPKEY_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_SET_TPKEY_RSP_ERR_IDX 1 + +/* PACK and UNPACK. */ + +/* Size HWKM_KEY_POLICY_WORDS words. */ +struct hwkm_hw_key_policy { + unsigned dbg_qfprom_key_rd_iv_sel:1; /* [0] */ + unsigned reserved0:1; /* [1] */ + unsigned wrap_with_tpkey:1; /* [2] */ + unsigned hw_destination:4; /* [3:6] */ + unsigned reserved1:1; /* [7] */ + unsigned propagate_sec_level_to_child_keys:1; /* [8] */ + unsigned security_level:2; /* [9:10] */ + unsigned swap_export_allowed:1; /* [11] */ + unsigned wrap_export_allowed:1; /* [12] */ + unsigned key_type:3; /* [13:15] */ + unsigned kdf_depth:8; /* [16:23] */ + unsigned decrypt_allowed:1; /* [24] */ + unsigned encrypt_allowed:1; /* [25] */ + unsigned alg_allowed:6; /* [26:31] */ + unsigned key_management_by_tz_secure_allowed:1; /* [32] */ + unsigned key_management_by_nonsecure_allowed:1; /* [33] */ + unsigned key_management_by_modem_allowed:1; /* [34] */ + unsigned key_management_by_spu_allowed:1; /* [35] */ + unsigned reserved2:28; /* [36:63] */ +} __packed; + +/* Size HWKM_BSVE_WORDS words. */ +struct hwkm_hw_kdf_bsve { + unsigned mks:8; /* [7:0] */ + unsigned key_policy_version_en:1; /* [8] */ + unsigned apps_secure_en:1; /* [9] */ + unsigned msa_secure_en:1; /* [10] */ + unsigned lcm_fuse_row_en:1; /* [11] */ + unsigned boot_stage_otp_en:1; /* [12] */ + unsigned swc_en:1; /* [13] */ + uint64_t fuse_region_sha_digest_en:64; /* [77:14] */ + unsigned child_key_policy_en:1; /* [78] */ + unsigned mks_en:1; /* [79] */ + unsigned reserved:16; /* [95:80] */ +} __packed; + +/* Size HWKM_BSVE_WORDS words. */ +struct hwkm_hw_wrapping_bsve { + unsigned key_policy_version_en:1; /* [0] */ + unsigned apps_secure_en:1; /* [1] */ + unsigned msa_secure_en:1; /* [2] */ + unsigned lcm_fuse_row_en:1; /* [3] */ + unsigned boot_stage_otp_en:1; /* [4] */ + unsigned reserved0:27; /* [31:5] */ + unsigned reserved1:32; /* [63:32] */ + unsigned reserved2:32; /* [95:64] */ +} __packed; + +/** + * hwkm_pack_key_policy() - Pack a software key policy into hardware format. + * @dst: Destination hardware policy structure. + * @src: Source software policy. + * + * Fill @dst from @src using the HWKM hardware policy encoding. + * Fields not represented in struct hwkm_key_policy are cleared. + */ +static void hwkm_pack_key_policy(struct hwkm_hw_key_policy *dst, + const struct hwkm_key_policy *src) +{ + memset(dst, 0, sizeof(*dst)); + + dst->wrap_with_tpkey = src->wrap_with_tpkey_allowed ? 1:0; + dst->hw_destination = (unsigned)src->hw_destination; + dst->security_level = (unsigned)src->security_lvl; + dst->swap_export_allowed = src->swap_export_allowed ? 1:0; + dst->wrap_export_allowed = src->wrap_export_allowed ? 1:0; + dst->key_type = (unsigned)src->key_type; + dst->kdf_depth = src->kdf_depth; + dst->decrypt_allowed = src->dec_allowed ? 1:0; + dst->encrypt_allowed = src->enc_allowed ? 1:0; + dst->alg_allowed = (unsigned)src->alg_allowed; + dst->key_management_by_tz_secure_allowed = src->km_by_tz_allowed ? 1:0; + dst->key_management_by_nonsecure_allowed = src->km_by_nsec_allowed ? 1:0; + dst->key_management_by_modem_allowed = src->km_by_modem_allowed ? 1:0; + dst->key_management_by_spu_allowed = src->km_by_spu_allowed ? 1:0; +} + +/** + * hwkm_unpack_key_policy() - Unpack a hardware key policy into software form. + * @dst: Destination software policy. + * @src: Source hardware policy structure. + * + * Decode @src from the HWKM hardware policy format into @dst. + * Fields not represented in struct hwkm_key_policy are ignored. + */ +static void hwkm_unpack_key_policy(struct hwkm_key_policy *dst, + const struct hwkm_hw_key_policy *src) +{ + memset(dst, 0, sizeof(*dst)); + + dst->wrap_with_tpkey_allowed = !!src->wrap_with_tpkey; + dst->hw_destination = (enum hwkm_key_destination)src->hw_destination; + dst->security_lvl = (enum hwkm_key_security_lvl)src->security_level; + dst->swap_export_allowed = !!src->swap_export_allowed; + dst->wrap_export_allowed = !!src->wrap_export_allowed; + dst->key_type = (enum hwkm_key_type)src->key_type; + dst->kdf_depth = (uint8_t)src->kdf_depth; + dst->dec_allowed = !!src->decrypt_allowed; + dst->enc_allowed = !!src->encrypt_allowed; + dst->alg_allowed = (enum hwkm_algo)src->alg_allowed; + dst->km_by_tz_allowed = !!src->key_management_by_tz_secure_allowed; + dst->km_by_nsec_allowed = !!src->key_management_by_nonsecure_allowed; + dst->km_by_modem_allowed = !!src->key_management_by_modem_allowed; + dst->km_by_spu_allowed = !!src->key_management_by_spu_allowed; +} + +/** + * hwkm_pack_wrapping_bsve() - Pack wrapping BSVE settings into hardware format. + * @dst: Destination hardware BSVE structure. + * @src: Source BSVE settings. + * + * Fill @dst with the wrapping-BSVE encoding derived from @src. If BSVE is + * disabled, @dst is cleared and no fields are set. + */ +static void hwkm_pack_wrapping_bsve(struct hwkm_hw_wrapping_bsve *dst, + const struct hwkm_bsve *src) +{ + memset(dst, 0, sizeof(*dst)); + + if (!src->enabled) + return; + + dst->key_policy_version_en = src->km_key_policy_ver_en ? 1:0; + dst->apps_secure_en = src->km_apps_secure_en ? 1:0; + dst->msa_secure_en = src->km_msa_secure_en ? 1:0; + dst->lcm_fuse_row_en = src->km_lcm_fuse_en ? 1:0; + dst->boot_stage_otp_en = src->km_boot_stage_otp_en ? 1:0; +} + +/** + * hwkm_pack_kdf_bsve() - Pack KDF BSVE settings into hardware format. + * @dst: Destination hardware BSVE structure. + * @src: Source BSVE settings. + * @mks: Mixing key selector value. + * + * Fill @dst with the KDF-BSVE encoding derived from @src and @mks. If BSVE is + * disabled, @dst is cleared and no fields are set. + */ +static void hwkm_pack_kdf_bsve(struct hwkm_hw_kdf_bsve *dst, + const struct hwkm_bsve *src, + uint8_t mks) +{ + memset(dst, 0, sizeof(*dst)); + + if (!src->enabled) + return; + + dst->mks = mks; + dst->key_policy_version_en = src->km_key_policy_ver_en ? 1:0; + dst->apps_secure_en = src->km_apps_secure_en ? 1:0; + dst->msa_secure_en = src->km_msa_secure_en ? 1:0; + dst->lcm_fuse_row_en = src->km_lcm_fuse_en ? 1:0; + dst->boot_stage_otp_en = src->km_boot_stage_otp_en ? 1:0; + dst->swc_en = src->km_swc_en ? 1:0; + dst->fuse_region_sha_digest_en = src->km_fuse_region_sha_digest_en; + dst->child_key_policy_en = src->km_child_key_policy_en ? 1:0; + dst->mks_en = src->km_mks_en ? 1:0; +} + +/* RUN TRANSACTION. */ + +#define HWKM_MASTER_MAX_RETRIES 100000U + +/** + * hwkm_fifo_wait() - Poll a FIFO status field until it is non-zero. + * @base: HWKM MMIO base. + * @reg: Status register offset. + * @mask: Field mask. + * @shift: Field shift. + * + * Return: HWKM_SUCCESS when the field becomes non-zero, or + * HWKM_ERR_FIFO_TIMEOUT if the field does not become non-zero within + * HWKM_MASTER_MAX_RETRIES attempts. + */ +static int hwkm_fifo_wait(vaddr_t base, uint32_t reg, + uint32_t mask, uint32_t shift) +{ + uint32_t retries = 0; + + while (!hwkm_reg_get_field(base, reg, mask, shift)) { + if (++retries > HWKM_MASTER_MAX_RETRIES) + return HWKM_ERR_FIFO_TIMEOUT; + udelay(10); + } + + return HWKM_SUCCESS; +} + +/** + * master_run_transaction() - Submit one command packet to the master HWKM. + * @cmd: Command packet words to write into the command FIFO. + * @cmd_words: Number of 32-bit words in @cmd. + * @rsp: Response buffer filled from the response FIFO. + * @rsp_words: Number of 32-bit words expected in @rsp. + * + * Step by step: + * 1. Fetch the driver context and resolve the HWKM MMIO base. + * 2. Pulse CMD_FIFO_CLEAR to flush any stale command FIFO contents. + * 3. Clear stale ESR bits from the previous transaction. + * 4. Enable command processing in BANK0_KM_CTL. + * 5. Verify that CMD_FIFO_CLEAR has deasserted, otherwise the FIFO did not + * drain correctly. + * 6. For each command word, poll CMD_FIFO_AVAIL_SPACE until space is + * available or a timeout is reached, then write the word to + * HWKM_BANK0_KM_CMD_FIFO. + * 7. For each response word, poll RSP_FIFO_AVAIL_DATA until data is + * available or a timeout is reached, then read the word from + * HWKM_BANK0_KM_RSP_FIFO. + * 8. Verify that CMD_DONE is set, which indicates the command completed and + * the full response was produced. + * 9. Clear CMD_DONE before returning. + * + * 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) +{ + struct hwkm_drv_ctx *ctx; + vaddr_t base; + + 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. */ + hwkm_reg_set_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT, 1); + hwkm_reg_set_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT, 0); + + /* Clear stale error state from the previous transaction. */ + HWKM_REG_WRITE(base, HWKM_BANK0_KM_ESR, + HWKM_REG_READ(base, HWKM_BANK0_KM_ESR)); + + /* Enable command processing. */ + hwkm_reg_set_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_ENABLE, + HWKM_BANK0_KM_CTL_CMD_ENABLE_SHIFT, 1); + + /* Confirm the FIFO clear bit has deasserted. */ + if (hwkm_reg_get_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT)) + return HWKM_ERR_FIFO_NOT_EMPTY; + + /* Push the command packet one word at a time. */ + for (size_t i = 0; i < cmd_words; i++) { + int rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + HWKM_BANK0_KM_STATUS_CMD_FIFO_AVAIL_SPACE, + HWKM_BANK0_KM_STATUS_CMD_FIFO_AVAIL_SPACE_SHIFT); + if (rc) + return rc; + + HWKM_REG_WRITE(base, HWKM_BANK0_KM_CMD_FIFO, cmd[i]); + } + + /* Pull the response packet one word at a time. */ + for (size_t i = 0; i < rsp_words; i++) { + int rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + HWKM_BANK0_KM_STATUS_RSP_FIFO_AVAIL_DATA, + HWKM_BANK0_KM_STATUS_RSP_FIFO_AVAIL_DATA_SHIFT); + if (rc) + return rc; + + rsp[i] = HWKM_REG_READ(base, HWKM_BANK0_KM_RSP_FIFO); + } + + /* The hardware must report completion after the response is read. */ + if (!hwkm_reg_get_field(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE, + HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE_SHIFT)) + return HWKM_ERR_RSP_OVERFLOW; + + /* Acknowledge completion. */ + HWKM_REG_WRITE(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE); + + return HWKM_SUCCESS; +} + +static int run_transaction(const struct hwkm_transaction *t, + const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + switch (t->hdl->dest) { + case HWKM_KEY_DEST_KM_MASTER: + return master_run_transaction(cmd, cmd_words, rsp, rsp_words); + default: + return HWKM_ERR_INVALID_DEST; + } +} + +/* Command handlers. */ + +/** + * hwkm_nist_keygen_handle() - Execute HWKM_OP_NIST_KEYGEN. + * @t: Transaction carrying the keygen request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1:2] = Key policy. + * CMD[3] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_nist_keygen_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_NIST_KEYGEN_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_NIST_KEYGEN_RSP_WORDS] = { 0 }; + struct hwkm_hw_key_policy policy = { }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_NIST_KEYGEN, + .irq_en = 0, + .slot1_desc = t->cmd.keygen.dks, + .slot2_desc = 0, + .op_flag = 0, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + hwkm_pack_key_policy(&policy, &t->cmd.keygen.policy); + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + /* CMD[1:2]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], &policy, sizeof(policy)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX]; + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_system_kdf_handle() - Execute HWKM_OP_SYSTEM_KDF. + * @t: Transaction carrying the KDF request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1:2] = Child key policy. + * CMD[3] = BSVE[0] if bsve is enabled, 0 otherwise. + * CMD[4:5] = BSVE[1:2] only if bsve is enabled. + * CMD[n:] = Software context, padded to 32-bit words. + * CMD[last] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_system_kdf_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_SYSTEM_KDF_CMD_WORDS + + HWKM_BSVE_WORDS + HWKM_SOFTWARE_CONTEXT_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_SYSTEM_KDF_RSP_WORDS] = { 0 }; + struct hwkm_hw_key_policy policy = { }; + struct hwkm_operation_info op = { }; + size_t ctx_words, ctx_idx, cmd_words; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + if (t->cmd.kdf.ctx_len > sizeof(t->cmd.kdf.ctx)) + return HWKM_ERR_INVALID_ARG; + + ctx_words = ROUNDUP_DIV(t->cmd.kdf.ctx_len, sizeof(uint32_t)); + ctx_idx = HWKM_OPERATION_INFO_WORDS + HWKM_KEY_POLICY_WORDS + + (t->cmd.kdf.bsve.enabled ? HWKM_BSVE_WORDS : 1); + + /* Total command length. */ + cmd_words = ctx_idx + ctx_words + 1 /* CRC */; + if (cmd_words > ARRAY_SIZE(cmd)) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_SYSTEM_KDF, + .irq_en = 0, + .slot1_desc = t->cmd.kdf.dks, + .slot2_desc = t->cmd.kdf.kdk, + .op_flag = t->cmd.kdf.bsve.enabled ? 1U : 0U, + .context_len = ctx_words, + .len = ctx_idx + 1, /* Up to and including CRC, excluding context. */ + }; + + hwkm_pack_key_policy(&policy, &t->cmd.kdf.policy); + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + /* CMD[1:2]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], &policy, sizeof(policy)); + + if (t->cmd.kdf.bsve.enabled) { + struct hwkm_hw_kdf_bsve bsve = { }; + + hwkm_pack_kdf_bsve(&bsve, &t->cmd.kdf.bsve, t->cmd.kdf.mks); + /* CMD[3:5]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS + HWKM_KEY_POLICY_WORDS], + &bsve, sizeof(bsve)); + } + + if (t->cmd.kdf.ctx_len) { + /* CMD[n:], if software context exists: */ + memcpy(&cmd[ctx_idx], t->cmd.kdf.ctx, t->cmd.kdf.ctx_len); + } + + rc = run_transaction(t, cmd, cmd_words, rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX]; + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_key_wrap_export_handle() - Execute HWKM_OP_KEY_WRAP_EXPORT. + * @t: Transaction carrying the wrap-export request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1:3] = BSVE, or 0 if BSVE is disabled. + * CMD[4] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * RSP[2:18] = Wrapped key blob on success. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_key_wrap_export_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_WRAP_EXPORT_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_WORDS] = { 0 }; + struct hwkm_hw_wrapping_bsve bsve = { }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_KEY_WRAP_EXPORT, + .irq_en = 0, + .slot1_desc = t->cmd.wrap.sks, + .slot2_desc = t->cmd.wrap.kwk, + .op_flag = 0, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + hwkm_pack_wrapping_bsve(&bsve, &t->cmd.wrap.bsve); + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + /* CMD[1:3]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], &bsve, sizeof(bsve)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) { + t->rsp.status = rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_ERR_IDX]; + /* On operation success, RSP[2:18]: */ + if (!t->rsp.status) { + memcpy(t->rsp.wrap.wkb, + &rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_WRAPPED_KEY_IDX], + HWKM_MAX_BLOB_SIZE); + } + } + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_key_unwrap_import_handle() - Execute HWKM_OP_KEY_UNWRAP_IMPORT. + * @t: Transaction carrying the unwrap-import request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1:17] = Wrapped key blob. + * CMD[18] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_key_unwrap_import_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_UNWRAP_IMPORT_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_WORDS] = { 0 }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_KEY_UNWRAP_IMPORT, + .irq_en = 0, + .slot1_desc = t->cmd.unwrap.dks, + .slot2_desc = t->cmd.unwrap.kwk, + .op_flag = 0, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + /* CMD[1:17]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], t->cmd.unwrap.wkb, + HWKM_MAX_BLOB_SIZE); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX]; + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_key_slot_clear_handle() - Execute HWKM_OP_KEY_SLOT_CLEAR. + * @t: Transaction carrying the clear request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_key_slot_clear_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_SLOT_CLEAR_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_KEY_SLOT_CLEAR_RSP_WORDS] = { 0 }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_KEY_SLOT_CLEAR, + .irq_en = 0, + .slot1_desc = t->cmd.clear.dks, + .slot2_desc = 0, + .op_flag = t->cmd.clear.is_double_key ? 1U : 0U, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX]; + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_key_slot_rdwr_handle() - Execute HWKM_OP_KEY_SLOT_RDWR. + * @t: Transaction carrying the read/write request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1:2] = Written policy, or 0 for read. + * CMD[3:10] = Written key value, or 0 for read. + * CMD[11] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * RSP[2:3] = Read policy, or 0 for write. + * RSP[4:11] = Read key value, or 0 for write. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_key_slot_rdwr_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_SLOT_RDWR_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_WORDS] = { 0 }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_KEY_SLOT_RDWR, + .irq_en = 0, + .slot1_desc = t->cmd.rdwr.slot, + .slot2_desc = 0, + .op_flag = t->cmd.rdwr.is_write ? 1U : 0U, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + + if (t->cmd.rdwr.is_write) { + struct hwkm_hw_key_policy policy = { }; + + hwkm_pack_key_policy(&policy, &t->cmd.rdwr.policy); + /* CMD[1:2]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], &policy, + sizeof(policy)); + /* CMD[3:10]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS + HWKM_KEY_POLICY_WORDS], + t->cmd.rdwr.key, HWKM_MAX_KEY_SIZE); + + } + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) { + t->rsp.status = rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX]; + if (!t->cmd.rdwr.is_write && !t->rsp.status) { + struct hwkm_hw_key_policy policy = { }; + + /* RSP[2:3]: */ + memcpy(&policy, + &rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX], + sizeof(policy)); + hwkm_unpack_key_policy(&t->rsp.rdwr.policy, &policy); + /* RSP[4:11]: */ + memcpy(t->rsp.rdwr.key, + &rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX], + HWKM_MAX_KEY_SIZE); + } + } + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/** + * hwkm_set_tpkey_handle() - Execute HWKM_OP_SET_TPKEY. + * @t: Transaction carrying the set-TPKEY request and response. + * + * Command format: + * CMD[0] = Operation info. + * CMD[1] = CRC word, left as 0 when CRC checking is disabled. + * + * Response format: + * RSP[0] = Unused. + * RSP[1] = Error status. + * + * Return: HWKM_SUCCESS on transport success, or a HWKM_ERR_* code. + * Hardware command failure is reported in t->rsp.status. + */ +static int hwkm_set_tpkey_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_SET_TPKEY_CMD_WORDS] = { 0 }; + uint32_t rsp[HWKM_OP_SET_TPKEY_RSP_WORDS] = { 0 }; + struct hwkm_operation_info op = { }; + int rc; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + if (t->hdl->dest != HWKM_KEY_DEST_KM_MASTER) + return HWKM_ERR_INVALID_DEST; + + op = (struct hwkm_operation_info){ + .op = HWKM_OP_SET_TPKEY, + .irq_en = 0, + .slot1_desc = t->cmd.set_tpkey.sks, + .slot2_desc = 0, + .op_flag = 0, + .context_len = 0, + .len = ARRAY_SIZE(cmd), + }; + + /* CMD[0]: */ + memcpy(&cmd[0], &op, sizeof(op)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_SET_TPKEY_RSP_ERR_IDX]; + + memset(cmd, 0, sizeof(cmd)); + memset(rsp, 0, sizeof(rsp)); + + return rc; +} + +/* Command dispatcher: */ +static int hwkm_transaction_dispatch(struct hwkm_transaction *t) +{ + switch (t->cmd.op) { + case HWKM_OP_NIST_KEYGEN: + return hwkm_nist_keygen_handle(t); + case HWKM_OP_SYSTEM_KDF: + return hwkm_system_kdf_handle(t); + case HWKM_OP_KEY_WRAP_EXPORT: + return hwkm_key_wrap_export_handle(t); + case HWKM_OP_KEY_UNWRAP_IMPORT: + return hwkm_key_unwrap_import_handle(t); + case HWKM_OP_KEY_SLOT_CLEAR: + return hwkm_key_slot_clear_handle(t); + case HWKM_OP_KEY_SLOT_RDWR: + return hwkm_key_slot_rdwr_handle(t); + case HWKM_OP_SET_TPKEY: + return hwkm_set_tpkey_handle(t); + default: + return HWKM_ERR_NOT_SUPPORTED; + } +} + +/* Command queue. */ + +/** + * hwkm_handle_init() - Initialize an HWKM handle. + * @hdl: Handle to initialize. + * @dest: Target hardware engine. + * + * Initializes @hdl for queuing commands to @dest. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest) +{ + switch (dest) { + case HWKM_KEY_DEST_KM_MASTER: + break; + default: + return HWKM_ERR_INVALID_DEST; + } + + hdl->dest = dest; + STAILQ_INIT(&hdl->queue); + + return HWKM_SUCCESS; +} + +/** + * hwkm_enqueue() - Enqueue a transaction on a handle. + * @hdl: Handle that owns the queue. + * @t: Transaction to enqueue. + * + * Associates @t with @hdl and appends it to the tail of the queue. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_enqueue(struct hwkm_handle *hdl, struct hwkm_transaction *t) +{ + if (!t) + return HWKM_ERR_INVALID_ARG; + + if (t->hdl) + return HWKM_ERR_INVALID_ARG; + + t->hdl = hdl; + STAILQ_INSERT_TAIL(&hdl->queue, t, link); + + return HWKM_SUCCESS; +} + +/** + * hwkm_enqueue_many() - Enqueue multiple transactions on one handle. + * @hdl: Handle that owns the queue. + * @num_t: Number of transactions in @trans. + * @trans: Array of transaction pointers to enqueue in FIFO order. + * + * Enqueues all transactions in @trans onto @hdl using hwkm_enqueue(). + * If enqueueing any transaction fails, all transactions already queued by + * this call are removed again and their ownership is cleared, so the caller + * sees all-or-nothing behavior. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_enqueue_many(struct hwkm_handle *hdl, size_t num_t, + struct hwkm_transaction *const trans[]) +{ + size_t i = 0; + int rc; + + if (!hdl || (num_t && !trans)) + return HWKM_ERR_INVALID_ARG; + + for (i = 0; i < num_t; i++) { + rc = hwkm_enqueue(hdl, trans[i]); + if (rc) + goto rollback; + } + + return HWKM_SUCCESS; + +rollback: + while (i > 0) { + struct hwkm_transaction *t = trans[--i]; + /* Release the ownership. */ + STAILQ_REMOVE(&hdl->queue, t, hwkm_transaction, link); + t->hdl = NULL; + } + + return rc; +} + +/** + * hwkm_run_cmd_queue() - Execute queued transactions. + * @hdl: Handle containing queued transactions + * + * Executes all transactions queued on @hdl in FIFO order by calling + * hwkm_transaction_dispatch() for each transaction. Execution stops at the + * first error and that error is returned to the caller. + * + * On return, transactions that were processed are removed from the queue and + * their ownership is cleared (to be reused). + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_run_cmd_queue(struct hwkm_handle *hdl) +{ + struct hwkm_transaction *t = NULL; + int rc = HWKM_SUCCESS; + + while (!STAILQ_EMPTY(&hdl->queue)) { + t = STAILQ_FIRST(&hdl->queue); + rc = hwkm_transaction_dispatch(t); + /* Release the ownership. */ + STAILQ_REMOVE_HEAD(&hdl->queue, link); + t->hdl = NULL; + + if (rc != HWKM_SUCCESS) + break; + } + + return rc; +} + +/* Run one transaction. */ +int hwkm_run_transaction(enum hwkm_key_destination dest, + struct hwkm_transaction *t) +{ + struct hwkm_handle hdl; + int rc; + + rc = hwkm_handle_init(&hdl, dest); + if (rc) + return rc; + + rc = hwkm_enqueue(&hdl, t); + if (rc) + return rc; + + return hwkm_run_cmd_queue(&hdl); +} diff --git a/core/drivers/crypto/hwkm/include/hwkm.h b/core/drivers/crypto/hwkm/include/hwkm.h new file mode 100644 index 000000000..ef7c7ece1 --- /dev/null +++ b/core/drivers/crypto/hwkm/include/hwkm.h @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#ifndef __HWKM_H__ +#define __HWKM_H__ + +#include +#include +#include +#include +#include + +#define HWKM_MAX_KEY_SIZE 32 /* bytes */ +#define HWKM_MAX_CTX_SIZE 64 /* bytes */ +#define HWKM_MAX_BLOB_SIZE 68 /* bytes */ + +enum hwkm_key_destination { + HWKM_KEY_DEST_KM_MASTER = 0, +}; + +enum hwkm_key_security_lvl { + HWKM_KEY_SECURITY_LVL_SW_KEY = 0, + HWKM_KEY_SECURITY_LVL_MANAGED_KEY, + HWKM_KEY_SECURITY_LVL_HW_KEY, +}; + +enum hwkm_key_type { + HWKM_KEY_TYPE_KDK = 0, /* Key derivation key. */ + HWKM_KEY_TYPE_KWK, /* Key wrapping key. */ + HWKM_KEY_TYPE_KSK, /* Key swap key. */ + HWKM_KEY_TYPE_TPKEY, /* Transport protection key. */ + HWKM_KEY_TYPE_GENERIC_KEY, +}; + +/* Master key slot assignments. */ +enum hwkm_master_key_slots { + /* L1 KDKs - HW-only: */ + HWKM_SLOT_NKDK_L1 = 0, + HWKM_SLOT_PKDK_L1 = 1, + HWKM_SLOT_SKDK_L1 = 2, + HWKM_SLOT_UKDK_L1 = 3, + /* L2 KDKs. */ + HWKM_SLOT_TZ_NKDK_L2 = 4, + HWKM_SLOT_TZ_PKDK_L2 = 5, + HWKM_SLOT_TZ_SKDK_L2 = 6, + HWKM_SLOT_MODEM_PKDK_L2 = 7, + HWKM_SLOT_MODEM_SKDK_L2 = 8, + HWKM_SLOT_TZ_UKDK_L2 = 9, + /* TPKEY pair. */ + HWKM_SLOT_TPKEY_SLOT = 10, + HWKM_SLOT_TPKEY_ODD_SLOT = 11, + /* Swap key pair. */ + HWKM_SLOT_TZ_SWAP_KEY_SLOT = 12, + HWKM_SLOT_TZ_SWAP_KEY_ODD_SLOT = 13, + /* Wrap key pair. */ + HWKM_SLOT_TZ_WRAP_KEY_SLOT = 14, + HWKM_SLOT_TZ_WRAP_KEY_ODD_SLOT = 15, + /* General purpose scratch. */ + HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1 = 16, + HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2 = 17, + /* Persistent shared pairs. */ + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1 = 18, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1_ODD = 19, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2 = 20, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2_ODD = 21, + /* Mixing key. */ + HWKM_SLOT_TZ_MIXING_KEY_SLOT = 22, +}; + +enum hwkm_algo { + HWKM_ALGO_AES128_ECB = 0, + HWKM_ALGO_AES256_ECB = 1, + HWKM_ALGO_AES128_CBC = 4, + HWKM_ALGO_AES256_CBC = 5, + HWKM_ALGO_AES256_SIV = 12, + HWKM_ALGO_AES128_CTR = 13, + HWKM_ALGO_AES256_CTR = 14, + HWKM_ALGO_AES128_XTS = 15, + HWKM_ALGO_AES256_XTS = 16, + HWKM_ALGO_AES128_CMAC = 19, + HWKM_ALGO_AES256_CMAC = 20, +}; + +struct hwkm_key_policy { + bool km_by_tz_allowed; /* TZ may issue commands against this slot. */ + bool km_by_nsec_allowed; /* Non-secure world may use this slot. */ + bool km_by_modem_allowed; /* Modem may use this slot. */ + bool km_by_spu_allowed; /* SPU may use this slot. */ + enum hwkm_algo alg_allowed; /* Only algorithm permitted for this key. */ + bool enc_allowed; /* Key may be used for encryption. */ + bool dec_allowed; /* Key may be used for decryption. */ + enum hwkm_key_type key_type; /* Functional role. */ + uint8_t kdf_depth; /* Remaining KDF derivation hops. */ + bool wrap_export_allowed; /* Key may be exported via KEY_WRAP_EXPORT. */ + bool swap_export_allowed; /* Key may be delivered via key-swap. */ + enum hwkm_key_security_lvl security_lvl; /* SW_KEY, MANAGED_KEY, or HW_KEY. */ + enum hwkm_key_destination hw_destination; /* Hardware destination. */ + bool wrap_with_tpkey_allowed; /* Allow wrapping under TPKEY. */ +}; + +struct hwkm_bsve { + bool enabled; /* Master enable for all BSVE checks. */ + bool km_key_policy_ver_en; /* Enforce key policy version field. */ + bool km_apps_secure_en; /* Require apps processor to be in secure state. */ + bool km_msa_secure_en; /* Require modem to be in secure state. */ + bool km_lcm_fuse_en; /* Bind derivation to life-cycle fuse state. */ + bool km_boot_stage_otp_en; /* Bind derivation to boot-stage OTP value. */ + bool km_swc_en; /* Enforce software component version check. */ + bool km_child_key_policy_en; /* Child key policy must be subset of parent. */ + bool km_mks_en; /* Include the mixing-key slot value in the KDF. */ + uint64_t km_fuse_region_sha_digest_en; /* Fuse regions hashed into KDF. */ +}; + +/* Command and response structs. */ + +enum hwkm_op { + HWKM_OP_NIST_KEYGEN = 0, /* Generate a key via HW PRNG. */ + HWKM_OP_SYSTEM_KDF = 1, /* Derive a child key from a KDK slot. */ + /* Opcode 2 is reserved. */ + HWKM_OP_KEY_WRAP_EXPORT = 3, /* Wrap a slot under a KWK/KSK. */ + HWKM_OP_KEY_UNWRAP_IMPORT = 4, /* Unwrap a blob into a destination slot. */ + HWKM_OP_KEY_SLOT_CLEAR = 5, /* Clear a slot and invalidate its policy. */ + HWKM_OP_KEY_SLOT_RDWR = 6, /* Read or write raw key material. */ + HWKM_OP_SET_TPKEY = 7, /* Install a slot as the active TPKEY. */ +}; + +struct hwkm_keygen_cmd { + uint8_t dks; /* Destination key slot. */ + struct hwkm_key_policy policy; +}; + +struct hwkm_rdwr_cmd { + uint8_t slot; + bool is_write; + struct hwkm_key_policy policy; + uint8_t key[HWKM_MAX_KEY_SIZE]; +}; + +struct hwkm_kdf_cmd { + uint8_t dks; /* Destination key slot. */ + uint8_t kdk; /* Parent KDK slot. */ + uint8_t mks; /* Mixing-key slot selector. */ + struct hwkm_key_policy policy; + struct hwkm_bsve bsve; + uint8_t ctx[HWKM_MAX_CTX_SIZE]; + size_t ctx_len; +}; + +struct hwkm_set_tpkey_cmd { + uint8_t sks; /* Source key slot. */ +}; + +struct hwkm_unwrap_cmd { + uint8_t dks; /* Destination key slot. */ + uint8_t kwk; /* Wrapping key slot. */ + uint8_t wkb[HWKM_MAX_BLOB_SIZE]; +}; + +struct hwkm_wrap_cmd { + uint8_t sks; /* Source key slot. */ + uint8_t kwk; /* Wrapping key slot. */ + struct hwkm_bsve bsve; +}; + +struct hwkm_clear_cmd { + uint8_t dks; /* Destination key slot. */ + bool is_double_key; +}; + +struct hwkm_cmd { + enum hwkm_op op; + union { + struct hwkm_keygen_cmd keygen; + struct hwkm_rdwr_cmd rdwr; + struct hwkm_kdf_cmd kdf; + struct hwkm_set_tpkey_cmd set_tpkey; + struct hwkm_unwrap_cmd unwrap; + struct hwkm_wrap_cmd wrap; + struct hwkm_clear_cmd clear; + }; +}; + +struct hwkm_rdwr_rsp { + struct hwkm_key_policy policy; + uint8_t key[HWKM_MAX_KEY_SIZE]; +}; + +struct hwkm_wrap_rsp { + uint8_t wkb[HWKM_MAX_BLOB_SIZE]; +}; + +struct hwkm_rsp { + uint32_t status; + union { + struct hwkm_rdwr_rsp rdwr; + struct hwkm_wrap_rsp wrap; + }; +}; + +/* PUBLIC API. */ + +#define HWKM_SUCCESS 0 +#define HWKM_ERR_GENERIC 1 +#define HWKM_ERR_INVALID_ARG 2 +#define HWKM_ERR_INVALID_DEST 3 +#define HWKM_ERR_FIFO_NOT_EMPTY 4 +#define HWKM_ERR_FIFO_TIMEOUT 5 +#define HWKM_ERR_RSP_OVERFLOW 6 +#define HWKM_ERR_NOT_SUPPORTED 7 + +static inline TEE_Result hwkm_to_tee(int rc) +{ + switch (rc) { + case HWKM_SUCCESS: + return TEE_SUCCESS; + case HWKM_ERR_INVALID_ARG: + case HWKM_ERR_INVALID_DEST: + return TEE_ERROR_BAD_PARAMETERS; + case HWKM_ERR_NOT_SUPPORTED: + return TEE_ERROR_NOT_SUPPORTED; + default: + return TEE_ERROR_GENERIC; + } +} + +struct hwkm_handle; + +struct hwkm_drv_ctx { + vaddr_t base; + + struct mutex hwkm_lock; + + bool hwkm_huk_ready; + uint8_t hwkm_huk[HWKM_MAX_KEY_SIZE]; + + bool initialized; +}; + +struct hwkm_drv_ctx *hwkm_get_context(void); + +/** + * struct hwkm_transaction - queued HWKM command and response. + * @cmd: Command to submit. + * @rsp: Response filled by the driver. + * @hdl: Owning handle when the transaction is queued. + * @link: Queue link. + * + * A transaction contains one HWKM command and its corresponding response. + * Transactions are queued on a handle and executed in FIFO order by + * hwkm_run_cmd_queue(). + */ +struct hwkm_transaction { + struct hwkm_cmd cmd; + struct hwkm_rsp rsp; + struct hwkm_handle *hdl; + STAILQ_ENTRY(hwkm_transaction) link; +}; + +STAILQ_HEAD(hwkm_transaction_queue, hwkm_transaction); + +/** + * struct hwkm_handle - HWKM command queue handle. + * @dest: Target hardware engine. + * @queue: Queued transactions. + * + * A handle groups one or more HWKM commands targeting the same destination. + * Initialize it with hwkm_handle_init(), enqueue transactions with + * hwkm_enqueue() / hwkm_enqueue_many(), then execute them with + * hwkm_run_cmd_queue(). + * + * Example: + * + * struct hwkm_transaction t_kdf = { + * .cmd = { + * .op = HWKM_OP_SYSTEM_KDF, + * }, + * }; + * + * struct hwkm_transaction t_clear = { + * .cmd = { + * .op = HWKM_OP_KEY_SLOT_CLEAR, + * }, + * }; + * + * struct hwkm_handle h = { }; + * + * hwkm_handle_init(&h, HWKM_KEY_DEST_KM_MASTER); + * hwkm_enqueue(&h, &t_kdf); + * hwkm_enqueue(&h, &t_clear); + * hwkm_run_cmd_queue(&h); + * + * The responses are returned in t_kdf.rsp and t_clear.rsp. + */ +struct hwkm_handle { + enum hwkm_key_destination dest; + struct hwkm_transaction_queue queue; +}; + +struct hwkm_transaction *hwkm_transaction_alloc(void); +void hwkm_transaction_free(struct hwkm_transaction *t); + +int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest); +int hwkm_enqueue(struct hwkm_handle *hdl, struct hwkm_transaction *t); +int hwkm_enqueue_many(struct hwkm_handle *hdl, size_t num_t, + struct hwkm_transaction *const trans[]); +int hwkm_run_cmd_queue(struct hwkm_handle *hdl); +int hwkm_run_transaction(enum hwkm_key_destination dest, + struct hwkm_transaction *t); + +/** + * hwkm_run_transactions() - Init a handle, enqueue @num_t transactions, and + * execute them in one call. + * @dest: Target hardware engine. + * @num_t: Number of entries in @trans. + * @trans: Array of transaction pointers executed in FIFO order. + * + * Convenience wrapper around hwkm_handle_init() + hwkm_enqueue_many() + + * hwkm_run_cmd_queue(). Use when all transactions target the same destination + * and no handle needs to survive the call. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +static inline int hwkm_run_transactions(enum hwkm_key_destination dest, + size_t num_t, + struct hwkm_transaction *const trans[]) +{ + struct hwkm_handle hdl; + int rc; + + rc = hwkm_handle_init(&hdl, dest); + if (rc) + return rc; + + rc = hwkm_enqueue_many(&hdl, num_t, trans); + if (rc) + return rc; + + return hwkm_run_cmd_queue(&hdl); +} + +#endif /* __HWKM_H__ */ diff --git a/core/drivers/crypto/hwkm/include/hwkm_errno.h b/core/drivers/crypto/hwkm/include/hwkm_errno.h new file mode 100644 index 000000000..f1e7bdaa9 --- /dev/null +++ b/core/drivers/crypto/hwkm/include/hwkm_errno.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + * + * Hardware Key Manager (HWKM) response-error bit definitions. + */ + +#ifndef __HWKM_ERRNO_H__ +#define __HWKM_ERRNO_H__ + +/* Error status bitmap at RSP[*_RSP_ERR_IDX]. */ + +#define HWKM_RSP_ERR_SUCCESS 0U +/* Common errors (bits 0 - 7, present in every opcode response). */ +#define HWKM_ERR_RSP_FIFO_UNDERFLOW BIT(0) +#define HWKM_ERR_CMD_FIFO_OVERFLOW BIT(1) +#define HWKM_ERR_OPCODE_UNSUPPORTED BIT(2) +#define HWKM_ERR_INVALID_OPCODE BIT(3) +#define HWKM_ERR_INCORRECT_CMD_LENGTH BIT(4) +#define HWKM_ERR_CRC_CHECK BIT(5) +#define HWKM_ERR_BBAC_ACCESS BIT(6) + +/* NIST_KEYGEN specific errors (bits 8+). */ +#define HWKM_NIST_KEYGEN_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(8) +#define HWKM_NIST_KEYGEN_ERR_UNSUPPORTED_KEY_SIZE BIT(9) +#define HWKM_NIST_KEYGEN_ERR_KT_PARITY BIT(10) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KEY_TYPE BIT(11) +#define HWKM_NIST_KEYGEN_ERR_INVALID_ALG_ALLOWED BIT(12) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KEY_TYPE_OTHER_ATTRIB BIT(13) +#define HWKM_NIST_KEYGEN_ERR_INVALID_TPKEY_ATTRIB BIT(14) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KSK_ATTRIB BIT(15) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KWK_ATTRIB BIT(16) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KDF_DEPTH BIT(17) +#define HWKM_NIST_KEYGEN_ERR_INVALID_SECURITY_LEVEL BIT(18) +#define HWKM_NIST_KEYGEN_ERR_DKS_SLOT_NOT_EMPTY BIT(19) +#define HWKM_NIST_KEYGEN_ERR_ACCESS_VIOLATION BIT(20) + +/* KEY_SLOT_RDWR specific errors (bits 8+). */ +#define HWKM_KEY_SLOT_RDWR_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(8) +#define HWKM_KEY_SLOT_RDWR_ERR_UNSUPPORTED_KEY_SIZE BIT(9) +#define HWKM_KEY_SLOT_RDWR_ERR_KT_PARITY BIT(10) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_SECURITY_LEVEL BIT(11) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_TYPE BIT(12) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_ALG_ALLOWED BIT(13) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_TYPE_OTHER_ATTRIB BIT(14) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_TPKEY_ATTRIB BIT(15) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KSK_ATTRIB BIT(16) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KWK_ATTRIB BIT(17) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KDF_DEPTH BIT(18) +#define HWKM_KEY_SLOT_RDWR_ERR_SECURITY_LEVEL_VIOLATION BIT(19) +#define HWKM_KEY_SLOT_RDWR_ERR_SKS_SLOT_EMPTY BIT(20) +#define HWKM_KEY_SLOT_RDWR_ERR_DKS_SLOT_NOT_EMPTY BIT(21) +#define HWKM_KEY_SLOT_RDWR_ERR_ACCESS_VIOLATION BIT(22) + +/* SYSTEM_KDF specific errors (bits 8+). */ +#define HWKM_SYSTEM_KDF_ERR_FR_RD_ERR_ON_SLAVE BIT(8) +#define HWKM_SYSTEM_KDF_ERR_MKS_L1L2_NOT_ALLOWED BIT(9) +#define HWKM_SYSTEM_KDF_ERR_SEC_CTRL_ACCESS BIT(10) +#define HWKM_SYSTEM_KDF_ERR_KT_PARITY BIT(11) +#define HWKM_SYSTEM_KDF_ERR_BSVE_BIT_NOT_SUPPORTED BIT(12) +#define HWKM_SYSTEM_KDF_ERR_INVALID_SECURITY_LEVEL BIT(13) +#define HWKM_SYSTEM_KDF_ERR_INVALID_KEY_TYPE BIT(14) +#define HWKM_SYSTEM_KDF_ERR_INVALID_ALG_ALLOWED BIT(15) +#define HWKM_SYSTEM_KDF_ERR_SWAP_EXPORT_NOT_ALLOWED BIT(16) +#define HWKM_SYSTEM_KDF_ERR_KEYPOLICY_CONSISTENCY BIT(17) +#define HWKM_SYSTEM_KDF_ERR_ENCRYPT_NOT_ALLOWED BIT(18) +#define HWKM_SYSTEM_KDF_ERR_UNEXPECTED_ALG_ALLOWED BIT(19) +#define HWKM_SYSTEM_KDF_ERR_UNEXPECTED_KEY_TYPE BIT(20) +#define HWKM_SYSTEM_KDF_ERR_KDF_DEPTH_ERROR BIT(21) +#define HWKM_SYSTEM_KDF_ERR_KDK_TYPE BIT(22) +#define HWKM_SYSTEM_KDF_ERR_KEY_SIZE BIT(23) +#define HWKM_SYSTEM_KDF_ERR_KEYSLOT_SLOT BIT(24) +#define HWKM_SYSTEM_KDF_ERR_KEYSLOT_OUT_OF_RANGE BIT(25) +#define HWKM_SYSTEM_KDF_ERR_DOUBLE_KEY_SLOT_NOT_EVEN BIT(26) +#define HWKM_SYSTEM_KDF_ERR_ACCESS_VIOLATION BIT(27) +#define HWKM_SYSTEM_KDF_ERR_SECURITY_LEVEL_VIOLATION BIT(28) + +/* KEY_WRAP_EXPORT specific errors (bits 8+). */ +#define HWKM_EXPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT BIT(8) +#define HWKM_EXPORT_ERR_ENCRYPT_NOT_ALLOWED BIT(9) +#define HWKM_EXPORT_ERR_SKS_SIZE_ERROR BIT(10) +#define HWKM_EXPORT_ERR_SKS_SLOT_OUT_OF_RANGE BIT(11) +#define HWKM_EXPORT_ERR_KWK_SIZE_ERROR BIT(12) +#define HWKM_EXPORT_ERR_KWK_SLOT_OUT_OF_RANGE BIT(13) +#define HWKM_EXPORT_ERR_KT_PARITY BIT(14) +#define HWKM_EXPORT_ERR_WRAP_WITH_TPKEY_NOT_ALLOWED BIT(15) +#define HWKM_EXPORT_ERR_SWAP_EXPORT_NOT_ALLOWED BIT(16) +#define HWKM_EXPORT_ERR_WRAP_EXPORT_NOT_ALLOWED BIT(17) +#define HWKM_EXPORT_ERR_UNEXPECTED_ALG_ALLOWED BIT(18) +#define HWKM_EXPORT_ERR_SECURITY_LEVEL_ERROR BIT(19) +#define HWKM_EXPORT_ERR_UNEXPECTED_KEY_TYPE BIT(20) +#define HWKM_EXPORT_ERR_KWK_SLOT_EMPTY BIT(21) +#define HWKM_EXPORT_ERR_SKS_SLOT_EMPTY BIT(22) +#define HWKM_EXPORT_ERR_SKS_ACCESS_VIOLATION BIT(23) +#define HWKM_EXPORT_ERR_KWK_ACCESS_VIOLATION BIT(24) + +/* KEY_UNWRAP_IMPORT specific errors (bits 8+). */ +#define HWKM_IMPORT_ERR_UNWRAP_FAILED BIT(8) +#define HWKM_IMPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT BIT(9) +#define HWKM_IMPORT_ERR_KWK_SIZE_ERROR BIT(10) +#define HWKM_IMPORT_ERR_DKS_SIZE_ERROR BIT(11) +#define HWKM_IMPORT_ERR_DKS_OUT_OF_RANGE BIT(12) +#define HWKM_IMPORT_ERR_KWK_SLOT_OUT_OF_RANGE BIT(13) +#define HWKM_IMPORT_ERR_KT_PARITY BIT(14) +#define HWKM_IMPORT_ERR_SECURITY_LEVEL_ERROR BIT(15) +#define HWKM_IMPORT_ERR_INVALID_SECURITY_LEVEL BIT(16) +#define HWKM_IMPORT_ERR_INVALID_KEY_TYPE BIT(17) +#define HWKM_IMPORT_ERR_INVALID_ALG_ALLOWED BIT(18) +#define HWKM_IMPORT_ERR_CONSISTENCY_CHECK_FAILED BIT(19) +#define HWKM_IMPORT_ERR_INVALID_TPKEY_ATTRIB BIT(20) +#define HWKM_IMPORT_ERR_INVALID_KSK_ATTRIB BIT(21) +#define HWKM_IMPORT_ERR_INVALID_KWK_ATTRIB BIT(22) +#define HWKM_IMPORT_ERR_INVALID_KDF_DEPTH BIT(23) +#define HWKM_IMPORT_ERR_DECRYPT_NOT_ALLOWED BIT(24) +#define HWKM_IMPORT_ERR_UNEXPECTED_ALG_ALLOWED BIT(25) +#define HWKM_IMPORT_ERR_UNEXPECTED_KEY_TYPE BIT(26) +#define HWKM_IMPORT_ERR_KWK_SLOT_EMPTY BIT(27) +#define HWKM_IMPORT_ERR_DKS_SLOT_NOT_EMPTY BIT(28) +#define HWKM_IMPORT_ERR_DKS_ACCESS_VIOLATION BIT(29) +#define HWKM_IMPORT_ERR_KWK_ACCESS_VIOLATION BIT(30) + +/* KEY_SLOT_CLEAR specific errors (bits 8+). */ +#define HWKM_CLEAR_ERR_KEYSLOT_ID_NOT_ACCESSIBLE BIT(8) +#define HWKM_CLEAR_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(9) +#define HWKM_CLEAR_ERR_KEY_SIZE BIT(10) +#define HWKM_CLEAR_ERR_DOUBLE_KEY_DKS_SLOT_NOT_EVEN BIT(11) +#define HWKM_CLEAR_ERR_DKS_SLOT_EMPTY BIT(12) +#define HWKM_CLEAR_ERR_ACCESS_VIOLATION BIT(13) + +/* SET_TPKEY specific errors (bits 8+). */ +#define HWKM_TPKEY_ERR_KT_PARITY BIT(8) +#define HWKM_TPKEY_ERR_DOUBLE_KEY_SLOT_NOT_EVEN BIT(9) +#define HWKM_TPKEY_ERR_WRONG_KEY_SIZE BIT(10) +#define HWKM_TPKEY_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(11) +#define HWKM_TPKEY_ERR_UNEXPECTED_KEY_TYPE BIT(12) +#define HWKM_TPKEY_ERR_SKS_SLOT_EMPTY BIT(13) +#define HWKM_TPKEY_ERR_ACCESS_VIOLATION BIT(14) + +#endif /* __HWKM_ERRNO_H__ */ diff --git a/core/drivers/crypto/hwkm/include/hwkm_regs.h b/core/drivers/crypto/hwkm/include/hwkm_regs.h new file mode 100644 index 000000000..8a9ef1d09 --- /dev/null +++ b/core/drivers/crypto/hwkm/include/hwkm_regs.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + * + * HWKM register offsets and field masks. + */ + +#ifndef __HWKM_REGS_H__ +#define __HWKM_REGS_H__ + +#include +#include +#include /* BIT() and GENMASK_32(). */ + +/* Raw read/write. */ +#define HWKM_REG_READ(base, reg) (io_read32((base) + (reg))) +#define HWKM_REG_WRITE(base, reg, val) \ + do { \ + io_write32((base) + (reg), (val)); \ + } while (0) + +/* Resource group - SHARED. */ + +/* SHARED_IPCAT_VERSION (HWKM_REGS_BASE + 0x0000U). */ +/* 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 + +/* Resource group - TZ. */ + +/* TZ_KM_CTL (HWKM_REGS_BASE + 0x1000U). */ +#define HWKM_TZ_KM_CTL 0x1000U +/* 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 +/* 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 a fatal error. */ +#define HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT 0xfU +#define HWKM_TZ_KM_STATUS_BIST_ERROR BIT(HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT) +/* Internal crypto-library self-test failed. Treated identically to + * BIST_ERROR by the driver; either bit causes bist_failed to be set. */ +#define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR_SHIFT 0xdU +#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 +/* 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 BIT(HWKM_TZ_TPKEY_RECEIVE_CTL_EN_SHIFT) +/* Destination key slot that will receive the incoming TPKEY [7:0]. + * Write CRYPTO_DEFAULT_TPKEY here before asserting EN. */ +#define HWKM_TZ_TPKEY_RECEIVE_CTL_TPKEY_DKS_SHIFT 0U +#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 +/* Set by hardware when the TPKEY has been written into TPKEY_DKS. + * Poll this after SET_TPKEY, before disarming the slave. */ +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE_SHIFT 0x8U +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE \ + BIT(HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE_SHIFT) +/* Slot index where the TPKEY was stored (readback of TPKEY_DKS) [7:0]. */ +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_TPKEY_DKS_SHIFT 0U +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_TPKEY_DKS GENMASK_32(7, 0) + +/* Resource group - BANKn_CLIENT. */ + +/* BANK0_KM_CTL (HWKM_REGS_BASE + 0x2000U). */ +#define HWKM_BANK0_KM_CTL 0x2000U +/* Enable the command FIFO for a new packet. Write 1 after clearing the + * FIFO and ESR, before writing the first command word. */ +#define HWKM_BANK0_KM_CTL_CMD_ENABLE_SHIFT 0U +#define HWKM_BANK0_KM_CTL_CMD_ENABLE BIT(HWKM_BANK0_KM_CTL_CMD_ENABLE_SHIFT) +/* Flush the command FIFO. Write 1 then 0 to reset. Read back: if still 1 + * after deassertion, the FIFO did not drain. */ +#define HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT 0x1U +#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 +/* Words of data available in the response FIFO [13:9]. + * Poll > 0 before reading each response word. */ +#define HWKM_BANK0_KM_STATUS_RSP_FIFO_AVAIL_DATA_SHIFT 0x9U +#define HWKM_BANK0_KM_STATUS_RSP_FIFO_AVAIL_DATA GENMASK_32(13, 9) +/* Words of free space in the command FIFO [18:14]. + * Poll > 0 before writing each command word. */ +#define HWKM_BANK0_KM_STATUS_CMD_FIFO_AVAIL_SPACE_SHIFT 0xeU +#define HWKM_BANK0_KM_STATUS_CMD_FIFO_AVAIL_SPACE GENMASK_32(18, 14) + +/* KM_IRQ_STATUS (HWKM_REGS_BASE + 0x2008U). */ +#define HWKM_BANK0_KM_IRQ_STATUS 0x2008U +/* 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 + * words, more data remains -> HWKM_ERR_RSP_OVERFLOW. + * Clear by writing 1 at the end of every successful transaction. */ +#define HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE_SHIFT 0x1U +#define HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE \ + BIT(HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE_SHIFT) +/* Response FIFO full. May be spuriously set after reset (HW errata + * QCTDD06252768). Clear unconditionally during init by writing 1. */ +#define HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL_SHIFT 0x3U +#define HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL \ + BIT(HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL_SHIFT) + +/* + * BANK0_KM_ESR - Error status register (HWKM_REGS_BASE + 0x2010U). + * + * Each bit represents one error condition from the last command. + * Clear pattern: HWKM_REG_WRITE(HWKM_REGS_BASE, HWKM_BANK0_KM_ESR, + * HWKM_REG_READ(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 + +/* 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 + +/* 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 + +/* Resource group - BANKn_AC_MANAGER . */ + +/* + * BANKn_AC_BBAC_m - Bank-Based Access Control (BBAC) bitmaps. + * + * Each KM instance exposes one AC register block per security domain ("bank"). + * Within each block, up to five 32-bit BBAC registers cover the full key + * table in 32-slot windows. + * + * Setting a bit to 1 grants the owning security domain ("bank") the right to + * reference that slot in any command it submits. Clearing a bit causes the + * hardware to reject any command from that domain that names the slot, + * returning HWKM_ERR_BBAC_ACCESS in the response error word. + * + * BBAC access control is checked only for DKS/SKS (destination/source key + * slot) fields in the command packet. + * + * Example: grant BANK0 (TZ) unrestricted access to all 128 key slots: + * + * HWKM_REG_WRITE(HWKM_REGS_BASE + HWKM_BANK0_AC, + * HWKM_BANKn_AC_BBAC_0, 0xFFFFFFFF); + * HWKM_REG_WRITE(HWKM_REGS_BASE + HWKM_BANK0_AC, + * HWKM_BANKn_AC_BBAC_1, 0xFFFFFFFF); + * HWKM_REG_WRITE(HWKM_REGS_BASE + HWKM_BANK0_AC, + * HWKM_BANKn_AC_BBAC_2, 0xFFFFFFFF); + * HWKM_REG_WRITE(HWKM_REGS_BASE + HWKM_BANK0_AC, + * HWKM_BANKn_AC_BBAC_3, 0xFFFFFFFF); + */ + +#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. */ +#define HWKM_BANKn_AC_BBAC_3 0x000cU /* slots 96-127. */ +#define HWKM_BANKn_AC_BBAC_4 0x0010U /* slots 128-159. */ + +/* + * hwkm_reg_get_field() - Read a register field. + * @base: HWKM MMIO base. + * @reg: register offset from @base. + * @mask: field mask. + * @shift: field shift. + * + * Return: field value, right-justified. + */ +static inline uint32_t hwkm_reg_get_field(vaddr_t base, uint32_t reg, + uint32_t mask, uint32_t shift) +{ + return (HWKM_REG_READ(base, reg) & mask) >> shift; +} + +/* + * hwkm_reg_set_field() - Update a register field. + * @base: HWKM MMIO base. + * @reg: register offset from @base. + * @mask: field mask. + * @shift: field shift. + * @val: new field value, right-justified. + * + * Update only the bits covered by @mask. + */ +static inline void hwkm_reg_set_field(vaddr_t base, uint32_t reg, + uint32_t mask, uint32_t shift, + uint32_t val) +{ + uint32_t regval = HWKM_REG_READ(base, reg); + + regval &= ~mask; + regval |= (val << shift) & mask; + HWKM_REG_WRITE(base, reg, regval); +} + +#endif /* __HWKM_REGS_H__ */ diff --git a/core/drivers/crypto/hwkm/sub.mk b/core/drivers/crypto/hwkm/sub.mk new file mode 100644 index 000000000..33249512b --- /dev/null +++ b/core/drivers/crypto/hwkm/sub.mk @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) 2026, Qualcomm Technologies, Inc. + +incdirs-y += include + +srcs-y += hwkm.c +srcs-y += hwkm_transaction.c diff --git a/core/drivers/crypto/sub.mk b/core/drivers/crypto/sub.mk index 3457f43cc..038211a32 100644 --- a/core/drivers/crypto/sub.mk +++ b/core/drivers/crypto/sub.mk @@ -17,3 +17,5 @@ subdirs-$(CFG_AMD_ASU_SUPPORT) += asu_driver subdirs-$(CFG_HISILICON_CRYPTO_DRIVER) += hisilicon subdirs-$(CFG_IMX_ELE) += ele + +subdirs-$(CFG_QCOM_HWKM) += hwkm