From fdd7fd814174c09ce2b3de68ba27a282a9dca3df Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 14 Apr 2026 23:31:51 +0530 Subject: [PATCH 1/4] plat-qcom: Add support for diagnostic ring buffer Implement a diagnostic ring buffer driver that preserves crash logs in IMEM across reboots for post-mortem analysis. The driver provides a write-only circular buffer that captures console output during normal operation and crash scenarios. Key features: - Write-only circular buffer in IMEM for crash log preservation - DLOAD mode detection to disable logging during download mode - Automatic buffer wraparound with overflow tracking Signed-off-by: Selvam Sathappan Periakaruppan Signed-off-by: Jorge Ramirez-Ortiz --- core/arch/arm/plat-qcom/diag_log.c | 133 +++++++++++++++++++++++++++++ core/arch/arm/plat-qcom/diag_log.h | 23 +++++ core/arch/arm/plat-qcom/sub.mk | 1 + 3 files changed, 157 insertions(+) create mode 100644 core/arch/arm/plat-qcom/diag_log.c create mode 100644 core/arch/arm/plat-qcom/diag_log.h 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/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 87297fa96..271a9c42f 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,5 +1,6 @@ global-incdirs-y += . 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 From 0f69413858c53c51159c1738583cc4fa26cbf4d2 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 14 Apr 2026 23:42:01 +0530 Subject: [PATCH 2/4] plat-qcom: Integrate diagnostic ring buffer Integrate the diagnostic ring buffer driver into the platform: - Add IMEM memory region definitions (platform_config.h) * Kodiak: 0x14680000, size 0x19000 * Lemans: 0x14680000, size 0x32000 - Enable CFG_QCOM_DIAG_LOG in debug builds (conf.mk) * Automatically enabled when CFG_TEE_CORE_DEBUG=y - Hook into trace infrastructure * Initialize DIAG during plat_trace_init() * Mirror console output via plat_trace_ext_puts() This enables crash log preservation for debugging production issues when debug builds are deployed. Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 2 ++ core/arch/arm/plat-qcom/main.c | 12 ++++++++++++ core/arch/arm/plat-qcom/platform_config.h | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index 76d3baab0..fa81ae69a 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -31,6 +31,8 @@ CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 $(call force,CFG_QCOM_PRNG,y) +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + CFG_TZDRAM_START ?= 0x1c300000 CFG_TEE_RAM_VA_SIZE ?= 0x200000 CFG_TA_RAM_VA_SIZE ?= 0x1c00000 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..7f2c9c4d5 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -64,6 +64,10 @@ #define GENI_UART_REG_BASE UL(0x994000) +/* IMEM and Diagnostic buffer */ +#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) @@ -97,6 +101,10 @@ #define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) #define GENI_UART_REG_BASE UL(0xa8c000) + +/* IMEM and Diagnostic buffer */ +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) #endif #define PAS_ID_QDSP6 1 From 6486321263e2e07a8812a830b411278ca6d589a9 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sun, 26 Apr 2026 22:40:45 +0530 Subject: [PATCH 3/4] plat-qcom: Refactor configuration into architecture-based hierarchy Introduce an architecture-centric structure for Qualcomm platform configuration to improve scalability and maintainability. Chipsets are now grouped by architecture family (e.g., Kodiak and Lemans under HOYA). Configuration is split into three levels: 1. Platform-wide (conf.mk): Maps chipsets to architecture families 2. Architecture-level (hoya/arch.mk, hoya/arch_config.h): Base platform requirements common across all chipsets in the family (CPU, cores, TZDRAM, RAMBLUR, PRNG) 3. Chipset-specific (hoya/kodiak/target.mk, hoya/lemans/target.mk): Advanced features like clock drivers, diagnostic logging, and fuse provisioning The conf.mk uses variable-based includes to automatically load the correct architecture and chipset configurations, eliminating conditional logic from architecture files. This structure allows new chipsets to start with minimal base platform support and incrementally add advanced features. It also simplifies adding new architecture families in the future. No functional changes - pure refactoring with identical configuration values and hardware addresses. Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 48 ++------- core/arch/arm/plat-qcom/hoya/arch.mk | 15 +++ core/arch/arm/plat-qcom/hoya/arch_config.h | 28 ++++++ core/arch/arm/plat-qcom/hoya/kodiak/target.mk | 21 ++++ .../arm/plat-qcom/hoya/kodiak/target_config.h | 46 +++++++++ core/arch/arm/plat-qcom/hoya/lemans/target.mk | 11 +++ .../arm/plat-qcom/hoya/lemans/target_config.h | 32 ++++++ core/arch/arm/plat-qcom/platform_config.h | 99 +------------------ core/arch/arm/plat-qcom/sub.mk | 2 + 9 files changed, 166 insertions(+), 136 deletions(-) create mode 100644 core/arch/arm/plat-qcom/hoya/arch.mk create mode 100644 core/arch/arm/plat-qcom/hoya/arch_config.h create mode 100644 core/arch/arm/plat-qcom/hoya/kodiak/target.mk create mode 100644 core/arch/arm/plat-qcom/hoya/kodiak/target_config.h create mode 100644 core/arch/arm/plat-qcom/hoya/lemans/target.mk create mode 100644 core/arch/arm/plat-qcom/hoya/lemans/target_config.h diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index fa81ae69a..ec21b872a 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -22,45 +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_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) - -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/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..76a785e6a --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -0,0 +1,46 @@ +/* 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 + +#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..f645897e9 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -0,0 +1,11 @@ +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) 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..d0b525159 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -0,0 +1,32 @@ +/* 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) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 7f2c9c4d5..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,101 +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) - -/* IMEM and Diagnostic buffer */ -#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) -#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) - -/* IMEM and Diagnostic buffer */ -#define IMEM_BASE UL(0x14680000) -#define IMEM_SIZE UL(0x32000) -#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 271a9c42f..e9faeb6f9 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,4 +1,6 @@ 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 From 3167ba57c85392f2731c7ef5d38b39c1678d2be2 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Fri, 24 Apr 2026 20:08:41 -0700 Subject: [PATCH 4/4] drivers/crypto: add Qualcomm Hardware Key Manager (HWKM) driver Add a driver for the Qualcomm Hardware Key Manager (HWKM), a hardware IP block present on Qualcomm SoCs that manages cryptographic key slots in a tamper-resistant key table. Keys stored in HWKM slots are never exposed in plaintext to software above the security level they were provisioned at; the hardware enforces per-slot access-control and usage policies. The driver exposes the following functionality to OP-TEE: - Hardware Unique Key (HUK): implements tee_otp_get_hw_unique_key() by deriving a device-unique key from the TZ UKDK L2 slot via the SYSTEM_KDF command, using a fixed software context string and the SWC BSVE binding. The derived key is cached in the driver context after the first call. - Full command set: NIST_KEYGEN, SYSTEM_KDF, KEY_WRAP_EXPORT, KEY_UNWRAP_IMPORT, KEY_SLOT_CLEAR, KEY_SLOT_RDWR, and SET_TPKEY are all implemented and exposed through a transaction queue API. Signed-off-by: Amirreza Zarrabi --- .../arm/plat-qcom/hoya/kodiak/target_config.h | 3 + core/arch/arm/plat-qcom/hoya/lemans/target.mk | 2 + .../arm/plat-qcom/hoya/lemans/target_config.h | 3 + core/drivers/crypto/hwkm/hwkm.c | 403 +++++++ core/drivers/crypto/hwkm/hwkm_transaction.c | 1070 +++++++++++++++++ core/drivers/crypto/hwkm/include/hwkm.h | 343 ++++++ core/drivers/crypto/hwkm/include/hwkm_errno.h | 139 +++ core/drivers/crypto/hwkm/include/hwkm_regs.h | 211 ++++ core/drivers/crypto/hwkm/sub.mk | 7 + core/drivers/crypto/sub.mk | 2 + 10 files changed, 2183 insertions(+) create mode 100644 core/drivers/crypto/hwkm/hwkm.c create mode 100644 core/drivers/crypto/hwkm/hwkm_transaction.c create mode 100644 core/drivers/crypto/hwkm/include/hwkm.h create mode 100644 core/drivers/crypto/hwkm/include/hwkm_errno.h create mode 100644 core/drivers/crypto/hwkm/include/hwkm_regs.h create mode 100644 core/drivers/crypto/hwkm/sub.mk diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h index 76a785e6a..57fca614b 100644 --- a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -43,4 +43,7 @@ #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 index f645897e9..b26021c1e 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -9,3 +9,5 @@ _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 index d0b525159..1808a56c3 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -29,4 +29,7 @@ #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/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