From 66f880fc1c3bbd802d4a0e893256eeb2b90238f8 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 13:42:07 +0530 Subject: [PATCH 01/10] feat(esp_tee): Support for ESP-TEE - `bootloader` component --- components/bootloader/CMakeLists.txt | 5 ++++- components/bootloader/project_include.cmake | 4 +++- components/bootloader/subproject/CMakeLists.txt | 3 ++- components/bootloader/subproject/main/bootloader_start.c | 7 ++++++- .../bootloader/subproject/main/ld/esp32c6/bootloader.ld | 1 + 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index 3c62ae818ba6..b8135bb25ce4 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -1,4 +1,5 @@ idf_build_get_property(target IDF_TARGET) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator @@ -7,7 +8,9 @@ endif() idf_component_register(PRIV_REQUIRES partition_table esptool_py) # Do not generate flash file when building bootloader or is in early expansion of the build -if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER) +# This also applies to the ESP-TEE build, as the esp_tee component only requires the +# Kconfig options from the bootloader +if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER) return() endif() diff --git a/components/bootloader/project_include.cmake b/components/bootloader/project_include.cmake index 12cf17dc70ab..3fdcea84b057 100644 --- a/components/bootloader/project_include.cmake +++ b/components/bootloader/project_include.cmake @@ -1,7 +1,9 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH}) # Do not generate flash file when building bootloader -if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER) +if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER) return() endif() diff --git a/components/bootloader/subproject/CMakeLists.txt b/components/bootloader/subproject/CMakeLists.txt index 34c7cded18a9..2a74c266d167 100644 --- a/components/bootloader/subproject/CMakeLists.txt +++ b/components/bootloader/subproject/CMakeLists.txt @@ -32,7 +32,8 @@ set(COMPONENTS main efuse esp_system - newlib) + newlib + esp_tee) # EXTRA_COMPONENT_DIRS can be populated with directories containing one or several components. # Make sure this variable contains `bootloader_components` directory of the project being compiled. diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index 55bcc8b121a9..71d83a79d031 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -53,6 +53,11 @@ void __attribute__((noreturn)) call_start_cpu0(void) bootloader_reset(); } + // 2.1 Load the TEE image +#if CONFIG_SECURE_ENABLE_TEE + bootloader_utility_load_tee_image(&bs); +#endif + // 3. Load the app image for booting bootloader_utility_load_boot_image(&bs, boot_index); } diff --git a/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld index a9eb54247ce5..9afa82140850 100644 --- a/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld @@ -71,6 +71,7 @@ SECTIONS *libbootloader_support.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) *libbootloader_support.a:bootloader_efuse.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_utility.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_utility_tee.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) From 2dee5791a60121463b7da00000a48f1608053366 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 13:19:49 +0530 Subject: [PATCH 02/10] feat(esp_tee): Support for ESP-TEE - `esptool_py`, `esp_rom` & other components --- components/esp_app_format/esp_app_desc.c | 4 +-- .../esp_partition/include/esp_partition.h | 8 ++++++ components/esp_phy/src/phy_override.c | 20 +++++++++++++- components/esp_rom/CMakeLists.txt | 6 +++++ components/esp_rom/esp32c6/ld/esp32c6.rom.ld | 12 ++++----- components/esptool_py/CMakeLists.txt | 6 +++++ components/esptool_py/project_include.cmake | 2 +- components/log/include/esp_log.h | 2 +- components/log/include/esp_log_level.h | 8 +++--- components/log/src/log_timestamp_common.c | 2 +- components/partition_table/Kconfig.projbuild | 26 +++++++++++++++++++ components/partition_table/gen_esp32part.py | 20 ++++++++++++++ .../partitions_singleapp_tee.csv | 7 +++++ .../partitions_two_ota_tee.csv | 11 ++++++++ 14 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 components/partition_table/partitions_singleapp_tee.csv create mode 100644 components/partition_table/partitions_two_ota_tee.csv diff --git a/components/esp_app_format/esp_app_desc.c b/components/esp_app_format/esp_app_desc.c index 302250b1792b..6b68b618d0d5 100644 --- a/components/esp_app_format/esp_app_desc.c +++ b/components/esp_app_format/esp_app_desc.c @@ -13,7 +13,7 @@ #include "esp_log.h" // startup_internal.h is necessary for startup function definition, which does not exist on Linux (TODO: IDF-9950) -#if !CONFIG_IDF_TARGET_LINUX +#if !CONFIG_IDF_TARGET_LINUX && !ESP_TEE_BUILD #include "esp_private/startup_internal.h" static const char *TAG = "app_init"; @@ -118,7 +118,7 @@ int esp_app_get_elf_sha256(char* dst, size_t size) // startup function definition and execution does not exist on the Linux target // (TODO: IDF-9950) -#if !CONFIG_IDF_TARGET_LINUX +#if !CONFIG_IDF_TARGET_LINUX && !ESP_TEE_BUILD ESP_SYSTEM_INIT_FN(init_show_app_info, CORE, BIT(0), 20) { // Load the current ELF SHA256 diff --git a/components/esp_partition/include/esp_partition.h b/components/esp_partition/include/esp_partition.h index d53d0cbaac26..2636bf31064f 100644 --- a/components/esp_partition/include/esp_partition.h +++ b/components/esp_partition/include/esp_partition.h @@ -95,6 +95,11 @@ typedef enum { ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition + ESP_PARTITION_SUBTYPE_APP_TEE_MIN = 0x30, //!< Base for TEE partition subtypes + ESP_PARTITION_SUBTYPE_APP_TEE_0 = ESP_PARTITION_SUBTYPE_APP_TEE_MIN + 0, //!< TEE partition 0 + ESP_PARTITION_SUBTYPE_APP_TEE_1 = ESP_PARTITION_SUBTYPE_APP_TEE_MIN + 1, //!< TEE partition 1 + ESP_PARTITION_SUBTYPE_APP_TEE_MAX = ESP_PARTITION_SUBTYPE_APP_TEE_1, //!< Max subtype of TEE partition + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition @@ -108,6 +113,9 @@ typedef enum { ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition ESP_PARTITION_SUBTYPE_DATA_LITTLEFS = 0x83, //!< LITTLEFS partition + ESP_PARTITION_SUBTYPE_DATA_TEE_OTA = 0x90, //!< TEE OTA selection partition + ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE= 0x91, //!< TEE secure storage partition + #if __has_include("extra_partition_subtypes.inc") #include "extra_partition_subtypes.inc" #endif diff --git a/components/esp_phy/src/phy_override.c b/components/esp_phy/src/phy_override.c index 2406e81dc362..4fc5857806ce 100644 --- a/components/esp_phy/src/phy_override.c +++ b/components/esp_phy/src/phy_override.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ #include "esp_private/sar_periph_ctrl.h" #include "freertos/FreeRTOS.h" +#include "hal/efuse_hal.h" /* * This file is used to override the hooks provided by the PHY lib for some system features. @@ -99,3 +100,20 @@ int16_t phy_get_tsens_value(void) return 0; #endif } + +/* NOTE:: With ESP-TEE enabled, we override certain functions from the libphy + * component archive which directly access the eFuse later (e.g. REG_READ) + * with the HAL APIs. + * + * In the future, ESP-TEE would need to protect the entire eFuse range through + * APM and expects users to use HAL APIs which would be redirected as service calls. + */ +void esp_phy_efuse_get_mac(uint8_t *mac) +{ + efuse_hal_get_mac(mac); +} + +uint32_t esp_phy_efuse_get_chip_ver_pkg(void) +{ + return efuse_hal_get_chip_ver_pkg(); +} diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index 3a09baf959b7..647da6539636 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -126,6 +126,12 @@ if(CONFIG_ESP_ROM_HAS_VERSION) rom_linker_script("version") endif() +if(ESP_TEE_BUILD) + if(target STREQUAL "esp32c6") + rom_linker_script("spiflash") + endif() +endif() + if(BOOTLOADER_BUILD) if(target STREQUAL "esp32") if(NOT CONFIG_SPI_FLASH_ROM_DRIVER_PATCH) diff --git a/components/esp_rom/esp32c6/ld/esp32c6.rom.ld b/components/esp_rom/esp32c6/ld/esp32c6.rom.ld index 2af963d50873..79737553fe86 100644 --- a/components/esp_rom/esp32c6/ld/esp32c6.rom.ld +++ b/components/esp_rom/esp32c6/ld/esp32c6.rom.ld @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -259,11 +259,11 @@ gpio_bypass_matrix_in = 0x40000714; ***************************************/ /* Functions */ -esprv_intc_int_set_priority = 0x40000718; -esprv_intc_int_set_threshold = 0x4000071c; -esprv_intc_int_enable = 0x40000720; -esprv_intc_int_disable = 0x40000724; -esprv_intc_int_set_type = 0x40000728; +PROVIDE( esprv_intc_int_set_priority = 0x40000718 ); +PROVIDE( esprv_intc_int_set_threshold = 0x4000071c ); +PROVIDE( esprv_intc_int_enable = 0x40000720 ); +PROVIDE( esprv_intc_int_disable = 0x40000724 ); +PROVIDE( esprv_intc_int_set_type = 0x40000728 ); PROVIDE( intr_handler_set = 0x4000072c ); intr_matrix_set = 0x40000730; ets_intr_lock = 0x40000734; diff --git a/components/esptool_py/CMakeLists.txt b/components/esptool_py/CMakeLists.txt index 66778dc019b5..955c63a0fa7f 100644 --- a/components/esptool_py/CMakeLists.txt +++ b/components/esptool_py/CMakeLists.txt @@ -1,9 +1,15 @@ idf_build_get_property(target IDF_TARGET) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator endif() +if(esp_tee_build) + idf_component_register() + return() +endif() + idf_component_register(REQUIRES bootloader PRIV_REQUIRES partition_table) if(NOT BOOTLOADER_BUILD) diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index de0efe407ccc..89547014ed27 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -112,7 +112,7 @@ idf_build_get_property(build_dir BUILD_DIR) idf_build_get_property(elf_name EXECUTABLE_NAME GENERATOR_EXPRESSION) idf_build_get_property(elf EXECUTABLE GENERATOR_EXPRESSION) -if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES AND NOT BOOTLOADER_BUILD) +if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES AND NOT non_os_build) set(unsigned_project_binary "${elf_name}-unsigned.bin") else() set(unsigned_project_binary "${elf_name}.bin") diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index e2cb068f436b..088650462741 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -101,7 +101,7 @@ void esp_log_writev(esp_log_level_t level, const char* tag, const char* format, esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag, ##__VA_ARGS__); \ }} while(0) -#ifndef NON_OS_BUILD +#if !NON_OS_BUILD #if defined(__cplusplus) && (__cplusplus > 201703L) #define ESP_LOGE( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, format __VA_OPT__(,) __VA_ARGS__) #define ESP_LOGW( tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, format __VA_OPT__(,) __VA_ARGS__) diff --git a/components/log/include/esp_log_level.h b/components/log/include/esp_log_level.h index 1f10660b992c..bfd390ab9e8f 100644 --- a/components/log/include/esp_log_level.h +++ b/components/log/include/esp_log_level.h @@ -32,12 +32,14 @@ typedef enum { #ifndef LOG_LOCAL_LEVEL #if BOOTLOADER_BUILD #define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL -#else // !BOOTLOADER_BUILD +#elif ESP_TEE_BUILD +#define LOG_LOCAL_LEVEL CONFIG_SECURE_TEE_LOG_LEVEL +#else #define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL -#endif // !BOOTLOADER_BUILD +#endif #endif // LOG_LOCAL_LEVEL -#ifdef NON_OS_BUILD +#if NON_OS_BUILD #define _ESP_LOG_ENABLED(log_level) (LOG_LOCAL_LEVEL >= (log_level)) #define _ESP_LOG_EARLY_ENABLED(log_level) _ESP_LOG_ENABLED(log_level) diff --git a/components/log/src/log_timestamp_common.c b/components/log/src/log_timestamp_common.c index 7905899da5b4..40aaab6c2e40 100644 --- a/components/log/src/log_timestamp_common.c +++ b/components/log/src/log_timestamp_common.c @@ -12,7 +12,7 @@ #include "esp_private/log_timestamp.h" #include "sdkconfig.h" -#ifndef NON_OS_BUILD +#if !NON_OS_BUILD #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index e3740d9dadec..c7ef8b073fad 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -100,6 +100,30 @@ menu "Partition Table" The corresponding CSV file in the IDF directory is components/partition_table/partitions_two_ota_encr_nvs.csv + config PARTITION_TABLE_SINGLE_APP_TEE + bool "Single factory app, no OTA, TEE" + depends on SECURE_ENABLE_TEE + help + This is a variation of the default "Single factory app, no OTA" partition table + that supports the ESP-TEE framework. See the Trusted Execution Environment (TEE) section + in the ESP-IDF Programmers Guide for more information. + + The corresponding CSV file in the IDF directory is + components/partition_table/partitions_singleapp_tee.csv + + config PARTITION_TABLE_TWO_OTA_TEE + bool "Two OTA definitions, TEE" + depends on SECURE_ENABLE_TEE + help + This is a basic OTA-enabled partition table with two OTA app partitions each + for the TEE and the user (REE) application. The user app partition sizes are 1536K, + so this partition table requires 4MB or larger flash size. See the + Trusted Execution Environment (TEE) section in the ESP-IDF Programmers Guide + for more information. + + The corresponding CSV file in the IDF directory is + components/partition_table/partitions_two_ota_tee.csv + endchoice config PARTITION_TABLE_CUSTOM_FILENAME @@ -122,6 +146,8 @@ menu "Partition Table" default "partitions_two_ota_coredump.csv" if PARTITION_TABLE_TWO_OTA && ESP_COREDUMP_ENABLE_TO_FLASH default "partitions_two_ota_encr_nvs.csv" if PARTITION_TABLE_TWO_OTA_ENCRYPTED_NVS default "partitions_two_ota_large.csv" if PARTITION_TABLE_TWO_OTA_LARGE && !ESP_COREDUMP_ENABLE_TO_FLASH + default "partitions_singleapp_tee.csv" if PARTITION_TABLE_SINGLE_APP_TEE + default "partitions_two_ota_tee.csv" if PARTITION_TABLE_TWO_OTA_TEE default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config PARTITION_TABLE_OFFSET diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index d8d19d6e7e4e..029ac4104eba 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -24,6 +24,8 @@ MIN_PARTITION_SUBTYPE_APP_OTA = 0x10 NUM_PARTITION_SUBTYPE_APP_OTA = 16 +MIN_PARTITION_SUBTYPE_APP_TEE = 0x30 +NUM_PARTITION_SUBTYPE_APP_TEE = 2 SECURE_NONE = None SECURE_V1 = 'v1' @@ -82,6 +84,8 @@ def get_ptype_as_int(ptype): 'fat': 0x81, 'spiffs': 0x82, 'littlefs': 0x83, + 'tee_ota': 0x90, + 'tee_sec_stg': 0x91, }, } @@ -308,6 +312,18 @@ def verify(self): critical('%s' % (p.to_csv())) raise InputError('otadata partition must have size = 0x2000') + # Above checks but for TEE otadata + otadata_duplicates = [p for p in self if p.type == TYPES['data'] and p.subtype == SUBTYPES[DATA_TYPE]['tee_ota']] + if len(otadata_duplicates) > 1: + for p in otadata_duplicates: + critical('%s' % (p.to_csv())) + raise InputError('Found multiple TEE otadata partitions. Only one partition can be defined with type="data"(1) and subtype="tee_ota"(0x90).') + + if len(otadata_duplicates) == 1 and otadata_duplicates[0].size != 0x2000: + p = otadata_duplicates[0] + critical('%s' % (p.to_csv())) + raise InputError('TEE otadata partition must have size = 0x2000') + def flash_size(self): """ Return the size that partitions will occupy in flash (ie the offset the last partition ends at) @@ -379,6 +395,10 @@ class PartitionDefinition(object): for ota_slot in range(NUM_PARTITION_SUBTYPE_APP_OTA): SUBTYPES[TYPES['app']]['ota_%d' % ota_slot] = MIN_PARTITION_SUBTYPE_APP_OTA + ota_slot + # add subtypes for the 2 TEE OTA slot values ("tee_XX, etc.") + for tee_slot in range(NUM_PARTITION_SUBTYPE_APP_TEE): + SUBTYPES[TYPES['app']]['tee_%d' % tee_slot] = MIN_PARTITION_SUBTYPE_APP_TEE + tee_slot + def __init__(self): self.name = '' self.type = None diff --git a/components/partition_table/partitions_singleapp_tee.csv b/components/partition_table/partitions_singleapp_tee.csv new file mode 100644 index 000000000000..aefbee66f6de --- /dev/null +++ b/components/partition_table/partitions_singleapp_tee.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +tee, app, tee_0, , 192K, +secure_storage, data, tee_sec_stg, , 64K, +factory, app, factory, , 1536K, +nvs, data, nvs, , 24K, +phy_init, data, phy, , 4K, diff --git a/components/partition_table/partitions_two_ota_tee.csv b/components/partition_table/partitions_two_ota_tee.csv new file mode 100644 index 000000000000..98ee581e0ed1 --- /dev/null +++ b/components/partition_table/partitions_two_ota_tee.csv @@ -0,0 +1,11 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +tee_0, app, tee_0, , 192K, +tee_1, app, tee_1, , 192K, +tee_otadata, data, tee_ota, , 8K, +secure_storage, data, tee_sec_stg, , 56K, +ota_0, app, ota_0, , 1536K, +ota_1, app, ota_1, , 1536K, +otadata, data, ota, , 8K, +nvs, data, nvs, , 24K, +phy_init, data, phy, , 4K, From ba2af7f611ce5ca86d5012605b363ca987923ba3 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 14:19:33 +0530 Subject: [PATCH 03/10] feat(esp_tee): Support for ESP-TEE - `hal`, `soc` and `freertos` components --- .../FreeRTOS-Kernel-SMP/portable/riscv/port.c | 17 ++++++++++++++-- components/hal/CMakeLists.txt | 3 ++- components/hal/efuse_hal.c | 5 +++++ components/hal/include/hal/efuse_hal.h | 5 +++++ .../soc/esp32c6/include/soc/interrupt_reg.h | 20 +++++++++++++++++++ .../soc/esp32c6/register/soc/reg_base.h | 1 + 6 files changed, 48 insertions(+), 3 deletions(-) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c index 0baffbcd82e9..d08948517da3 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c @@ -127,10 +127,23 @@ void vPortSetStackWatchpoint(void *pxStackStart) UBaseType_t ulPortSetInterruptMask(void) { int ret; - unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + unsigned old_xstatus; + +#if CONFIG_SECURE_ENABLE_TEE + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); +#else + // For non-secure configuration + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); +#endif + ret = REG_READ(INTERRUPT_CURRENT_CORE_INT_THRESH_REG); REG_WRITE(INTERRUPT_CURRENT_CORE_INT_THRESH_REG, RVHAL_EXCM_LEVEL); - RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); + +#if CONFIG_SECURE_ENABLE_TEE + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); +#else + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); +#endif /** * In theory, this function should not return immediately as there is a * delay between the moment we mask the interrupt threshold register and diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 698bd8adffb8..422a4965b592 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -1,4 +1,5 @@ idf_build_get_property(target IDF_TARGET) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) set(srcs "hal_utils.c") set(includes "platform_port/include") @@ -44,7 +45,7 @@ if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP) endif() endif() -if(NOT BOOTLOADER_BUILD) +if(NOT BOOTLOADER_BUILD AND NOT esp_tee_build) list(APPEND srcs "color_hal.c") if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP) diff --git a/components/hal/efuse_hal.c b/components/hal/efuse_hal.c index 28cdaed788e8..43a007aca251 100644 --- a/components/hal/efuse_hal.c +++ b/components/hal/efuse_hal.c @@ -33,6 +33,11 @@ IRAM_ATTR bool efuse_hal_get_disable_wafer_version_major(void) return efuse_ll_get_disable_wafer_version_major(); } +IRAM_ATTR uint32_t efuse_hal_get_chip_ver_pkg(void) +{ + return efuse_ll_get_chip_ver_pkg(); +} + IRAM_ATTR bool efuse_hal_get_disable_blk_version_major(void) { return efuse_ll_get_disable_blk_version_major(); diff --git a/components/hal/include/hal/efuse_hal.h b/components/hal/include/hal/efuse_hal.h index 73f092ee0003..cfed79f59152 100644 --- a/components/hal/include/hal/efuse_hal.h +++ b/components/hal/include/hal/efuse_hal.h @@ -69,6 +69,11 @@ uint32_t efuse_hal_get_major_chip_version(void); */ uint32_t efuse_hal_get_minor_chip_version(void); +/** + * @brief Returns the chip package version + */ +uint32_t efuse_hal_get_chip_ver_pkg(void); + #if SOC_EFUSE_ECDSA_KEY /** * @brief Set the efuse block that should be used as ECDSA private key diff --git a/components/soc/esp32c6/include/soc/interrupt_reg.h b/components/soc/esp32c6/include/soc/interrupt_reg.h index 50f8a6e1c651..e111811dbff9 100644 --- a/components/soc/esp32c6/include/soc/interrupt_reg.h +++ b/components/soc/esp32c6/include/soc/interrupt_reg.h @@ -10,16 +10,36 @@ #include "soc/plic_reg.h" #include "soc/soc_caps.h" +#ifdef __has_include +# if __has_include("sdkconfig.h") +# include "sdkconfig.h" +# endif +#endif + +#if CONFIG_SECURE_ENABLE_TEE +#define INTERRUPT_PRIO_REG(n) (PLIC_UXINT0_PRI_REG + (n)*4) +#define INTERRUPT_CURRENT_CORE_INT_THRESH_REG PLIC_UXINT_THRESH_REG +#else #define INTERRUPT_PRIO_REG(n) (PLIC_MXINT0_PRI_REG + (n)*4) #define INTERRUPT_CURRENT_CORE_INT_THRESH_REG PLIC_MXINT_THRESH_REG +#endif /** * ESP32C6 should use the PLIC controller as the interrupt controller instead of INTC (SOC_INT_PLIC_SUPPORTED = y) * Keep the following macros for backward compatibility reasons */ +#if CONFIG_SECURE_ENABLE_TEE +#define INTERRUPT_CORE0_CPU_INT_ENABLE_REG PLIC_UXINT_ENABLE_REG +#define INTERRUPT_CORE0_CPU_INT_THRESH_REG PLIC_UXINT_THRESH_REG +#define INTERRUPT_CORE0_CPU_INT_CLEAR_REG PLIC_UXINT_CLEAR_REG +#define INTERRUPT_CORE0_CPU_INT_TYPE_REG PLIC_UXINT_TYPE_REG +#define INTC_INT_PRIO_REG(n) (PLIC_UXINT0_PRI_REG + (n)*4) +#else #define INTERRUPT_CORE0_CPU_INT_ENABLE_REG PLIC_MXINT_ENABLE_REG #define INTERRUPT_CORE0_CPU_INT_THRESH_REG PLIC_MXINT_THRESH_REG #define INTERRUPT_CORE0_CPU_INT_CLEAR_REG PLIC_MXINT_CLEAR_REG #define INTERRUPT_CORE0_CPU_INT_TYPE_REG PLIC_MXINT_TYPE_REG #define INTC_INT_PRIO_REG(n) (PLIC_MXINT0_PRI_REG + (n)*4) +#endif + #define DR_REG_INTERRUPT_BASE DR_REG_INTERRUPT_MATRIX_BASE diff --git a/components/soc/esp32c6/register/soc/reg_base.h b/components/soc/esp32c6/register/soc/reg_base.h index f2da385dc10d..603ccc602eba 100644 --- a/components/soc/esp32c6/register/soc/reg_base.h +++ b/components/soc/esp32c6/register/soc/reg_base.h @@ -8,6 +8,7 @@ #define DR_REG_PLIC_UX_BASE 0x20001400 #define DR_REG_CLINT_M_BASE 0x20001800 #define DR_REG_CLINT_U_BASE 0x20001C00 +#define DR_REG_CLINT_U_END 0x20002000 #define DR_REG_UART_BASE 0x60000000 #define DR_REG_UART1_BASE 0x60001000 From 05e31e51482fdbaad257cd6b4491fed4b192cc37 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 16:07:59 +0530 Subject: [PATCH 04/10] feat(esp_tee): Support for ESP-TEE - `mbedtls` component --- components/mbedtls/CMakeLists.txt | 15 +- .../esp_tee/esp_tee_crypto_shared_gdma.c | 128 ++++++++++++++++++ .../esp_tee/esp_tee_crypto_shared_gdma.h | 37 +++++ .../mbedtls/esp_tee/esp_tee_mbedtls.cmake | 61 +++++++++ .../mbedtls/esp_tee/esp_tee_mbedtls_config.h | 57 ++++++++ components/mbedtls/port/aes/dma/esp_aes.c | 6 + .../mbedtls/port/aes/dma/esp_aes_dma_core.c | 59 +++++--- components/mbedtls/port/sha/dma/sha.c | 14 +- 8 files changed, 348 insertions(+), 29 deletions(-) create mode 100644 components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.c create mode 100644 components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.h create mode 100644 components/mbedtls/esp_tee/esp_tee_mbedtls.cmake create mode 100644 components/mbedtls/esp_tee/esp_tee_mbedtls_config.h diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index d3f4fe40a7d6..8bc72f61791d 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -1,12 +1,17 @@ idf_build_get_property(idf_target IDF_TARGET) idf_build_get_property(python PYTHON) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) -if(NOT ${IDF_TARGET} STREQUAL "linux") -set(priv_requires soc esp_hw_support) - -if(NOT BOOTLOADER_BUILD) - list(APPEND priv_requires esp_pm) +if(esp_tee_build) + include(${COMPONENT_DIR}/esp_tee/esp_tee_mbedtls.cmake) + return() endif() + +if(NOT ${IDF_TARGET} STREQUAL "linux") + set(priv_requires soc esp_hw_support) + if(NOT BOOTLOADER_BUILD) + list(APPEND priv_requires esp_pm) + endif() endif() set(mbedtls_srcs "") diff --git a/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.c b/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.c new file mode 100644 index 000000000000..6c0c47b4f77e --- /dev/null +++ b/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.c @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include "esp_err.h" + +#include "mbedtls/aes.h" +#include "esp_crypto_dma.h" + +#include "hal/clk_gate_ll.h" +#include "hal/gdma_ll.h" +#include "hal/gdma_types.h" +#include "hal/aes_hal.h" + +#include "soc/lldesc.h" +#include "soc/periph_defs.h" +#include "soc/gdma_channel.h" +#include "soc/gdma_struct.h" +#include "soc/soc_caps.h" + +#define TEE_CRYPTO_GDMA_CH (0) + +/* + * NOTE: [ESP-TEE] This is a low-level (LL), non-OS version of + * port/crypto_shared_gdma/esp_crypto_shared_gdma.c that defines + * the shared DMA layer for the AES and SHA peripherals for ESP-TEE. + */ + +/* ---------------------------------------------- Shared GDMA layer for AES/SHA crypto ------------------------------------------------- */ + +static void crypto_shared_gdma_init(void) +{ + // enable APB to access GDMA registers + periph_ll_enable_clk_clear_rst(PERIPH_GDMA_MODULE); + + // enable gdma clock + gdma_ll_force_enable_reg_clock(&GDMA, true); + + // setting the transfer ability + gdma_ll_tx_enable_data_burst(&GDMA, TEE_CRYPTO_GDMA_CH, true); + gdma_ll_tx_enable_descriptor_burst(&GDMA, TEE_CRYPTO_GDMA_CH, true); + + gdma_ll_rx_enable_data_burst(&GDMA, TEE_CRYPTO_GDMA_CH, false); + gdma_ll_rx_enable_descriptor_burst(&GDMA, TEE_CRYPTO_GDMA_CH, true); + +#if SOC_GDMA_SUPPORT_PSRAM + gdma_ll_tx_set_block_size_psram(&GDMA, TEE_CRYPTO_GDMA_CH, GDMA_LL_EXT_MEM_BK_SIZE_16B); + gdma_ll_rx_set_block_size_psram(&GDMA, TEE_CRYPTO_GDMA_CH, GDMA_LL_EXT_MEM_BK_SIZE_16B); +#endif // SOC_GDMA_SUPPORT_PSRAM + + gdma_ll_tx_reset_channel(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_tx_connect_to_periph(&GDMA, TEE_CRYPTO_GDMA_CH, GDMA_TRIG_PERIPH_M2M, SOC_GDMA_TRIG_PERIPH_M2M0); + + gdma_ll_rx_reset_channel(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_rx_connect_to_periph(&GDMA, TEE_CRYPTO_GDMA_CH, GDMA_TRIG_PERIPH_M2M, SOC_GDMA_TRIG_PERIPH_M2M0); + +} + +esp_err_t esp_tee_crypto_shared_gdma_start(const crypto_dma_desc_t *input, const crypto_dma_desc_t *output, gdma_trigger_peripheral_t periph) +{ + int periph_inst_id = SOC_GDMA_TRIG_PERIPH_M2M0; + if (periph == GDMA_TRIG_PERIPH_SHA) { + periph_inst_id = SOC_GDMA_TRIG_PERIPH_SHA0; + } else if (periph == GDMA_TRIG_PERIPH_AES) { + periph_inst_id = SOC_GDMA_TRIG_PERIPH_AES0; + } else { + return ESP_ERR_INVALID_ARG; + } + + crypto_shared_gdma_init(); + + gdma_ll_tx_disconnect_from_periph(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_rx_disconnect_from_periph(&GDMA, TEE_CRYPTO_GDMA_CH); + + gdma_ll_tx_reset_channel(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_tx_connect_to_periph(&GDMA, TEE_CRYPTO_GDMA_CH, periph, periph_inst_id); + + gdma_ll_rx_reset_channel(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_rx_connect_to_periph(&GDMA, TEE_CRYPTO_GDMA_CH, periph, periph_inst_id); + + gdma_ll_tx_set_desc_addr(&GDMA, TEE_CRYPTO_GDMA_CH, (intptr_t)input); + gdma_ll_tx_start(&GDMA, TEE_CRYPTO_GDMA_CH); + + gdma_ll_rx_set_desc_addr(&GDMA, TEE_CRYPTO_GDMA_CH, (intptr_t)output); + gdma_ll_rx_start(&GDMA, TEE_CRYPTO_GDMA_CH); + + return ESP_OK; +} + +void esp_tee_crypto_shared_gdma_free(void) +{ + gdma_ll_tx_stop(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_rx_stop(&GDMA, TEE_CRYPTO_GDMA_CH); + + gdma_ll_tx_disconnect_from_periph(&GDMA, TEE_CRYPTO_GDMA_CH); + gdma_ll_rx_disconnect_from_periph(&GDMA, TEE_CRYPTO_GDMA_CH); + + gdma_ll_tx_set_priority(&GDMA, TEE_CRYPTO_GDMA_CH, 0); + gdma_ll_rx_set_priority(&GDMA, TEE_CRYPTO_GDMA_CH, 0); + + // disable gdma clock + gdma_ll_force_enable_reg_clock(&GDMA, false); + + // disable APB for GDMA registers + periph_ll_disable_clk_set_rst(PERIPH_GDMA_MODULE); +} + +/* ---------------------------------------------- DMA Implementations: AES ------------------------------------------------- */ + +esp_err_t esp_aes_dma_start(const crypto_dma_desc_t *input, const crypto_dma_desc_t *output) +{ + return esp_tee_crypto_shared_gdma_start(input, output, GDMA_TRIG_PERIPH_AES); +} + +bool esp_aes_dma_done(crypto_dma_desc_t *output) +{ + return (output->dw0.owner == 0); +} + +/* ---------------------------------------------- DMA Implementations: SHA ------------------------------------------------- */ + +esp_err_t esp_sha_dma_start(const crypto_dma_desc_t *input) +{ + return esp_tee_crypto_shared_gdma_start(input, NULL, GDMA_TRIG_PERIPH_SHA); +} diff --git a/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.h b/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.h new file mode 100644 index 000000000000..21dfac04f333 --- /dev/null +++ b/components/mbedtls/esp_tee/esp_tee_crypto_shared_gdma.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "hal/gdma_types.h" +#include "esp_crypto_dma.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Start a GDMA transfer on the shared crypto DMA channel for ESP-TEE + * + * @note This is a low-level (LL), non-OS version of esp_crypto_shared_gdma_start() + * + * @param input Input linked list descriptor (crypto_dma_desc_t *) + * @param output Output linked list descriptor (crypto_dma_desc_t *) + * @param periph Crypto peripheral to connect the DMA to, either GDMA_TRIG_PERIPH_AES or + * GDMA_TRIG_PERIPH_SHA + * @return esp_err_t ESP_OK on success, ESP_ERR_INVALID_ARG if invalid peripheral specified + */ +esp_err_t esp_tee_crypto_shared_gdma_start(const crypto_dma_desc_t *input, const crypto_dma_desc_t *output, gdma_trigger_peripheral_t periph); + +/** + * @brief Frees the TEE-specific shared crypto DMA channel. + * + * @note This is a low-level (LL), non-OS version of esp_crypto_shared_gdma_free() + */ +void esp_tee_crypto_shared_gdma_free(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake b/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake new file mode 100644 index 000000000000..7a08feba3d8a --- /dev/null +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake @@ -0,0 +1,61 @@ +idf_component_get_property(hal_dir hal COMPONENT_DIR) +idf_component_get_property(heap_dir heap COMPONENT_DIR) + +set(priv_requires soc esp_hw_support) +set(include_dirs "${COMPONENT_DIR}/port/include" + "${COMPONENT_DIR}/mbedtls/include" + "${COMPONENT_DIR}/mbedtls/library") + +# Shared GDMA layer for TEE +set(srcs "${COMPONENT_DIR}/esp_tee/esp_tee_crypto_shared_gdma.c") + +# HAL for the AES/SHA peripherals +list(APPEND srcs "${hal_dir}/aes_hal.c" + "${hal_dir}/sha_hal.c") + +list(APPEND include_dirs "${COMPONENT_DIR}/port/aes/include" + "${COMPONENT_DIR}/port/aes/dma/include" + "${COMPONENT_DIR}/port/sha/dma/include") + +list(APPEND srcs "${COMPONENT_DIR}/port/aes/esp_aes_common.c" + "${COMPONENT_DIR}/port/aes/dma/esp_aes.c" + "${COMPONENT_DIR}/port/aes/dma/esp_aes_dma_core.c") + +list(APPEND srcs "${COMPONENT_DIR}/port/sha/dma/sha.c" + "${COMPONENT_DIR}/port/sha/esp_sha.c") + +# Supporting headers +list(APPEND include_dirs "${heap_dir}/include") + +idf_component_register(INCLUDE_DIRS "${include_dirs}" + PRIV_REQUIRES "${priv_requires}" + SRCS "${srcs}") + +# Only build mbedtls libraries +set(ENABLE_TESTING CACHE BOOL OFF) +set(ENABLE_PROGRAMS CACHE BOOL OFF) + +# Use pre-generated source files in mbedtls repository +set(GEN_FILES CACHE BOOL OFF) + +# Needed to for include_next includes to work from within mbedtls +include_directories("${COMPONENT_DIR}/port/include") + +# Import mbedtls library targets +add_subdirectory(mbedtls) + +set(mbedtls_targets mbedcrypto) + +foreach(target ${mbedtls_targets}) + target_compile_definitions(${target} PUBLIC + -DMBEDTLS_CONFIG_FILE="${COMPONENT_DIR}/esp_tee/esp_tee_mbedtls_config.h") +endforeach() + +target_link_libraries(${COMPONENT_LIB} INTERFACE ${mbedtls_targets}) + +if(CONFIG_MBEDTLS_HARDWARE_SHA) + target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/sha/dma/esp_sha1.c" + "${COMPONENT_DIR}/port/sha/dma/esp_sha256.c" + "${COMPONENT_DIR}/port/sha/dma/esp_sha512.c" + ) +endif() diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h new file mode 100644 index 000000000000..665a009e1c4a --- /dev/null +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h @@ -0,0 +1,57 @@ +/** + * + * \brief Default mbedTLS configuration options for the ESP-TEE framework + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + * + * SPDX-FileCopyrightText: The Mbed TLS Contributors + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ESP_TEE_MBEDTLS_CONFIG_H +#define ESP_TEE_MBEDTLS_CONFIG_H + +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_HAVE_TIME +#define MBEDTLS_PLATFORM_MS_TIME_ALT +#undef MBEDTLS_TIMING_C +#define MBEDTLS_PLATFORM_C + +#define MBEDTLS_CIPHER_C +#define MBEDTLS_AES_C +#define MBEDTLS_GCM_C + +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_C +#define MBEDTLS_ECDSA_C + +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA256_C + +#ifdef CONFIG_MBEDTLS_HARDWARE_SHA +#define MBEDTLS_SHA1_ALT +#define MBEDTLS_SHA224_ALT +#define MBEDTLS_SHA256_ALT +#endif + +#define MBEDTLS_ENTROPY_C + +#endif /* ESP_TEE_MBEDTLS_CONFIG_H */ diff --git a/components/mbedtls/port/aes/dma/esp_aes.c b/components/mbedtls/port/aes/dma/esp_aes.c index be87512e79c8..28af391bf359 100644 --- a/components/mbedtls/port/aes/dma/esp_aes.c +++ b/components/mbedtls/port/aes/dma/esp_aes.c @@ -36,8 +36,14 @@ #include "esp_private/esp_crypto_lock_internal.h" #if SOC_AES_GDMA +#if !ESP_TEE_BUILD #define AES_LOCK() esp_crypto_sha_aes_lock_acquire() #define AES_RELEASE() esp_crypto_sha_aes_lock_release() +#else +#define AES_RCC_ATOMIC() +#define AES_LOCK() +#define AES_RELEASE() +#endif #elif SOC_AES_CRYPTO_DMA #define AES_LOCK() esp_crypto_dma_lock_acquire() #define AES_RELEASE() esp_crypto_dma_lock_release() diff --git a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c index f870cf1f51a3..b68aba8666d9 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -6,33 +6,37 @@ #include #include #include "esp_attr.h" -#include "esp_cache.h" #include "esp_check.h" #include "esp_err.h" #include "esp_heap_caps.h" #include "esp_intr_alloc.h" #include "esp_log.h" #include "esp_memory_utils.h" -#include "esp_private/esp_cache_private.h" #include "esp_private/periph_ctrl.h" #include "soc/soc_caps.h" #include "sdkconfig.h" -#if CONFIG_PM_ENABLE -#include "esp_pm.h" -#endif #include "hal/aes_hal.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" #include "esp_aes_dma_priv.h" #include "esp_aes_internal.h" #include "esp_crypto_dma.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" - #include "mbedtls/aes.h" #include "mbedtls/platform_util.h" +#if !ESP_TEE_BUILD +#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" +#if CONFIG_PM_ENABLE +#include "esp_pm.h" +#endif +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#endif + #if SOC_AES_SUPPORT_GCM #include "aes/esp_aes_gcm.h" #endif @@ -60,6 +64,7 @@ */ #define AES_WAIT_INTR_TIMEOUT_MS 2000 +#if !ESP_TEE_BUILD #if defined(CONFIG_MBEDTLS_AES_USE_INTERRUPT) static SemaphoreHandle_t op_complete_sem; #if defined(CONFIG_PM_ENABLE) @@ -67,6 +72,9 @@ static esp_pm_lock_handle_t s_pm_cpu_lock; static esp_pm_lock_handle_t s_pm_sleep_lock; #endif #endif +#else +extern bool intr_flag; +#endif static const char *TAG = "esp-aes"; @@ -82,6 +90,7 @@ static bool s_check_dma_capable(const void *p) } #if defined (CONFIG_MBEDTLS_AES_USE_INTERRUPT) +#if !ESP_TEE_BUILD static IRAM_ATTR void esp_aes_complete_isr(void *arg) { BaseType_t higher_woken; @@ -112,12 +121,14 @@ void esp_aes_intr_alloc(void) assert(op_complete_sem != NULL); } } +#endif static esp_err_t esp_aes_isr_initialise( void ) { aes_hal_interrupt_clear(); aes_hal_interrupt_enable(true); +#if !ESP_TEE_BUILD /* AES is clocked proportionally to CPU clock, take power management lock */ #ifdef CONFIG_PM_ENABLE if (s_pm_cpu_lock == NULL) { @@ -133,6 +144,9 @@ static esp_err_t esp_aes_isr_initialise( void ) esp_pm_lock_acquire(s_pm_cpu_lock); esp_pm_lock_acquire(s_pm_sleep_lock); #endif +#else + intr_flag = true; +#endif return ESP_OK; } @@ -153,6 +167,7 @@ static int esp_aes_dma_wait_complete(bool use_intr, crypto_dma_desc_t *output_de { #if defined (CONFIG_MBEDTLS_AES_USE_INTERRUPT) if (use_intr) { +#if !ESP_TEE_BUILD if (!xSemaphoreTake(op_complete_sem, AES_WAIT_INTR_TIMEOUT_MS / portTICK_PERIOD_MS)) { /* indicates a fundamental problem with driver */ ESP_LOGE(TAG, "Timed out waiting for completion of AES Interrupt"); @@ -162,6 +177,15 @@ static int esp_aes_dma_wait_complete(bool use_intr, crypto_dma_desc_t *output_de esp_pm_lock_release(s_pm_cpu_lock); esp_pm_lock_release(s_pm_sleep_lock); #endif // CONFIG_PM_ENABLE +#else + /* NOTE: ESP-TEE does not support multitasking - secure service calls are serialized. + * When waiting for AES interrupt here, we simply busy-wait since there are no + * other tasks to switch to. Note that REE interrupts could still preempt us. + */ + while (intr_flag) { + esp_rom_delay_us(1); + } +#endif } #endif /* Checking this if interrupt is used also, to avoid @@ -175,23 +199,14 @@ static int esp_aes_dma_wait_complete(bool use_intr, crypto_dma_desc_t *output_de static inline size_t get_cache_line_size(const void *addr) { - esp_err_t ret = ESP_FAIL; - size_t cache_line_size = 0; - + uint32_t cache_level = #if (CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE) - if (esp_ptr_external_ram(addr)) { - ret = esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &cache_line_size); - } else + esp_ptr_external_ram(addr) ? CACHE_LL_LEVEL_EXT_MEM : CACHE_LL_LEVEL_INT_MEM; +#else + CACHE_LL_LEVEL_INT_MEM; #endif - { - ret = esp_cache_get_alignment(MALLOC_CAP_DMA, &cache_line_size); - } - - if (ret != ESP_OK) { - return 0; - } - return cache_line_size; + return (size_t)cache_hal_get_cache_line_size(cache_level, CACHE_TYPE_DATA); } /* Output buffers in external ram needs to be 16-byte aligned and DMA can't access input in the iCache mem range, diff --git a/components/mbedtls/port/sha/dma/sha.c b/components/mbedtls/port/sha/dma/sha.c index f3c6b17a937f..31f89d7d7fc8 100644 --- a/components/mbedtls/port/sha/dma/sha.c +++ b/components/mbedtls/port/sha/dma/sha.c @@ -30,19 +30,23 @@ #include #include "esp_private/esp_crypto_lock_internal.h" -#include "esp_private/esp_cache_private.h" + #include "esp_log.h" #include "esp_memory_utils.h" #include "esp_crypto_lock.h" #include "esp_attr.h" #include "esp_crypto_dma.h" -#include "esp_cache.h" +#include "esp_heap_caps.h" #include "hal/dma_types.h" #include "soc/ext_mem_defs.h" #include "soc/periph_defs.h" +#if !ESP_TEE_BUILD +#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +#endif #include "esp_private/periph_ctrl.h" #include "sys/param.h" @@ -59,8 +63,14 @@ #endif /* SOC_AXI_DMA_EXT_MEM_ENC_ALIGNMENT */ #if SOC_SHA_GDMA +#if !ESP_TEE_BUILD #define SHA_LOCK() esp_crypto_sha_aes_lock_acquire() #define SHA_RELEASE() esp_crypto_sha_aes_lock_release() +#else +#define SHA_RCC_ATOMIC() +#define SHA_LOCK() +#define SHA_RELEASE() +#endif #elif SOC_SHA_CRYPTO_DMA #define SHA_LOCK() esp_crypto_dma_lock_acquire() #define SHA_RELEASE() esp_crypto_dma_lock_release() From f254f93594f2b5fe6cfb97505f47058f33bf2959 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 16:26:06 +0530 Subject: [PATCH 05/10] feat(esp_tee): Support for ESP-TEE - `esp_hw_support` component --- components/esp_hw_support/CMakeLists.txt | 3 ++ components/esp_hw_support/esp_clk.c | 15 +++++++++- components/esp_hw_support/hw_random.c | 7 +++-- components/esp_hw_support/include/esp_cpu.h | 29 ++++++++++++++++++- .../include/esp_private/regi2c_ctrl.h | 6 ++-- components/esp_hw_support/intr_alloc.c | 4 +++ .../esp_hw_support/lowpower/CMakeLists.txt | 4 ++- .../port/esp32c6/CMakeLists.txt | 4 ++- .../port/esp32c6/cpu_region_protect.c | 12 ++++++++ .../port/esp32c6/esp_cpu_intr.c | 13 ++++++++- 10 files changed, 87 insertions(+), 10 deletions(-) diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 5652bcf14020..e0ccdbce1730 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -145,6 +145,9 @@ if(NOT non_os_build) list(APPEND srcs "esp_clock_output.c") endif() else() + if(ESP_TEE_BUILD) + list(APPEND srcs "esp_clk.c" "hw_random.c") + endif() # Requires "_esp_error_check_failed()" function list(APPEND priv_requires "esp_system") endif() diff --git a/components/esp_hw_support/esp_clk.c b/components/esp_hw_support/esp_clk.c index c4a2c0c207ff..2f5d1b77550f 100644 --- a/components/esp_hw_support/esp_clk.c +++ b/components/esp_hw_support/esp_clk.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,7 +9,10 @@ #include #include +#if !NON_OS_BUILD #include "freertos/FreeRTOS.h" +#endif + #include "esp_attr.h" #include "soc/rtc.h" #include "soc/soc_caps.h" @@ -52,7 +55,11 @@ // g_ticks_us defined in ROMs for PRO and APP CPU extern uint32_t g_ticks_per_us_pro; +// Any code utilizing locks, which depend on FreeRTOS, should be omitted +// when building for Non-OS environments +#if !NON_OS_BUILD static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED; +#endif #if SOC_RTC_MEM_SUPPORTED typedef struct { @@ -64,6 +71,7 @@ typedef struct { _Static_assert(sizeof(retain_mem_t) == 24, "retain_mem_t must be 24 bytes"); _Static_assert(offsetof(retain_mem_t, checksum) == sizeof(retain_mem_t) - sizeof(uint32_t), "Wrong offset for checksum field in retain_mem_t structure"); +#if !NON_OS_BUILD static __attribute__((section(".rtc_timer_data_in_rtc_mem"))) retain_mem_t s_rtc_timer_retain_mem; static uint32_t calc_checksum(void) @@ -77,6 +85,7 @@ static uint32_t calc_checksum(void) return checksum; } #define IS_RETAIN_MEM_VALID() (s_rtc_timer_retain_mem.checksum == calc_checksum()) +#endif // NON_OS_BUILD #endif // SOC_RTC_MEM_SUPPORTED inline static int IRAM_ATTR s_get_cpu_freq_mhz(void) @@ -108,6 +117,7 @@ int IRAM_ATTR esp_clk_xtal_freq(void) return rtc_clk_xtal_freq_get() * MHZ; } +#if !NON_OS_BUILD uint64_t esp_rtc_get_time_us(void) { portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock); @@ -161,6 +171,7 @@ uint64_t esp_rtc_get_time_us(void) return esp_rtc_time_us; #endif } +#endif void esp_clk_slowclk_cal_set(uint32_t new_cal) { @@ -214,6 +225,7 @@ uint64_t esp_clk_rtc_time(void) #endif } +#if !NON_OS_BUILD void esp_clk_private_lock(void) { portENTER_CRITICAL(&s_esp_rtc_time_lock); @@ -223,3 +235,4 @@ void esp_clk_private_unlock(void) { portEXIT_CRITICAL(&s_esp_rtc_time_lock); } +#endif diff --git a/components/esp_hw_support/hw_random.c b/components/esp_hw_support/hw_random.c index e83b767d96a4..4f43e39d5369 100644 --- a/components/esp_hw_support/hw_random.c +++ b/components/esp_hw_support/hw_random.c @@ -13,9 +13,12 @@ #include "esp_cpu.h" #include "soc/wdev_reg.h" #include "esp_private/esp_clk.h" -#include "esp_private/startup_internal.h" #include "soc/soc_caps.h" +#if !ESP_TEE_BUILD +#include "esp_private/startup_internal.h" +#endif + #if SOC_LP_TIMER_SUPPORTED #include "hal/lp_timer_hal.h" #endif @@ -100,7 +103,7 @@ void esp_fill_random(void *buf, size_t len) } } -#if SOC_RNG_CLOCK_IS_INDEPENDENT +#if SOC_RNG_CLOCK_IS_INDEPENDENT && !ESP_TEE_BUILD ESP_SYSTEM_INIT_FN(init_rng_clock, SECONDARY, BIT(0), 102) { _lp_clkrst_ll_enable_rng_clock(true); diff --git a/components/esp_hw_support/include/esp_cpu.h b/components/esp_hw_support/include/esp_cpu.h index f0f441b713ea..b0f3e89ed538 100644 --- a/components/esp_hw_support/include/esp_cpu.h +++ b/components/esp_hw_support/include/esp_cpu.h @@ -15,6 +15,7 @@ #include "xtensa_api.h" #include "xt_utils.h" #elif __riscv +#include "riscv/csr.h" #include "riscv/rv_utils.h" #endif #include "esp_intr_alloc.h" @@ -129,6 +130,27 @@ FORCE_INLINE_ATTR __attribute__((pure)) int esp_cpu_get_core_id(void) return (int)rv_utils_get_core_id(); #endif } +/** + * @brief Get the current [RISC-V] CPU core's privilege level + * + * This function returns the current privilege level of the CPU core executing + * this function. + * + * @return The current CPU core's privilege level, -1 if not supported. + */ + +FORCE_INLINE_ATTR __attribute__((always_inline)) int esp_cpu_get_curr_privilege_level(void) +{ +#ifdef __XTENSA__ + return -1; +#else +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 + return PRV_M; +#else + return RV_READ_CSR(CSR_PRV_MODE); +#endif +#endif +} /** * @brief Read the current stack pointer address @@ -229,7 +251,7 @@ FORCE_INLINE_ATTR void esp_cpu_intr_set_ivt_addr(const void *ivt_addr) #ifdef __XTENSA__ xt_utils_set_vecbase((uint32_t)ivt_addr); #else - rv_utils_set_mtvec((uint32_t)ivt_addr); + rv_utils_set_xtvec((uint32_t)ivt_addr); #endif } @@ -429,9 +451,14 @@ FORCE_INLINE_ATTR void esp_cpu_intr_edge_ack(int intr_num) assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM); #ifdef __XTENSA__ xthal_set_intclear((unsigned) (1 << intr_num)); +#else +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + extern esprv_int_mgmt_t esp_tee_intr_sec_srv_cb; + esp_tee_intr_sec_srv_cb(2, TEE_INTR_EDGE_ACK_SRV_ID, intr_num); #else rv_utils_intr_edge_ack((unsigned) intr_num); #endif +#endif } /* -------------------------------------------------- Memory Ports ----------------------------------------------------- diff --git a/components/esp_hw_support/include/esp_private/regi2c_ctrl.h b/components/esp_hw_support/include/esp_private/regi2c_ctrl.h index 4be8502eaf1e..6be822733b2e 100644 --- a/components/esp_hw_support/include/esp_private/regi2c_ctrl.h +++ b/components/esp_hw_support/include/esp_private/regi2c_ctrl.h @@ -61,9 +61,9 @@ extern "C" { #define regi2c_write_reg_mask_raw esp_rom_regi2c_write_mask -#ifdef BOOTLOADER_BUILD +#if NON_OS_BUILD /** - * If compiling for the bootloader, ROM functions can be called directly, + * If compiling for the non-FreeRTOS builds (e.g. bootloader), ROM functions can be called directly, * without the need of a lock. */ #define regi2c_ctrl_read_reg regi2c_read_reg_raw @@ -83,7 +83,7 @@ void regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, void regi2c_enter_critical(void); void regi2c_exit_critical(void); -#endif // BOOTLOADER_BUILD +#endif // NON_OS_BUILD /* Convenience macros for the above functions, these use register definitions * from regi2c_xxx.h header files. diff --git a/components/esp_hw_support/intr_alloc.c b/components/esp_hw_support/intr_alloc.c index dec1d1e24b92..4365aa31a63d 100644 --- a/components/esp_hw_support/intr_alloc.c +++ b/components/esp_hw_support/intr_alloc.c @@ -649,9 +649,13 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre } #endif +/* NOTE: ESP-TEE is responsible for all interrupt-related configurations + * when enabled. The following code is not applicable in that case */ +#if !CONFIG_SECURE_ENABLE_TEE #if SOC_INT_PLIC_SUPPORTED /* Make sure the interrupt is not delegated to user mode (IDF uses machine mode only) */ RV_CLEAR_CSR(mideleg, BIT(intr)); +#endif #endif portEXIT_CRITICAL(&spinlock); diff --git a/components/esp_hw_support/lowpower/CMakeLists.txt b/components/esp_hw_support/lowpower/CMakeLists.txt index 9525e680036c..2d4124bb6181 100644 --- a/components/esp_hw_support/lowpower/CMakeLists.txt +++ b/components/esp_hw_support/lowpower/CMakeLists.txt @@ -1,4 +1,6 @@ -if(BOOTLOADER_BUILD) +idf_build_get_property(non_os_build NON_OS_BUILD) + +if(non_os_build) return() endif() diff --git a/components/esp_hw_support/port/esp32c6/CMakeLists.txt b/components/esp_hw_support/port/esp32c6/CMakeLists.txt index d62fdf06eb45..ed1586db48ef 100644 --- a/components/esp_hw_support/port/esp32c6/CMakeLists.txt +++ b/components/esp_hw_support/port/esp32c6/CMakeLists.txt @@ -1,3 +1,5 @@ +idf_build_get_property(non_os_build NON_OS_BUILD) + set(srcs "rtc_clk_init.c" "rtc_clk.c" "pmu_param.c" @@ -8,7 +10,7 @@ set(srcs "rtc_clk_init.c" "ocode_init.c" ) -if(NOT BOOTLOADER_BUILD) +if(NOT non_os_build) list(APPEND srcs "sar_periph_ctrl.c") endif() diff --git a/components/esp_hw_support/port/esp32c6/cpu_region_protect.c b/components/esp_hw_support/port/esp32c6/cpu_region_protect.c index e385fb607d1b..512fe08ae2a0 100644 --- a/components/esp_hw_support/port/esp32c6/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c6/cpu_region_protect.c @@ -62,6 +62,10 @@ static void esp_cpu_configure_invalid_regions(void) // 7. End of address space PMA_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE); PMA_ENTRY_SET_TOR(12, UINT32_MAX, PMA_TOR | PMA_NONE); + + PMA_ENTRY_CFG_RESET(13); + PMA_ENTRY_CFG_RESET(14); + PMA_ENTRY_CFG_RESET(15); } void esp_cpu_configure_region_protection(void) @@ -112,6 +116,14 @@ void esp_cpu_configure_region_protection(void) // esp_cpu_configure_invalid_regions(); + /* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader + * to prevent errors before TEE initialization. TEE will handle all other + * memory protection. + */ +#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD + return; +#endif + // // Configure all the valid address regions using PMP // diff --git a/components/esp_hw_support/port/esp32c6/esp_cpu_intr.c b/components/esp_hw_support/port/esp32c6/esp_cpu_intr.c index 38fdf890d812..2eb79bdd3a87 100644 --- a/components/esp_hw_support/port/esp32c6/esp_cpu_intr.c +++ b/components/esp_hw_support/port/esp32c6/esp_cpu_intr.c @@ -6,6 +6,7 @@ #include "esp_cpu.h" #include "esp_riscv_intr.h" +#include "sdkconfig.h" void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_desc_ret) { @@ -16,7 +17,17 @@ void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_ * Interrupts 3, 4 and 7 are unavailable for PULP CPU as they are bound to Core-Local Interrupts (CLINT) */ // [TODO: IDF-2465] - const uint32_t rsvd_mask = BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(7); + const uint32_t base_rsvd_mask = BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(7); + + /* On the ESP32-C6, interrupt 14 is reserved for ESP-TEE + * for operations related to secure peripherals under its control + * (e.g. AES, SHA, APM) + */ +#if CONFIG_SECURE_ENABLE_TEE + const uint32_t rsvd_mask = base_rsvd_mask | BIT(14); +#else + const uint32_t rsvd_mask = base_rsvd_mask; +#endif intr_desc_ret->priority = 1; intr_desc_ret->type = ESP_CPU_INTR_TYPE_NA; From 54c3f1bae42f6a3e09a4a759a09ff20e8ac58a7d Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 6 Nov 2024 17:57:18 +0530 Subject: [PATCH 06/10] feat(esp_tee): Support for ESP-TEE - `bootloader_support` component --- components/bootloader_support/CMakeLists.txt | 32 +++ .../bootloader_flash/src/bootloader_flash.c | 208 ++++++++++++-- .../include/bootloader_utility_tee.h | 53 ++++ .../include/esp_flash_partitions.h | 5 + .../include/esp_tee_ota_utils.h | 45 +++ .../private_include/bootloader_config.h | 7 +- .../private_include/bootloader_utility.h | 7 + .../src/bootloader_utility.c | 198 +++++++++++++ .../src/bootloader_utility_tee.c | 260 ++++++++++++++++++ .../src/secure_boot_v2/secure_boot.c | 45 +++ 10 files changed, 833 insertions(+), 27 deletions(-) create mode 100644 components/bootloader_support/include/bootloader_utility_tee.h create mode 100644 components/bootloader_support/include/esp_tee_ota_utils.h create mode 100644 components/bootloader_support/src/bootloader_utility_tee.c diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index c1f8eb6fdb83..7099727b41a7 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -1,9 +1,38 @@ idf_build_get_property(target IDF_TARGET) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator endif() +if(esp_tee_build) + set(tee_inc_dirs "include" + "private_include" + "bootloader_flash/include") + + set(tee_srcs "src/flash_partitions.c" + "src/${IDF_TARGET}/bootloader_sha.c" + "src/bootloader_common_loader.c" + "src/esp_image_format.c" + "src/bootloader_utility.c" + "src/bootloader_utility_tee.c" + "bootloader_flash/src/bootloader_flash.c") + + if(CONFIG_SECURE_BOOT_V2_ENABLED) + if(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME) + list(APPEND tee_srcs "src/secure_boot_v2/secure_boot_signatures_bootloader.c" + "src/secure_boot_v2/secure_boot.c" + "src/${IDF_TARGET}/secure_boot_secure_features.c") + endif() + list(APPEND priv_requires efuse) + endif() + + idf_component_register(SRCS ${tee_srcs} + INCLUDE_DIRS ${tee_inc_dirs} + PRIV_REQUIRES efuse) + return() +endif() + set(srcs "src/bootloader_common.c" "src/bootloader_common_loader.c" @@ -49,6 +78,9 @@ if(BOOTLOADER_BUILD OR CONFIG_APP_BUILD_TYPE_RAM) "src/${IDF_TARGET}/bootloader_soc.c" "src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c" ) + if(CONFIG_SECURE_ENABLE_TEE) + list(APPEND srcs "src/bootloader_utility_tee.c") + endif() list(APPEND priv_requires hal) if(CONFIG_ESP_ROM_REV0_HAS_NO_ECDSA_INTERFACE) list(APPEND srcs diff --git a/components/bootloader_support/bootloader_flash/src/bootloader_flash.c b/components/bootloader_support/bootloader_flash/src/bootloader_flash.c index adff675c260a..73d4eb1fa591 100644 --- a/components/bootloader_support/bootloader_flash/src/bootloader_flash.c +++ b/components/bootloader_support/bootloader_flash/src/bootloader_flash.c @@ -13,11 +13,12 @@ #include "hal/efuse_ll.h" #include "hal/efuse_hal.h" -#ifndef BOOTLOADER_BUILD +#if !NON_OS_BUILD #include "spi_flash_mmap.h" #endif #include "hal/spi_flash_ll.h" #include "rom/spi_flash.h" +#include "esp_private/cache_utils.h" #if !CONFIG_IDF_TARGET_ESP32 #include "hal/spimem_flash_ll.h" #endif @@ -44,7 +45,7 @@ #define ESP_BOOTLOADER_SPIFLASH_QE_GD_SR2 BIT1 // QE position when you write 8 bits(for SR2) at one time. #define ESP_BOOTLOADER_SPIFLASH_QE_SR1_2BYTE BIT9 // QE position when you write 16 bits at one time. -#ifndef BOOTLOADER_BUILD +#if !NON_OS_BUILD /* Normal app version maps to spi_flash_mmap.h operations... */ static const char *TAG = "bootloader_mmap"; @@ -111,7 +112,7 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size) return esp_flash_erase_region(NULL, start_addr, size); } -#else //BOOTLOADER_BUILD +#else // NON_OS_BUILD /* Bootloader version, uses ROM functions only */ #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/cache.h" @@ -128,15 +129,46 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size) #elif CONFIG_IDF_TARGET_ESP32P4 #include "esp32p4/rom/opi_flash.h" #endif + +#if ESP_TEE_BUILD +#include "esp_flash_partitions.h" +#include "esp32c6/rom/spi_flash.h" +#endif + static const char *TAG = "bootloader_flash"; +/* + * NOTE: Memory mapping strategy + * + * Bootloader: + * - Uses the first N-1 MMU entries for general memory mapping. + * - Reserves the Nth (last) MMU entry for flash read through the cache + * (auto-decryption). + * - This strategy is viable because the bootloader runs exclusively + * on the device from the internal SRAM. + * + * ESP-TEE (Trusted Execution Environment) + * - Cannot adopt the strategy used by the bootloader as the TEE app operates + * in parallel to the REE. + * - The few initial MMU entries have already been taken by the TEE and REE + * application flash IDROM segments. + * - The REE could have also mapped some custom flash partitions it requires. + * - Therefore, the TEE uses MMU entries from the end of the range, with the number + * of entries corresponding to the size of its IDROM segment sizes. + * - The final MMU entry in this range is reserved for flash reads through the + * cache (auto-decryption). + * - The pages used by TEE are protected by PMP (Physical Memory Protection). + * While REE attempts to mmap this protected area would trigger a load access + * fault, this is unlikely since the MMU can address up to 16MB at once. + */ + #if CONFIG_IDF_TARGET_ESP32 /* Use first 50 blocks in MMU for bootloader_mmap, 50th block for bootloader_flash_read */ #define MMU_BLOCK0_VADDR SOC_DROM_LOW -#define MMAP_MMU_SIZE (0x320000) -#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE) +#define MMU_TOTAL_SIZE (0x320000) +#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE) #define FLASH_READ_VADDR MMU_BLOCK50_VADDR #else // !CONFIG_IDF_TARGET_ESP32 @@ -150,21 +182,89 @@ static const char *TAG = "bootloader_flash"; * On ESP32S2 we use `(SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW)`. * As this code is in bootloader, we keep this on ESP32S2 */ -#define MMAP_MMU_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped +#define MMU_TOTAL_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped #else -#define MMAP_MMU_SIZE (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_DRAM_FLASH_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped +#define MMU_TOTAL_SIZE (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_DRAM_FLASH_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped #endif -#define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE - SPI_FLASH_MMU_PAGE_SIZE) -#define FLASH_READ_VADDR MMU_BLOCK63_VADDR +#define MMU_END_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE) +#define MMU_BLOCKL_VADDR (MMU_END_VADDR - 1 * CONFIG_MMU_PAGE_SIZE) +#define FLASH_READ_VADDR MMU_BLOCKL_VADDR #endif +#if !ESP_TEE_BUILD +#define MMAP_MMU_SIZE (MMU_TOTAL_SIZE) +// Represents the MMU pages available for mmapping by the bootloader +#define MMU_FREE_PAGES (MMAP_MMU_SIZE / CONFIG_MMU_PAGE_SIZE) +#define FLASH_MMAP_VADDR (MMU_BLOCK0_VADDR) +#else /* ESP_TEE_BUILD */ +#define MMAP_MMU_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) +// Represents the MMU pages available for mmapping by the TEE #define MMU_FREE_PAGES (MMAP_MMU_SIZE / CONFIG_MMU_PAGE_SIZE) +#define FLASH_MMAP_VADDR (MMU_END_VADDR - (MMU_FREE_PAGES + 1) * CONFIG_MMU_PAGE_SIZE) +#endif /* !ESP_TEE_BUILD */ static bool mapped; +// Required for bootloader_flash_munmap() for ESP-TEE +static uint32_t current_mapped_size; + // Current bootloader mapping (ab)used for bootloader_read() static uint32_t current_read_mapping = UINT32_MAX; +#if ESP_TEE_BUILD && CONFIG_IDF_TARGET_ESP32C6 +extern void spi_common_set_dummy_output(esp_rom_spiflash_read_mode_t mode); +extern void spi_dummy_len_fix(uint8_t spi, uint8_t freqdiv); + +/* TODO: [ESP-TEE] Workarounds for the ROM read API + * + * The esp_rom_spiflash_read API requires two workarounds on ESP32-C6 ECO0: + * + * 1. [IDF-7199] Call esp_rom_spiflash_write API once before reading. + * Without this, reads return corrupted data. + * + * 2. Configure ROM flash parameters before each read using the function below. + * Without this, the first byte read is corrupted. + * + * Note: These workarounds are not needed for ESP32-C6 ECO1 and later versions. + */ +static void rom_read_api_workaround(void) +{ + static bool is_first_call = true; + if (is_first_call) { + uint32_t dummy_val = UINT32_MAX; + uint32_t dest_addr = ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN; + esp_rom_spiflash_write(dest_addr, &dummy_val, sizeof(dummy_val)); + is_first_call = false; + } + + uint32_t freqdiv = 0; + +#if CONFIG_ESPTOOLPY_FLASHFREQ_80M + freqdiv = 1; +#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M + freqdiv = 2; +#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M + freqdiv = 4; +#endif + + esp_rom_spiflash_read_mode_t read_mode; +#if CONFIG_ESPTOOLPY_FLASHMODE_QIO + read_mode = ESP_ROM_SPIFLASH_QIO_MODE; +#elif CONFIG_ESPTOOLPY_FLASHMODE_QOUT + read_mode = ESP_ROM_SPIFLASH_QOUT_MODE; +#elif CONFIG_ESPTOOLPY_FLASHMODE_DIO + read_mode = ESP_ROM_SPIFLASH_DIO_MODE; +#elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT + read_mode = ESP_ROM_SPIFLASH_DOUT_MODE; +#endif + + esp_rom_spiflash_config_clk(freqdiv, 1); + spi_dummy_len_fix(1, freqdiv); + esp_rom_spiflash_config_readmode(read_mode); + spi_common_set_dummy_output(read_mode); +} +#endif + uint32_t bootloader_mmap_get_free_pages(void) { /** @@ -188,13 +288,15 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size) uint32_t src_paddr_aligned = src_paddr & MMU_FLASH_MASK; //The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled. uint32_t size_after_paddr_aligned = (src_paddr - src_paddr_aligned) + size; + + uint32_t actual_mapped_len = 0; /** * @note 1 * Will add here a check to make sure the vaddr is on read-only and executable buses, since we use others for psram * Now simply check if it's valid vaddr, didn't check if it's readable, writable or executable. * TODO: IDF-4710 */ - if (mmu_ll_check_valid_ext_vaddr_region(0, MMU_BLOCK0_VADDR, size_after_paddr_aligned, MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION) == 0) { + if (mmu_ll_check_valid_ext_vaddr_region(0, FLASH_MMAP_VADDR, size_after_paddr_aligned, MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION) == 0) { ESP_EARLY_LOGE(TAG, "vaddr not valid"); return NULL; } @@ -204,15 +306,25 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size) Cache_Read_Disable(0); Cache_Flush(0); #else + /* NOTE: [ESP-TEE] Cache suspension vs disabling + * + * For ESP-TEE , we use suspend the cache instead of disabling it to avoid flushing the entire cache. + * This prevents performance hits when returning to the REE app due to cache misses. + * This is not applicable to the bootloader as it runs exclusively on the device from the internal SRAM. + */ +#if !ESP_TEE_BUILD cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#else + cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#endif #endif //---------------Do mapping------------------------ - ESP_EARLY_LOGD(TAG, "rodata starts from paddr=0x%08" PRIx32 ", size=0x%" PRIx32 ", will be mapped to vaddr=0x%08" PRIx32, src_paddr, size, (uint32_t)MMU_BLOCK0_VADDR); + ESP_EARLY_LOGD(TAG, "rodata starts from paddr=0x%08" PRIx32 ", size=0x%" PRIx32 ", will be mapped to vaddr=0x%08" PRIx32, src_paddr, size, (uint32_t)FLASH_MMAP_VADDR); #if CONFIG_IDF_TARGET_ESP32 uint32_t count = GET_REQUIRED_MMU_PAGES(size, src_paddr); - int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR, src_paddr_aligned, 64, count); - ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)MMU_BLOCK0_VADDR, count * SPI_FLASH_MMU_PAGE_SIZE); + int e = cache_flash_mmu_set(0, 0, FLASH_MMAP_VADDR, src_paddr_aligned, 64, count); + ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)FLASH_MMAP_VADDR, count * SPI_FLASH_MMU_PAGE_SIZE); if (e != 0) { ESP_EARLY_LOGE(TAG, "cache_flash_mmu_set failed: %d", e); Cache_Read_Enable(0); @@ -223,9 +335,8 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size) * This hal won't return error, it assumes the inputs are valid. The related check should be done in `bootloader_mmap()`. * See above comments (note 1) about IDF-4710 */ - uint32_t actual_mapped_len = 0; - mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_BLOCK0_VADDR, src_paddr_aligned, size_after_paddr_aligned, &actual_mapped_len); - ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)MMU_BLOCK0_VADDR, actual_mapped_len); + mmu_hal_map_region(0, MMU_TARGET_FLASH0, FLASH_MMAP_VADDR, src_paddr_aligned, size_after_paddr_aligned, &actual_mapped_len); + ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)FLASH_MMAP_VADDR, actual_mapped_len); #endif /** @@ -238,14 +349,19 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size) Cache_Read_Enable(0); #else #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, MMU_BLOCK0_VADDR, actual_mapped_len); + cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, FLASH_MMAP_VADDR, actual_mapped_len); #endif +#if !ESP_TEE_BUILD cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#else + cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#endif #endif mapped = true; + current_mapped_size = actual_mapped_len; - return (void *)(MMU_BLOCK0_VADDR + (src_paddr - src_paddr_aligned)); + return (void *)(FLASH_MMAP_VADDR + (src_paddr - src_paddr_aligned)); } void bootloader_munmap(const void *mapping) @@ -257,11 +373,18 @@ void bootloader_munmap(const void *mapping) Cache_Flush(0); mmu_init(0); #else +#if !ESP_TEE_BUILD cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); mmu_hal_unmap_all(); +#else + cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); + mmu_hal_unmap_region(0, FLASH_MMAP_VADDR, current_mapped_size); + cache_hal_invalidate_addr(FLASH_MMAP_VADDR, current_mapped_size); + cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#endif #endif mapped = false; - current_read_mapping = UINT32_MAX; + current_mapped_size = 0; } } @@ -285,7 +408,11 @@ static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, s Cache_Read_Disable(0); Cache_Flush(0); #else +#if !ESP_TEE_BUILD cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#elif CONFIG_ESP32C6_REV_MIN_0 + rom_read_api_workaround(); +#endif #endif esp_rom_spiflash_result_t r = esp_rom_spiflash_read(src_addr, dest, size); @@ -293,7 +420,9 @@ static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, s #if CONFIG_IDF_TARGET_ESP32 Cache_Read_Enable(0); #else +#if !ESP_TEE_BUILD cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#endif #endif return spi_to_esp_err(r); @@ -316,7 +445,13 @@ static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest Cache_Read_Disable(0); Cache_Flush(0); #else +#if !ESP_TEE_BUILD cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#else + cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); + //---------------Invalidating entries at to-be-mapped v_addr------------------------ + cache_hal_invalidate_addr(FLASH_READ_VADDR, SPI_FLASH_MMU_PAGE_SIZE); +#endif #endif //---------------Do mapping------------------------ @@ -337,13 +472,18 @@ static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest Cache_Read_Enable(0); #else #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, MMU_BLOCK0_VADDR, actual_mapped_len); + cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, FLASH_MMAP_VADDR, actual_mapped_len); #endif +#if !ESP_TEE_BUILD cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#else + cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); +#endif #endif } map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at)); dest_words[word] = *map_ptr; + current_read_mapping = UINT32_MAX; } return ESP_OK; } @@ -372,7 +512,6 @@ esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool a esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted) { - esp_err_t err; size_t alignment = write_encrypted ? 32 : 4; if ((dest_addr % alignment) != 0) { ESP_EARLY_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment); @@ -387,16 +526,29 @@ esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool return ESP_FAIL; } - err = bootloader_flash_unlock(); + esp_err_t err = bootloader_flash_unlock(); if (err != ESP_OK) { return err; } + esp_rom_spiflash_result_t rc = ESP_ROM_SPIFLASH_RESULT_OK; + if (write_encrypted && !ENCRYPTION_IS_VIRTUAL) { - return spi_to_esp_err(esp_rom_spiflash_write_encrypted(dest_addr, src, size)); + rc = esp_rom_spiflash_write_encrypted(dest_addr, src, size); } else { - return spi_to_esp_err(esp_rom_spiflash_write(dest_addr, src, size)); + rc = esp_rom_spiflash_write(dest_addr, src, size); } + /* NOTE: [ESP-TEE] Cache flushing after flash writes/erases + * + * After writing or erasing the flash, we need to flush the cache at locations + * corresponding to the destination write/erase address. This prevents stale data + * from being read from already memory-mapped addresses that were modified. + */ +#if ESP_TEE_BUILD + spi_flash_check_and_flush_cache(dest_addr, size); +#endif + + return spi_to_esp_err(rc); } esp_err_t bootloader_flash_erase_sector(size_t sector) @@ -426,6 +578,10 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size) ++sector; } } +#if ESP_TEE_BUILD + spi_flash_check_and_flush_cache(start_addr, size); +#endif + return spi_to_esp_err(rc); } @@ -480,7 +636,7 @@ void bootloader_flash_32bits_address_map_enable(esp_rom_spiflash_read_mode_t fla } #endif -#endif // BOOTLOADER_BUILD +#endif // NON_OS_BUILD FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip) @@ -671,7 +827,7 @@ void bootloader_spi_flash_reset(void) #define XMC_SUPPORT CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT #define XMC_VENDOR_ID_1 0x20 -#if BOOTLOADER_BUILD +#if NON_OS_BUILD #define BOOTLOADER_FLASH_LOG(level, ...) ESP_EARLY_LOG##level(TAG, ##__VA_ARGS__) #else static DRAM_ATTR char bootloader_flash_tag[] = "bootloader_flash"; diff --git a/components/bootloader_support/include/bootloader_utility_tee.h b/components/bootloader_support/include/bootloader_utility_tee.h new file mode 100644 index 000000000000..893d673a5e33 --- /dev/null +++ b/components/bootloader_support/include/bootloader_utility_tee.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_flash_partitions.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Fetch the currently running TEE partition + * + * @param[in] tee_ota_info TEE OTA data partition + * + * @return Subtype of the running TEE partition, or -1 if an error occurred + */ +int bootloader_utility_tee_get_boot_partition(const esp_partition_pos_t *tee_ota_info); + +/** + * @brief Set a new TEE boot partition in the TEE OTA data + * + * @param[in] tee_ota_info TEE OTA data partition + * @param[in] tee_try_part Partition table entry for the new boot partition + * + * @return ESP_OK on success, or an error code otherwise + */ +esp_err_t bootloader_utility_tee_set_boot_partition(const esp_partition_pos_t *tee_ota_info, const esp_partition_info_t *tee_try_part); + +/** + * @brief Fetch the next TEE partition for update + * + * @param[in] tee_ota_info TEE OTA data partition + * + * @return Subtype of the next TEE partition for update, or -1 if an error occurred + */ +int bootloader_utility_tee_get_next_update_partition(const esp_partition_pos_t *tee_ota_info); + +/** + * @brief Mark the current TEE app as valid and cancel update rollback + * + * @param[in] tee_ota_info TEE OTA data partition + * + * @return ESP_OK on success, or an error code otherwise + */ +esp_err_t bootloader_utility_tee_mark_app_valid_and_cancel_rollback(const esp_partition_pos_t *tee_ota_info); + +#ifdef __cplusplus +} +#endif diff --git a/components/bootloader_support/include/esp_flash_partitions.h b/components/bootloader_support/include/esp_flash_partitions.h index 5a6da221b00d..663e2053e468 100644 --- a/components/bootloader_support/include/esp_flash_partitions.h +++ b/components/bootloader_support/include/esp_flash_partitions.h @@ -21,6 +21,8 @@ extern "C" { #define PART_SUBTYPE_OTA_FLAG 0x10 #define PART_SUBTYPE_OTA_MASK 0x0f #define PART_SUBTYPE_TEST 0x20 +#define PART_SUBTYPE_TEE_0 0x30 +#define PART_SUBTYPE_TEE_1 0x31 #define PART_TYPE_DATA 0x01 #define PART_SUBTYPE_DATA_OTA 0x00 @@ -38,6 +40,9 @@ extern "C" { #define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00 #define PART_SUBTYPE_PARTITION_TABLE_OTA 0x01 +#define PART_SUBTYPE_DATA_TEE_OTA 0x90 +#define PART_SUBTYPE_DATA_TEE_SEC_STORAGE 0x91 + #define PART_TYPE_END 0xff #define PART_SUBTYPE_END 0xff diff --git a/components/bootloader_support/include/esp_tee_ota_utils.h b/components/bootloader_support/include/esp_tee_ota_utils.h new file mode 100644 index 000000000000..cb703a1de0b8 --- /dev/null +++ b/components/bootloader_support/include/esp_tee_ota_utils.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#include "esp_err.h" +#include "esp_flash_partitions.h" +#include "esp_image_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TEE otadata magic is derived from sha256 of "tee_ota" string +#define TEE_OTADATA_MAGIC 0x4337e1e1 + +/* TEE OTA selection structure (two copies in the TEE OTA data partition) */ +typedef struct { + uint32_t magic; // A magic byte for otadata structure + uint8_t version; // OTA image version + uint8_t boot_partition; // Default boot partition + uint8_t ota_state; // OTA_DATA states for checking operability of the app + uint8_t reserved_1; // Reserved field 1 + uint32_t reserved_2[5]; // Reserved fields 2 + uint32_t crc; // CRC32 of all fields in the structure +} __attribute__((packed)) esp_tee_ota_select_entry_t; + +ESP_STATIC_ASSERT(offsetof(esp_tee_ota_select_entry_t, crc) == sizeof(esp_tee_ota_select_entry_t) - sizeof(uint32_t)); + +// OTA_DATA states for checking operability of the app. +typedef enum { + ESP_TEE_OTA_IMG_NEW = 0x00U, /*!< Monitor the first boot - the bootloader changes the state to PENDING_VERIFY. */ + ESP_TEE_OTA_IMG_PENDING_VERIFY = 0x33U, /*!< If encountered during the second boot, the bootloader changes the state to INVALID. */ + ESP_TEE_OTA_IMG_INVALID = 0x55U, /*!< App was confirmed as workable - can boot and work without limits. */ + ESP_TEE_OTA_IMG_VALID = 0xAAU, /*!< App was confirmed as non-workable - will not selected to boot at all. */ + ESP_TEE_OTA_IMG_UNDEFINED = 0xFFU, /*!< Undefined. */ +} esp_tee_ota_img_states_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/bootloader_support/private_include/bootloader_config.h b/components/bootloader_support/private_include/bootloader_config.h index ef283b13bf01..cce4ba263853 100644 --- a/components/bootloader_support/private_include/bootloader_config.h +++ b/components/bootloader_support/private_include/bootloader_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -21,11 +21,16 @@ extern "C" #define SPI_ERROR_LOG "spi flash error" #define MAX_OTA_SLOTS 16 +#define MAX_TEE_OTA_SLOTS 2 typedef struct { esp_partition_pos_t ota_info; esp_partition_pos_t factory; esp_partition_pos_t test; +#if CONFIG_SECURE_ENABLE_TEE + esp_partition_pos_t tee_ota_info; + esp_partition_pos_t tee[MAX_TEE_OTA_SLOTS]; +#endif esp_partition_pos_t ota[MAX_OTA_SLOTS]; uint32_t app_count; uint32_t selected_subtype; diff --git a/components/bootloader_support/private_include/bootloader_utility.h b/components/bootloader_support/private_include/bootloader_utility.h index 94040cf41a9e..42b941944dcd 100644 --- a/components/bootloader_support/private_include/bootloader_utility.h +++ b/components/bootloader_support/private_include/bootloader_utility.h @@ -38,6 +38,13 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs); */ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs); +/** + * @brief Load and verify the TEE image from the selected partition + * + * @param bs Bootloader state structure + */ +void bootloader_utility_load_tee_image(const bootloader_state_t *bs); + /** * @brief Load the selected partition and start application. * diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index 2d62c0b92c33..9da668af3e5a 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -52,6 +52,10 @@ #include "esp_efuse.h" #include "esp_fault.h" +#if CONFIG_SECURE_ENABLE_TEE +#include "bootloader_utility_tee.h" +#endif + static const char *TAG = "boot"; /* Reduce literal size for some generic string literals */ @@ -69,6 +73,21 @@ static void set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_size, const esp_image_metadata_t *data); +#if CONFIG_SECURE_ENABLE_TEE +/* NOTE: Required by other sources for secure boot routine */ +esp_image_metadata_t tee_data; +static uint8_t tee_boot_part = UINT8_MAX; + +static void unpack_load_tee_app(const esp_image_metadata_t *data); +static void set_cache_and_load_tee_app(uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + const esp_image_metadata_t *data); +#endif + esp_err_t bootloader_common_read_otadata(const esp_partition_pos_t *ota_info, esp_ota_select_entry_t *two_otadata) { const esp_ota_select_entry_t *ota_select_map; @@ -162,6 +181,13 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs) bs->test = partition->pos; partition_usage = "test app"; break; +#if CONFIG_SECURE_ENABLE_TEE + case PART_SUBTYPE_TEE_0: /* TEE binary */ + case PART_SUBTYPE_TEE_1: + bs->tee[partition->subtype & 0x01] = partition->pos; + partition_usage = "TEE app"; + break; +#endif default: /* OTA binary */ if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { @@ -195,6 +221,15 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs) esp_efuse_init_virtual_mode_in_flash(partition->pos.offset, partition->pos.size); #endif break; +#if CONFIG_SECURE_ENABLE_TEE + case PART_SUBTYPE_DATA_TEE_OTA: /* TEE ota data */ + bs->tee_ota_info = partition->pos; + partition_usage = "TEE OTA data"; + break; + case PART_SUBTYPE_DATA_TEE_SEC_STORAGE: /* TEE secure storage */ + partition_usage = "TEE secure storage"; + break; +#endif default: partition_usage = "Unknown data"; break; @@ -518,6 +553,29 @@ void bootloader_utility_load_boot_image_from_deep_sleep(void) } #endif +#if CONFIG_SECURE_ENABLE_TEE +void bootloader_utility_load_tee_image(const bootloader_state_t *bs) +{ + esp_err_t err = ESP_FAIL; + uint8_t tee_active_part = bootloader_utility_tee_get_boot_partition(&bs->tee_ota_info); + if (tee_active_part != PART_SUBTYPE_TEE_0 && tee_active_part != PART_SUBTYPE_TEE_1) { + ESP_LOGE(TAG, "Failed to find valid TEE app"); + bootloader_reset(); + } + + uint8_t tee_part_idx = tee_active_part & 0x01; + const esp_partition_pos_t *tee_active_part_pos = &bs->tee[tee_part_idx]; + err = bootloader_load_image(tee_active_part_pos, &tee_data); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to load TEE app"); + bootloader_reset(); + } + tee_boot_part = tee_part_idx; + + ESP_LOGI(TAG, "Loaded TEE app from partition at offset 0x%"PRIx32, tee_active_part_pos->offset); +} +#endif + #define TRY_LOG_FORMAT "Trying partition index %d offs 0x%"PRIx32" size 0x%"PRIx32 void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index) @@ -856,6 +914,127 @@ static bool s_flash_seg_needs_map(uint32_t vaddr) #endif } +/* TODO: [IDF-11689] Unify the TEE-specific app loading implementation with + * the existing app loading implementation. + */ +#if CONFIG_SECURE_ENABLE_TEE +static void unpack_load_tee_app(const esp_image_metadata_t *data) +{ + /** + * note: + * On chips with shared D/I external vaddr, we don't divide them into either D or I, + * as essentially they are the same. + * We integrate all the hardware difference into this `unpack_load_app` function. + */ + uint32_t rom_addr[2] = {}; + uint32_t rom_load_addr[2] = {}; + uint32_t rom_size[2] = {}; + int rom_index = 0; //shall not exceed 2 + + // Find DROM & IROM addresses, to configure MMU mappings + for (int i = 0; i < data->image.segment_count; i++) { + const esp_image_segment_header_t *header = &data->segments[i]; + const uint32_t addr = header->load_addr; + + //`SOC_DROM_LOW` and `SOC_DROM_HIGH` are the same as `SOC_IROM_LOW` and `SOC_IROM_HIGH`, reasons are in above `note` + if ((addr >= SOC_DROM_LOW && addr < SOC_DROM_HIGH) +#if SOC_MMU_PER_EXT_MEM_TARGET + || (addr >= SOC_EXTRAM_LOW && addr < SOC_EXTRAM_HIGH) +#endif + ) { + /** + * D/I are shared, but there should not be a third segment on flash/psram + */ + assert(rom_index < 2); + rom_addr[rom_index] = data->segment_data[i]; + rom_load_addr[rom_index] = header->load_addr; + rom_size[rom_index] = header->data_len; + rom_index++; + } + } + assert(rom_index == 2); + + ESP_EARLY_LOGD(TAG, "calling set_cache_and_start_tee_app"); + set_cache_and_load_tee_app(rom_addr[0], + rom_load_addr[0], + rom_size[0], + rom_addr[1], + rom_load_addr[1], + rom_size[1], + data); +} + +static void set_cache_and_load_tee_app( + uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + const esp_image_metadata_t *data) +{ + uint32_t drom_load_addr_aligned = 0, drom_addr_aligned = 0; + uint32_t irom_load_addr_aligned = 0, irom_addr_aligned = 0; + uint32_t actual_mapped_len = 0; + + const uint32_t mmu_page_size = data->mmu_page_size; +#if SOC_MMU_PAGE_SIZE_CONFIGURABLE + // re-configure MMU page size + mmu_ll_set_page_size(0, mmu_page_size); +#endif //SOC_MMU_PAGE_SIZE_CONFIGURABLE + + if (drom_addr != 0) { + drom_load_addr_aligned = drom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size); + drom_addr_aligned = drom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size); + ESP_EARLY_LOGV(TAG, "TEE rodata starts from paddr=0x%08x, vaddr=0x%08x, size=0x%x", drom_addr, drom_load_addr, drom_size); + + //The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled. + if (s_flash_seg_needs_map(drom_load_addr_aligned)) { + mmu_hal_map_region(0, MMU_TARGET_FLASH0, drom_load_addr_aligned, drom_addr_aligned, drom_size, &actual_mapped_len); + ESP_EARLY_LOGV(TAG, "after mapping rodata, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, drom_load_addr_aligned, actual_mapped_len); + } + //we use the MMU_LL_END_DROM_ENTRY_ID mmu entry as a map page for app to find the boot partition + mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_DROM_END_ENTRY_VADDR_FROM_VAL(mmu_page_size), drom_addr_aligned, mmu_page_size, &actual_mapped_len); + ESP_EARLY_LOGV(TAG, "mapped one page of the rodata, from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, drom_load_addr_aligned, actual_mapped_len); + } + + if (irom_addr != 0) { + irom_load_addr_aligned = irom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size); + irom_addr_aligned = irom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size); + ESP_EARLY_LOGV(TAG, "TEE text starts from paddr=0x%08x, vaddr=0x%08x, size=0x%x", irom_addr, irom_load_addr, irom_size); + //The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled. + irom_size = (irom_load_addr - irom_load_addr_aligned) + irom_size; + + if (s_flash_seg_needs_map(irom_load_addr_aligned)) { + mmu_hal_map_region(0, MMU_TARGET_FLASH0, irom_load_addr_aligned, irom_addr_aligned, irom_size, &actual_mapped_len); + ESP_EARLY_LOGV(TAG, "after mapping text, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", irom_addr_aligned, irom_load_addr_aligned, actual_mapped_len); + } + } + + if (drom_load_addr_aligned != 0) { + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, drom_load_addr_aligned, drom_size); + cache_ll_l1_enable_bus(0, bus_mask); + } + + if (irom_load_addr_aligned != 0) { + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, irom_load_addr_aligned, irom_size); + cache_ll_l1_enable_bus(0, bus_mask); + } + +#if !CONFIG_FREERTOS_UNICORE + if (drom_load_addr_aligned != 0) { + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(1, drom_load_addr_aligned, drom_size); + cache_ll_l1_enable_bus(1, bus_mask); + } + + if (irom_load_addr_aligned != 0) { + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(1, irom_load_addr_aligned, irom_size); + cache_ll_l1_enable_bus(1, bus_mask); + } +#endif +} +#endif // CONFIG_SECURE_ENABLE_TEE + static void set_cache_and_start_app( uint32_t drom_addr, uint32_t drom_load_addr, @@ -943,6 +1122,11 @@ static void set_cache_and_start_app( cache_ll_l1_enable_bus(1, bus_mask); #endif +#if CONFIG_SECURE_ENABLE_TEE + //----------------------Unpacking and loading the TEE app---------------- + unpack_load_tee_app(&tee_data); +#endif + //----------------------Enable Cache---------------- #if CONFIG_IDF_TARGET_ESP32 // Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) @@ -953,12 +1137,26 @@ static void set_cache_and_start_app( ESP_LOGD(TAG, "start: 0x%08"PRIx32, entry_addr); bootloader_atexit(); + +#if CONFIG_SECURE_ENABLE_TEE + ESP_LOGI(TAG, "Current privilege level - %d", esp_cpu_get_curr_privilege_level()); + /* NOTE: TEE Initialization and REE Switch + * This call will not return back. After TEE initialization, + * it will switch to the REE and execute the user application. + */ + typedef void (*esp_tee_init_t)(uint32_t, uint32_t, uint8_t) __attribute__((noreturn)); + esp_tee_init_t esp_tee_init = ((esp_tee_init_t) tee_data.image.entry_addr); + + ESP_LOGI(TAG, "Starting TEE: Entry point - 0x%"PRIx32, (uint32_t)esp_tee_init); + (*esp_tee_init)(entry_addr, drom_addr, tee_boot_part); +#else typedef void (*entry_t)(void) __attribute__((noreturn)); entry_t entry = ((entry_t) entry_addr); // TODO: we have used quite a bit of stack at this point. // use "movsp" instruction to reset stack back to where ROM stack starts. (*entry)(); +#endif } void bootloader_reset(void) diff --git a/components/bootloader_support/src/bootloader_utility_tee.c b/components/bootloader_support/src/bootloader_utility_tee.c new file mode 100644 index 000000000000..c6efd2eeb014 --- /dev/null +++ b/components/bootloader_support/src/bootloader_utility_tee.c @@ -0,0 +1,260 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "esp_attr.h" +#include "esp_log.h" + +#include "esp_rom_sys.h" +#include "esp_rom_crc.h" + +#include "hal/efuse_hal.h" + +#include "esp_image_format.h" +#include "bootloader_config.h" +#include "bootloader_flash_priv.h" + +#include "bootloader_utility.h" +#include "bootloader_utility_tee.h" +#include "esp_tee_ota_utils.h" + +#include "sdkconfig.h" + +static const char *TAG = "boot_tee"; + +static esp_err_t write_tee_otadata_sector(esp_tee_ota_select_entry_t *tee_otadata, uint32_t offset) +{ + if (tee_otadata == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = bootloader_flash_erase_sector(offset / FLASH_SECTOR_SIZE); + if (err == ESP_OK) { + bool write_encrypted = false; +#if !CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH + write_encrypted = efuse_hal_flash_encryption_enabled(); +#endif + err = bootloader_flash_write(offset, tee_otadata, sizeof(esp_tee_ota_select_entry_t), write_encrypted); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write otadata sector, 0x%x", err); + } + } + + return err; +} + +static esp_err_t read_tee_otadata(const esp_partition_pos_t *tee_ota_info, esp_tee_ota_select_entry_t *two_otadata) +{ + if (tee_ota_info == NULL || two_otadata == NULL || tee_ota_info->offset == 0) { + return ESP_ERR_INVALID_ARG; + } + + if (tee_ota_info->size < 2 * FLASH_SECTOR_SIZE) { + return ESP_ERR_INVALID_SIZE; + } + + ESP_LOGV(TAG, "TEE OTA data offset 0x%"PRIx32, tee_ota_info->offset); + + const esp_tee_ota_select_entry_t *ota_select_map = bootloader_mmap(tee_ota_info->offset, tee_ota_info->size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%"PRIx32", 0x%"PRIx32") failed", tee_ota_info->offset, tee_ota_info->size); + return ESP_FAIL; + } + + memcpy(&two_otadata[0], (uint8_t *)ota_select_map, sizeof(esp_tee_ota_select_entry_t)); + memcpy(&two_otadata[1], (uint8_t *)ota_select_map + FLASH_SECTOR_SIZE, sizeof(esp_tee_ota_select_entry_t)); + + bootloader_munmap(ota_select_map); + + return ESP_OK; +} + +static esp_err_t write_tee_otadata(esp_tee_ota_select_entry_t *tee_otadata, const esp_partition_pos_t *tee_ota_info) +{ + esp_err_t err = write_tee_otadata_sector(tee_otadata, tee_ota_info->offset); + if (err == ESP_OK) { + err = write_tee_otadata_sector(tee_otadata, tee_ota_info->offset + FLASH_SECTOR_SIZE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to update otadata sector, 0x%x", err); + } + } + + return err; +} + +static esp_err_t get_valid_tee_otadata(const esp_partition_pos_t *tee_ota_info, esp_tee_ota_select_entry_t *tee_otadata) +{ + esp_tee_ota_select_entry_t two_otadata[2] = {0}; + if (read_tee_otadata(tee_ota_info, two_otadata) != ESP_OK) { + return ESP_ERR_NOT_FOUND; + } + + esp_tee_ota_select_entry_t blank_otadata; + memset(&blank_otadata, 0xff, sizeof(esp_tee_ota_select_entry_t)); + + // Check if the contents of both the otadata sectors match + bool sectors_match = (memcmp(&two_otadata[0], &two_otadata[1], sizeof(esp_tee_ota_select_entry_t)) == 0); + if (sectors_match) { + if (memcmp(&two_otadata[0], &blank_otadata, sizeof(esp_tee_ota_select_entry_t)) != 0) { + uint32_t crc = esp_rom_crc32_le(0, (uint8_t const *)two_otadata, (sizeof(esp_tee_ota_select_entry_t) - sizeof(uint32_t))); + if (two_otadata[0].magic != TEE_OTADATA_MAGIC || crc != two_otadata[0].crc) { + ESP_LOGE(TAG, "TEE otadata[0] magic or CRC verification failed"); + return ESP_FAIL; + } + } + memcpy(tee_otadata, &two_otadata[0], sizeof(esp_tee_ota_select_entry_t)); + ESP_LOGV(TAG, "Both tee_otadata sectors are the same"); + } else { + uint32_t crc_otadata0 = esp_rom_crc32_le(0, (uint8_t const *)&two_otadata[0], (sizeof(esp_tee_ota_select_entry_t) - sizeof(uint32_t))); + uint32_t crc_otadata1 = esp_rom_crc32_le(0, (uint8_t const *)&two_otadata[1], (sizeof(esp_tee_ota_select_entry_t) - sizeof(uint32_t))); + + if (crc_otadata0 == two_otadata[0].crc) { + ESP_LOGV(TAG, "Second tee_otadata sector is invalid - copying contents from first sector"); + // Copy contents of first tee_otadata sector into second + write_tee_otadata_sector(&two_otadata[0], tee_ota_info->offset + FLASH_SECTOR_SIZE); + memcpy(tee_otadata, &two_otadata[0], sizeof(esp_tee_ota_select_entry_t)); + } else if (crc_otadata1 == two_otadata[1].crc) { + ESP_LOGV(TAG, "First tee_otadata sector is invalid - copying contents from second sector"); + // Copy contents of second tee_otadata sector into first + write_tee_otadata_sector(&two_otadata[1], tee_ota_info->offset); + memcpy(tee_otadata, &two_otadata[1], sizeof(esp_tee_ota_select_entry_t)); + } else { + ESP_LOGE(TAG, "Both tee_otadata sectors are invalid!"); + abort(); + } + } + + return ESP_OK; +} + +static esp_err_t update_tee_otadata(const esp_partition_pos_t *tee_ota_info, uint8_t boot_partition, uint8_t ota_state) +{ + esp_tee_ota_select_entry_t otadata = { + .magic = TEE_OTADATA_MAGIC, + .boot_partition = boot_partition, + .ota_state = ota_state, + }; + otadata.crc = esp_rom_crc32_le(0, (uint8_t const *)&otadata, (sizeof(esp_tee_ota_select_entry_t) - sizeof(uint32_t))); + return write_tee_otadata(&otadata, tee_ota_info); +} + +int bootloader_utility_tee_get_boot_partition(const esp_partition_pos_t *tee_ota_info) +{ + esp_tee_ota_select_entry_t otadata = {}, blank_otadata; + const int default_tee_app_slot = PART_SUBTYPE_TEE_0; + + esp_err_t err = get_valid_tee_otadata(tee_ota_info, &otadata); + if (err == ESP_ERR_NOT_FOUND) { + ESP_LOGV(TAG, "otadata partition not found, booting from first partition"); + return default_tee_app_slot; + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get valid otadata, 0x%x", err); + return -1; + } + memset(&blank_otadata, 0xff, sizeof(esp_tee_ota_select_entry_t)); + if (!memcmp(&blank_otadata, &otadata, sizeof(esp_tee_ota_select_entry_t))) { + ESP_LOGV(TAG, "otadata partition empty, booting from first partition"); + /* NOTE: The first TEE partition will always be valid as it is flashed manually */ + if (update_tee_otadata(tee_ota_info, default_tee_app_slot, ESP_TEE_OTA_IMG_VALID) != ESP_OK) { + ESP_LOGW(TAG, "Failed to setup TEE otadata as per the first partition!"); + } + return default_tee_app_slot; + } + + int boot_partition = 0; + +#if BOOTLOADER_BUILD + switch(otadata.ota_state) { + case ESP_TEE_OTA_IMG_NEW: + ESP_LOGD(TAG, "TEE otadata - Current image state: NEW"); + boot_partition = otadata.boot_partition; + if (update_tee_otadata(tee_ota_info, otadata.boot_partition, ESP_TEE_OTA_IMG_PENDING_VERIFY) != ESP_OK) { + return -1; + } + break; + case ESP_TEE_OTA_IMG_UNDEFINED: + case ESP_TEE_OTA_IMG_PENDING_VERIFY: + ESP_LOGD(TAG, "TEE otadata - Current image state: PENDING_VERIFY/UNDEFINED"); + boot_partition = (otadata.boot_partition == PART_SUBTYPE_TEE_0) ? PART_SUBTYPE_TEE_1 : PART_SUBTYPE_TEE_0; + if (update_tee_otadata(tee_ota_info, boot_partition, ESP_TEE_OTA_IMG_INVALID) != ESP_OK) { + return -1; + } + break; + case ESP_TEE_OTA_IMG_INVALID: + ESP_LOGD(TAG, "TEE otadata - Current image state: INVALID"); + bootloader_reset(); + break; + case ESP_TEE_OTA_IMG_VALID: + ESP_LOGD(TAG, "TEE otadata - Current image state: VALID"); + boot_partition = otadata.boot_partition; + break; + break; + default: + break; + } +#else + boot_partition = otadata.boot_partition; +#endif + + return boot_partition; +} + +esp_err_t bootloader_utility_tee_set_boot_partition(const esp_partition_pos_t *tee_ota_info, const esp_partition_info_t *tee_try_part) +{ + if (tee_ota_info == NULL || tee_try_part == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (tee_try_part->subtype != PART_SUBTYPE_TEE_0 && tee_try_part->subtype != PART_SUBTYPE_TEE_1) { + return ESP_ERR_INVALID_ARG; + } + + esp_image_metadata_t data = {}; + if (esp_image_verify(ESP_IMAGE_VERIFY, &tee_try_part->pos, &data) != ESP_OK) { + return ESP_ERR_IMAGE_INVALID; + } + + return update_tee_otadata(tee_ota_info, tee_try_part->subtype, ESP_TEE_OTA_IMG_NEW); +} + +int bootloader_utility_tee_get_next_update_partition(const esp_partition_pos_t *tee_ota_info) +{ + esp_tee_ota_select_entry_t otadata = {}, blank_otadata; + const int default_tee_next_app_slot = PART_SUBTYPE_TEE_1; + + esp_err_t err = get_valid_tee_otadata(tee_ota_info, &otadata); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get valid otadata, 0x%x", err); + return -1; + } + memset(&blank_otadata, 0xff, sizeof(esp_tee_ota_select_entry_t)); + if (!memcmp(&blank_otadata, &otadata, sizeof(esp_tee_ota_select_entry_t))) { + return default_tee_next_app_slot; + } + + return (otadata.boot_partition == PART_SUBTYPE_TEE_0) ? PART_SUBTYPE_TEE_1 : PART_SUBTYPE_TEE_0; +} + +esp_err_t bootloader_utility_tee_mark_app_valid_and_cancel_rollback(const esp_partition_pos_t *tee_ota_info) +{ + esp_tee_ota_select_entry_t two_otadata[2]; + + esp_err_t err = read_tee_otadata(tee_ota_info, two_otadata); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch TEE otadata!"); + return err; + } + + if (two_otadata[0].ota_state == ESP_TEE_OTA_IMG_VALID) { + ESP_LOGD(TAG, "TEE otadata - Current image already has been marked VALID"); + return ESP_ERR_INVALID_STATE; + } + + int tee_app_slot = bootloader_utility_tee_get_boot_partition(tee_ota_info); + return update_tee_otadata(tee_ota_info, (uint8_t)tee_app_slot, ESP_TEE_OTA_IMG_VALID); +} diff --git a/components/bootloader_support/src/secure_boot_v2/secure_boot.c b/components/bootloader_support/src/secure_boot_v2/secure_boot.c index c9d58ebee651..06630a993b94 100644 --- a/components/bootloader_support/src/secure_boot_v2/secure_boot.c +++ b/components/bootloader_support/src/secure_boot_v2/secure_boot.c @@ -23,6 +23,10 @@ #ifdef CONFIG_SECURE_BOOT_V2_ENABLED +#if CONFIG_SECURE_ENABLE_TEE +extern esp_image_metadata_t tee_data; +#endif + #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) static const char *TAG = "secure_boot_v2"; @@ -268,6 +272,28 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t ESP_LOGW(TAG, "App has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", app_key_digests.num_digests, boot_key_digests.num_digests); } +#if CONFIG_SECURE_ENABLE_TEE + /* Generate the TEE public key digests */ + bool tee_match = false; + esp_image_sig_public_key_digests_t tee_key_digests = {0}; + + ret = s_calculate_image_public_key_digests(tee_data.start_addr, tee_data.image_len - SIG_BLOCK_PADDING, &tee_key_digests); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "TEE signature block is invalid."); + return ret; + } + + if (tee_key_digests.num_digests == 0) { + ESP_LOGE(TAG, "No valid TEE signature blocks found."); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "%d signature block(s) found appended to the tee.", tee_key_digests.num_digests); + if (tee_key_digests.num_digests > boot_key_digests.num_digests) { + ESP_LOGW(TAG, "TEE has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", tee_key_digests.num_digests, boot_key_digests.num_digests); + } +#endif + /* Confirm if at least one public key from the application matches a public key in the bootloader (Also, ensure if that public revoke bit is not set for the matched key) */ bool match = false; @@ -285,6 +311,18 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t match = true; } } +#if CONFIG_SECURE_ENABLE_TEE + if (!match) { + continue; + } + + for (unsigned j = 0; j < tee_key_digests.num_digests; j++) { + if (!memcmp(boot_key_digests.key_digests[i], tee_key_digests.key_digests[j], ESP_SECURE_BOOT_DIGEST_LEN)) { + ESP_LOGI(TAG, "TEE key(%d) matches with bootloader key(%d).", j, i); + tee_match = true; + } + } +#endif } if (match == false) { @@ -292,6 +330,13 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t return ESP_FAIL; } +#if CONFIG_SECURE_ENABLE_TEE + if (tee_match == false) { + ESP_LOGE(TAG, "No TEE key digest matches the bootloader key digest."); + return ESP_FAIL; + } +#endif + #if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS /* Revoke the empty signature blocks */ if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) { From 733741bbac71a16b64c9c575b73dfd0b7034ee16 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 14:42:08 +0530 Subject: [PATCH 07/10] feat(esp_tee): Support for ESP-TEE - `esp_system` component --- .../esp_mm/port/esp32c6/ext_mem_layout.c | 33 ++++++++++++++++--- components/esp_system/CMakeLists.txt | 3 +- components/esp_system/Kconfig | 12 +++++-- components/esp_system/ld/esp32c6/memory.ld.in | 20 ++++++++++- .../esp_system/ld/esp32c6/sections.ld.in | 11 +++++++ components/esp_system/port/cpu_start.c | 15 ++++++++- components/esp_system/startup_funcs.c | 7 ++++ components/heap/port/esp32c6/memory_layout.c | 13 ++++++++ 8 files changed, 105 insertions(+), 9 deletions(-) diff --git a/components/esp_mm/port/esp32c6/ext_mem_layout.c b/components/esp_mm/port/esp32c6/ext_mem_layout.c index 9c0004bfdd03..e8c200192c2f 100644 --- a/components/esp_mm/port/esp32c6/ext_mem_layout.c +++ b/components/esp_mm/port/esp32c6/ext_mem_layout.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,15 +10,40 @@ #include "../ext_mem_layout.h" #include "hal/mmu_types.h" +/* NOTE: With ESP-TEE enabled: + * - The start address is moved by the size of TEE IDROM segments since these + * segments are placed at the start of the linear address space + * - TEE IROM and DROM segments are both 64KB (CONFIG_SECURE_TEE_IROM_SIZE, + * CONFIG_SECURE_TEE_DROM_SIZE) for now. Thus, the number of reserved entries + * from the start would be (64KB + 64KB)/MMU_PAGE_SIZE + * - The last few MMU entries are reserved for TEE flash operations. The number + * of reserved entries matches the size of TEE IDROM segments (IROM + DROM) + * plus one additional entry, i.e. (64KB + 64KB)/MMU_PAGE_SIZE + 1 + */ +#if CONFIG_SECURE_ENABLE_TEE +#define TEE_MMU_MEM_REG_START_OFFS (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) +#define TEE_MMU_RESV_PAGES ((CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) / CONFIG_MMU_PAGE_SIZE) +#define TEE_MMU_MEM_REG_END_OFFS ((TEE_MMU_RESV_PAGES + 1) * CONFIG_MMU_PAGE_SIZE) + +#define MMU_MEM_REG_START_ADDR_W_TEE (SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW + TEE_MMU_MEM_REG_START_OFFS) +#define MMU_MEM_REG_END_ADDR_W_TEE (SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH - TEE_MMU_MEM_REG_END_OFFS) + +#define MMU_IRAM0_LINEAR_ADDRESS_LOW MMU_MEM_REG_START_ADDR_W_TEE +#define MMU_IRAM0_LINEAR_ADDRESS_HIGH MMU_MEM_REG_END_ADDR_W_TEE +#else +#define MMU_IRAM0_LINEAR_ADDRESS_LOW SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW +#define MMU_IRAM0_LINEAR_ADDRESS_HIGH SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH +#endif + /** * The start addresses in this list should always be sorted from low to high, as MMU driver will need to * coalesce adjacent regions */ const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = { [0] = { - .start = SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW, - .end = SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH, - .size = SOC_BUS_SIZE(SOC_MMU_IRAM0_LINEAR), + .start = MMU_IRAM0_LINEAR_ADDRESS_LOW, + .end = MMU_IRAM0_LINEAR_ADDRESS_HIGH, + .size = MMU_IRAM0_LINEAR_ADDRESS_HIGH - MMU_IRAM0_LINEAR_ADDRESS_LOW, .bus_id = CACHE_BUS_IBUS0 | CACHE_BUS_DBUS0, .targets = MMU_TARGET_FLASH0, .caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT, diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index 1443e619d4bb..b99131ad99ab 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -1,4 +1,5 @@ idf_build_get_property(target IDF_TARGET) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) # On Linux, we only support a few features, hence this simple component registration if(${target} STREQUAL "linux") @@ -22,7 +23,7 @@ if(CONFIG_IDF_ENV_FPGA OR CONFIG_ESP_BRINGUP_BYPASS_RANDOM_SETTING) list(APPEND srcs "fpga_overrides_rng.c") endif() -if(BOOTLOADER_BUILD) +if(BOOTLOADER_BUILD OR esp_tee_build) # "_esp_error_check_failed()" requires spi_flash module # Bootloader relies on some Kconfig options defined in esp_system. idf_component_register(SRCS "${srcs}" REQUIRES spi_flash) diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 504981640b11..136d4616fccd 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -121,7 +121,7 @@ menu "ESP System Settings" config ESP_SYSTEM_PMP_IDRAM_SPLIT bool "Enable IRAM/DRAM split protection" - depends on SOC_CPU_IDRAM_SPLIT_USING_PMP + depends on SOC_CPU_IDRAM_SPLIT_USING_PMP && !SECURE_ENABLE_TEE default "y" help If enabled, the CPU watches all the memory access and raises an exception in case @@ -141,6 +141,13 @@ menu "ESP System Settings" Warning: on ESP32-P4 this will also mark the memory area used for BOOTLOADER_RESERVE_RTC_MEM as executable. If you consider this a security risk then do not activate this option. + config ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE + bool "Enable memory protection (via TEE)" + depends on SECURE_ENABLE_TEE + default "y" + help + This option enables the default memory protection provided by TEE. + config ESP_SYSTEM_MEMPROT_FEATURE bool "Enable memory protection" depends on SOC_MEMPROT_SUPPORTED @@ -592,7 +599,8 @@ menu "ESP System Settings" config ESP_SYSTEM_HW_STACK_GUARD bool "Hardware stack guard" - depends on SOC_ASSIST_DEBUG_SUPPORTED + # TODO: [ESP-TEE] IDF-10770 + depends on SOC_ASSIST_DEBUG_SUPPORTED && !SECURE_ENABLE_TEE default y help This config allows to trigger a panic interrupt when Stack Pointer register goes out of allocated stack diff --git a/components/esp_system/ld/esp32c6/memory.ld.in b/components/esp_system/ld/esp32c6/memory.ld.in index 389113a128f2..4345cd201fe9 100644 --- a/components/esp_system/ld/esp32c6/memory.ld.in +++ b/components/esp_system/ld/esp32c6/memory.ld.in @@ -15,7 +15,13 @@ #include "sdkconfig.h" #include "ld.common" -#define SRAM_SEG_START 0x40800000 +#if !CONFIG_SECURE_ENABLE_TEE +#define SRAM_SEG_START (0x40800000) +#else +#define SRAM_SEG_START (0x40800000 + CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) +#define FLASH_SEG_OFFSET (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) +#endif // CONFIG_SECURE_ENABLE_TEE + #define SRAM_SEG_END 0x4086E610 /* 2nd stage bootloader iram_loader_seg start address */ #define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START @@ -35,8 +41,14 @@ MEMORY */ #if CONFIG_APP_BUILD_USE_FLASH_SECTIONS +#if CONFIG_SECURE_ENABLE_TEE + /* Flash mapped instruction data */ + irom_seg (RX) : org = 0x42000020 + FLASH_SEG_OFFSET, + len = IDRAM0_2_SEG_SIZE - FLASH_SEG_OFFSET - 0x20 +#else /* Flash mapped instruction data */ irom_seg (RX) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20 +#endif /** * (0x20 offset above is a convenience for the app binary image generation. @@ -54,8 +66,14 @@ MEMORY sram_seg (RWX) : org = SRAM_SEG_START, len = SRAM_SEG_SIZE #if CONFIG_APP_BUILD_USE_FLASH_SECTIONS +#if CONFIG_SECURE_ENABLE_TEE /* Flash mapped constant data */ + drom_seg (R) : org = 0x42000020 + FLASH_SEG_OFFSET, + len = IDRAM0_2_SEG_SIZE - FLASH_SEG_OFFSET - 0x20 +#else + /* Flash mapped instruction data */ drom_seg (R) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20 +#endif /* (See irom_seg for meaning of 0x20 offset in the above.) */ #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS diff --git a/components/esp_system/ld/esp32c6/sections.ld.in b/components/esp_system/ld/esp32c6/sections.ld.in index da8a695be4af..407d34390aad 100644 --- a/components/esp_system/ld/esp32c6/sections.ld.in +++ b/components/esp_system/ld/esp32c6/sections.ld.in @@ -173,11 +173,22 @@ SECTIONS /* Vectors go to start of IRAM */ ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned"); + _vector_table_start = ABSOLUTE(.); KEEP(*(.exception_vectors_table.text)); KEEP(*(.exception_vectors.text)); ALIGNED_SYMBOL(4, _invalid_pc_placeholder) + /* esp_tee_config_t structure: used to share information between the TEE and REE + * (e.g. interrupt handler addresses, REE flash text-rodata boundaries, etc.) + * This symbol is expected by the TEE at an offset of 0x300 from the vector table start. + */ +#if CONFIG_SECURE_ENABLE_TEE + ALIGNED_SYMBOL(0x10, _esp_tee_app_cfg) + ASSERT(ABSOLUTE(.) == _vector_table_start + 0x2e0, "esp_tee_app_cfg must be at an offset 0x2e0 from the vector table start"); + *libesp_tee.a:(.esp_tee_app_cfg); +#endif + /* Code marked as running out of IRAM */ _iram_text_start = ABSOLUTE(.); diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index 3303dfaf6524..a65bb3a56bba 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -419,6 +419,15 @@ void IRAM_ATTR call_start_cpu0(void) esp_cpu_intr_set_mtvt_addr(&_mtvt_table); #endif + /* NOTE: When ESP-TEE is enabled, this sets up the callback function + * which redirects all the interrupt management for the REE (user app) + * to the TEE by raising the appropriate service calls. + */ +#if CONFIG_SECURE_ENABLE_TEE + extern uint32_t esp_tee_service_call(int argc, ...); + esprv_int_setup_mgmt_cb((void *)esp_tee_service_call); +#endif + rst_reas[0] = esp_rom_get_reset_reason(0); #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE rst_reas[1] = esp_rom_get_reset_reason(1); @@ -606,8 +615,12 @@ void IRAM_ATTR call_start_cpu0(void) #else ESP_EARLY_LOGI(TAG, "Multicore app"); #endif - + /* NOTE: When ESP-TEE is enabled, it configures its own memory protection + * scheme using the CPU-inherent features PMP and PMA and the APM peripheral. + */ +#if !CONFIG_SECURE_ENABLE_TEE bootloader_init_mem(); +#endif #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE s_cpu_up[0] = true; diff --git a/components/esp_system/startup_funcs.c b/components/esp_system/startup_funcs.c index a4b33ba65367..f57fff7a58af 100644 --- a/components/esp_system/startup_funcs.c +++ b/components/esp_system/startup_funcs.c @@ -63,6 +63,12 @@ ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10) return ESP_OK; } +/* NOTE: When ESP-TEE is enabled, the Brownout Detection module is part + * of the TEE and is initialized in the TEE startup routine itself. + * It is protected from all REE accesses through memory protection mechanisms, + * as it is a critical module for device functioning. + */ +#if !CONFIG_SECURE_ENABLE_TEE ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104) { // [refactor-todo] leads to call chain rtc_is_register (driver) -> esp_intr_alloc (esp32/esp32s2) -> @@ -76,6 +82,7 @@ ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104) #endif // CONFIG_ESP_BROWNOUT_DET return ESP_OK; } +#endif ESP_SYSTEM_INIT_FN(init_newlib_time, CORE, BIT(0), 105) { diff --git a/components/heap/port/esp32c6/memory_layout.c b/components/heap/port/esp32c6/memory_layout.c index 3c0aceb7b0d1..05dadf57143d 100644 --- a/components/heap/port/esp32c6/memory_layout.c +++ b/components/heap/port/esp32c6/memory_layout.c @@ -12,6 +12,11 @@ #include "heap_memory_layout.h" #include "esp_heap_caps.h" +#if CONFIG_SECURE_ENABLE_TEE +#define SRAM_DIRAM_TEE_ORG (SOC_DIRAM_IRAM_LOW) +#define SRAM_DIRAM_TEE_END (SRAM_DIRAM_TEE_ORG + CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) +#endif + /** * @brief Memory type descriptors. These describe the capabilities of a type of memory in the SoC. * Each type of memory map consists of one or more regions in the address space. @@ -95,6 +100,14 @@ SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_d // Target has a shared D/IRAM virtual address, no need to calculate I_D_OFFSET like previous chips SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_end, iram_code); +/* NOTE: When ESP-TEE is enabled, the start of the internal SRAM + * is used by the TEE and is protected from any REE access using + * memory protection mechanisms employed by ESP-TEE. + */ +#if CONFIG_SECURE_ENABLE_TEE +SOC_RESERVE_MEMORY_REGION((intptr_t)SRAM_DIRAM_TEE_ORG, (intptr_t)(SRAM_DIRAM_TEE_END), tee_diram); +#endif + #ifdef CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP SOC_RESERVE_MEMORY_REGION(SOC_RTC_DRAM_LOW, (intptr_t)&_rtc_force_slow_end, rtcram_data); #endif From e51d2c1da356e295a6c7892d3f5c2125d7c5b5cb Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Thu, 3 Oct 2024 13:54:10 +0530 Subject: [PATCH 08/10] feat(esp_tee): Support for ESP-TEE - `riscv` component --- components/riscv/CMakeLists.txt | 6 + .../include/esp_private/interrupt_plic.h | 23 ++++ components/riscv/include/riscv/csr.h | 7 ++ components/riscv/include/riscv/interrupt.h | 15 +++ components/riscv/include/riscv/rv_utils.h | 105 ++++++++++++++++- components/riscv/interrupt_plic.c | 66 ++++++++++- components/riscv/vectors.S | 110 +++++++++++++++--- 7 files changed, 306 insertions(+), 26 deletions(-) diff --git a/components/riscv/CMakeLists.txt b/components/riscv/CMakeLists.txt index 96914d9beec2..43f6e42728fc 100644 --- a/components/riscv/CMakeLists.txt +++ b/components/riscv/CMakeLists.txt @@ -1,5 +1,6 @@ idf_build_get_property(target IDF_TARGET) idf_build_get_property(arch IDF_TARGET_ARCH) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(NOT "${arch}" STREQUAL "riscv") return() @@ -8,6 +9,11 @@ endif() if(BOOTLOADER_BUILD) set(priv_requires soc) +elseif(esp_tee_build) + set(priv_requires soc) + if(CONFIG_SOC_INT_PLIC_SUPPORTED) + set(srcs "interrupt_plic.c") + endif() else() set(priv_requires soc) set(srcs diff --git a/components/riscv/include/esp_private/interrupt_plic.h b/components/riscv/include/esp_private/interrupt_plic.h index 4d1fbba34e49..f4eaab32e417 100644 --- a/components/riscv/include/esp_private/interrupt_plic.h +++ b/components/riscv/include/esp_private/interrupt_plic.h @@ -12,6 +12,7 @@ #include "soc/interrupt_reg.h" #include "soc/soc_caps.h" #include "riscv/csr.h" +#include "sdkconfig.h" #if SOC_INT_PLIC_SUPPORTED @@ -84,12 +85,34 @@ FORCE_INLINE_ATTR void rv_utils_restore_intlevel_regval(uint32_t restoreval) */ FORCE_INLINE_ATTR uint32_t rv_utils_set_intlevel_regval(uint32_t intlevel) { +#if CONFIG_SECURE_ENABLE_TEE + unsigned prv_mode = RV_READ_CSR(CSR_PRV_MODE); + + unsigned old_xstatus; + if (prv_mode == PRV_M) { + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } + + uint32_t old_thresh = REG_READ(INTERRUPT_CURRENT_CORE_INT_THRESH_REG); + rv_utils_restore_intlevel_regval(intlevel); + + if (prv_mode == PRV_M) { + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + } else { + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); + } + + return old_thresh; +#else uint32_t old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); uint32_t old_thresh = REG_READ(INTERRUPT_CURRENT_CORE_INT_THRESH_REG); rv_utils_restore_intlevel_regval(intlevel); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); return old_thresh; +#endif } diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index b55932f0b721..0e9d71b4be18 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -187,6 +187,13 @@ extern "C" { #define STPC1 0xBF1 #define STPC2 0xBF2 +/* Espressif's custom CSR for the current privilege mode */ +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 +#define CSR_PRV_MODE 0xC10 +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32P4 +#define CSR_PRV_MODE 0x810 +#endif + /* RISC-V CSR macros * Adapted from https://github.com/michaeljclark/riscv-probe/blob/master/libfemto/include/arch/riscv/machine.h */ diff --git a/components/riscv/include/riscv/interrupt.h b/components/riscv/include/riscv/interrupt.h index b2dab4a67282..f500b6174b51 100644 --- a/components/riscv/include/riscv/interrupt.h +++ b/components/riscv/include/riscv/interrupt.h @@ -146,6 +146,21 @@ bool esprv_int_is_vectored(int rv_int_num); */ void esprv_int_set_vectored(int rv_int_num, bool vectored); +/*************************** ESP-TEE specific ***************************/ + +/** Function prototype executing interrupt configuration APIs as service calls */ +typedef void (*esprv_int_mgmt_t)(int argc, ...); + +/** + * @brief Setup the callback function which executes the interrupt + * configuration APIs as TEE service calls + * + * @note This function should be called right after landing in the REE application, + * before any system initialization + * + * @param fptr Pointer to the function + */ +void esprv_int_setup_mgmt_cb(void *fptr); /** * Include the deprecated functions last since they will alias the functions declared above diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index cc8c608e4aeb..fcf9b3bfcf52 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -15,16 +15,38 @@ #include "riscv/csr.h" #include "riscv/interrupt.h" #include "riscv/csr_pie.h" +#include "sdkconfig.h" #ifdef __cplusplus extern "C" { #endif +/* Check whether the current privilege level is Machine (M) mode */ +#if CONFIG_SECURE_ENABLE_TEE +#define IS_PRV_M_MODE() (RV_READ_CSR(CSR_PRV_MODE) == PRV_M) +#else +#define IS_PRV_M_MODE() (1UL) +#endif + +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD +/* [ESP-TEE] Secure service call IDs for interrupt management */ +#define TEE_INTR_ENABLE_SRV_ID (2) +#define TEE_INTR_DISABLE_SRV_ID (3) +#define TEE_INTR_SET_PRIORITY_SRV_ID (4) +#define TEE_INTR_SET_TYPE_SRV_ID (5) +#define TEE_INTR_SET_THRESHOLD_SRV_ID (6) +#define TEE_INTR_EDGE_ACK_SRV_ID (7) +#define TEE_INTR_GLOBAL_EN_SRV_ID (8) +/* [ESP-TEE] Callback function for accessing interrupt management services through REE */ +extern esprv_int_mgmt_t esp_tee_intr_sec_srv_cb; +#endif + #if SOC_CPU_HAS_CSR_PC /*performance counter*/ #define CSR_PCER_MACHINE 0x7e0 #define CSR_PCMR_MACHINE 0x7e1 #define CSR_PCCR_MACHINE 0x7e2 +#define CSR_PCCR_USER 0x802 #endif /* SOC_CPU_HAS_CSR_PC */ #if SOC_BRANCH_PREDICTOR_SUPPORTED @@ -89,7 +111,11 @@ FORCE_INLINE_ATTR uint32_t __attribute__((always_inline)) rv_utils_get_cycle_cou #if !SOC_CPU_HAS_CSR_PC return RV_READ_CSR(mcycle); #else - return RV_READ_CSR(CSR_PCCR_MACHINE); + if (IS_PRV_M_MODE()) { + return RV_READ_CSR(CSR_PCCR_MACHINE); + } else { + return RV_READ_CSR(CSR_PCCR_USER); + } #endif } @@ -98,7 +124,11 @@ FORCE_INLINE_ATTR void __attribute__((always_inline)) rv_utils_set_cycle_count(u #if !SOC_CPU_HAS_CSR_PC RV_WRITE_CSR(mcycle, ccount); #else - RV_WRITE_CSR(CSR_PCCR_MACHINE, ccount); + if (IS_PRV_M_MODE()) { + RV_WRITE_CSR(CSR_PCCR_MACHINE, ccount); + } else { + RV_WRITE_CSR(CSR_PCCR_USER, ccount); + } #endif } @@ -113,32 +143,89 @@ FORCE_INLINE_ATTR void rv_utils_set_mtvec(uint32_t mtvec_val) RV_WRITE_CSR(mtvec, mtvec_val | MTVEC_MODE_CSR); } +FORCE_INLINE_ATTR void rv_utils_set_xtvec(uint32_t xtvec_val) +{ + xtvec_val |= MTVEC_MODE_CSR; // Set MODE field to treat XTVEC as a vector base address + if (IS_PRV_M_MODE()) { + RV_WRITE_CSR(mtvec, xtvec_val); + } else { + RV_WRITE_CSR(utvec, xtvec_val); + } +} + // ------------------ Interrupt Control -------------------- FORCE_INLINE_ATTR void rv_utils_intr_enable(uint32_t intr_mask) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_ENABLE_SRV_ID, intr_mask); +#else // Disable all interrupts to make updating of the interrupt mask atomic. unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); esprv_int_enable(intr_mask); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_disable(uint32_t intr_mask) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_DISABLE_SRV_ID, intr_mask); +#else // Disable all interrupts to make updating of the interrupt mask atomic. unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); esprv_int_disable(intr_mask); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_global_enable(void) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(1, TEE_INTR_GLOBAL_EN_SRV_ID); +#else RV_SET_CSR(mstatus, MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_global_disable(void) { +#if CONFIG_SECURE_ENABLE_TEE + if (IS_PRV_M_MODE()) { + RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } +#else RV_CLEAR_CSR(mstatus, MSTATUS_MIE); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_type(int intr_num, enum intr_type type) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(3, TEE_INTR_SET_TYPE_SRV_ID, intr_num, type); +#else + esprv_int_set_type(intr_num, type); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_priority(int rv_int_num, int priority) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(3, TEE_INTR_SET_PRIORITY_SRV_ID, rv_int_num, priority); +#else + esprv_int_set_priority(rv_int_num, priority); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_threshold(int priority_threshold) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_SET_THRESHOLD_SRV_ID, priority_threshold); +#else + esprv_int_set_threshold(priority_threshold); +#endif } /** @@ -352,8 +439,12 @@ FORCE_INLINE_ATTR bool rv_utils_compare_and_set(volatile uint32_t *addr, uint32_ ); #else // For a single core RV target has no atomic CAS instruction, we can achieve atomicity by disabling interrupts - unsigned old_mstatus; - old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + unsigned old_xstatus; + if (IS_PRV_M_MODE()) { + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } // Compare and set uint32_t old_value; old_value = *addr; @@ -361,7 +452,11 @@ FORCE_INLINE_ATTR bool rv_utils_compare_and_set(volatile uint32_t *addr, uint32_ *addr = new_value; } // Restore interrupts - RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); + if (IS_PRV_M_MODE()) { + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + } else { + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); + } #endif //__riscv_atomic return (old_value == compare_value); diff --git a/components/riscv/interrupt_plic.c b/components/riscv/interrupt_plic.c index 86b99cb6169d..9c7aa0d2bfb0 100644 --- a/components/riscv/interrupt_plic.c +++ b/components/riscv/interrupt_plic.c @@ -4,7 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ #include +#include "soc/soc_caps.h" +#include "riscv/csr.h" #include "riscv/interrupt.h" +#include "riscv/rv_utils.h" #include "esp_private/interrupt_plic.h" #include "hal/interrupt_plic_ll.h" @@ -18,19 +21,35 @@ void intr_matrix_route(int intr_src, int intr_num) uint32_t esprv_get_interrupt_unmask(void) { - return interrupt_plic_ll_get_unmask(); + if (IS_PRV_M_MODE()) { + return REG_READ(PLIC_MXINT_ENABLE_REG); + } else { + return REG_READ(PLIC_UXINT_ENABLE_REG); + } } enum intr_type esprv_int_get_type(int rv_int_num) { - return interrupt_plic_ll_get_type(rv_int_num) ? INTR_TYPE_EDGE : INTR_TYPE_LEVEL; + uint32_t intr_type_reg; + + if (IS_PRV_M_MODE()) { + intr_type_reg = REG_READ(PLIC_MXINT_TYPE_REG); + } else { + intr_type_reg = REG_READ(PLIC_UXINT_TYPE_REG); + } + + return (intr_type_reg & (1 << rv_int_num)) ? INTR_TYPE_EDGE : INTR_TYPE_LEVEL; } int esprv_int_get_priority(int rv_int_num) { - return interrupt_plic_ll_get_priority(rv_int_num); + if (IS_PRV_M_MODE()) { + return REG_READ(PLIC_MXINT0_PRI_REG + (rv_int_num) * 4); + } else { + return REG_READ(PLIC_UXINT0_PRI_REG + (rv_int_num) * 4); + } } @@ -38,3 +57,44 @@ bool esprv_int_is_vectored(int rv_int_num) { return true; } + + +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD +DRAM_ATTR esprv_int_mgmt_t esp_tee_intr_sec_srv_cb = NULL; + +void esprv_int_setup_mgmt_cb(void *fptr) +{ + esp_tee_intr_sec_srv_cb = (esprv_int_mgmt_t)fptr; +} + + +/* NOTE: Overriding ROM-based interrupt configuration symbols */ +void esprv_int_enable(uint32_t unmask) +{ + rv_utils_intr_enable(unmask); +} + + +void esprv_int_disable(uint32_t mask) +{ + rv_utils_intr_disable(mask); +} + + +void esprv_int_set_type(int intr_num, enum intr_type type) +{ + rv_utils_intr_set_type(intr_num, type); +} + + +void esprv_int_set_priority(int rv_int_num, int priority) +{ + rv_utils_intr_set_priority(rv_int_num, priority); +} + + +void esprv_int_set_threshold(int priority_threshold) +{ + rv_utils_intr_set_threshold(priority_threshold); +} +#endif diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index d97ad7f83d47..4468155e8076 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -76,11 +76,39 @@ sw t6, RV_STK_T6(sp) .endm -.macro save_mepc +/* Macro for saving special registers depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ +/* Save the XEPC register */ +.macro save_xepc + #if CONFIG_SECURE_ENABLE_TEE + csrr t0, uepc + #else csrr t0, mepc + #endif sw t0, RV_STK_MEPC(sp) .endm +/* Save the required CSRs */ +.macro save_xcsr + #if CONFIG_SECURE_ENABLE_TEE + csrr t0, ustatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, utvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, utval + sw t0, RV_STK_MTVAL(sp) + #else + csrr t0, mstatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, mtvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, mhartid + sw t0, RV_STK_MHARTID(sp) + csrr t0, mtval + sw t0, RV_STK_MTVAL(sp) + #endif +.endm + /* Restore the general purpose registers (excluding gp) from the context on * the stack. The context is then deallocated. The default size is CONTEXT_SIZE * but it can be overridden. */ @@ -117,11 +145,49 @@ addi sp,sp, \cxt_size .endm -.macro restore_mepc +/* Macro for restoring special registers depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ + +/* Restore the XEPC register depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ +.macro restore_xepc lw t0, RV_STK_MEPC(sp) +#if CONFIG_SECURE_ENABLE_TEE + csrw uepc, t0 +#else csrw mepc, t0 +#endif .endm +/* Macros for enabling/disabling the global interrupts based on the + * active execution environment - REE - U-mode / TEE - M-mode */ +.macro enable_intr +#if CONFIG_SECURE_ENABLE_TEE + li t0, 0x1 + csrs ustatus, t0 +#else + li t0, 0x8 + csrs mstatus, t0 +#endif +.endm + +.macro disable_intr +#if CONFIG_SECURE_ENABLE_TEE + li t0, 0x1 + csrc ustatus, t0 +#else + li t0, 0x8 + csrc mstatus, t0 +#endif +.endm + +.macro xret +#if CONFIG_SECURE_ENABLE_TEE + uret +#else + mret +#endif +.endm .global rtos_int_enter .global rtos_int_exit @@ -148,19 +214,15 @@ _panic_handler: /* Save CSRs */ sw t0, RV_STK_SP(sp) - csrr t0, mepc - sw t0, RV_STK_MEPC(sp) - csrr t0, mstatus - sw t0, RV_STK_MSTATUS(sp) - csrr t0, mtvec - sw t0, RV_STK_MTVEC(sp) - csrr t0, mhartid - sw t0, RV_STK_MHARTID(sp) - csrr t0, mtval - sw t0, RV_STK_MTVAL(sp) + save_xepc + save_xcsr /* Keep mcause in s0, only the exception code and interrupt bit are relevant */ +#if CONFIG_SECURE_ENABLE_TEE + csrr s0, ucause +#else csrr s0, mcause +#endif li t1, VECTORS_MCAUSE_INTBIT_MASK | VECTORS_MCAUSE_REASON_MASK and s0, s0, t1 @@ -274,7 +336,7 @@ _store_mcause: * Restore the registers and return from the exception. */ _return_from_exception: - restore_mepc + restore_xepc /* MTVEC and SP are assumed to be unmodified. * MSTATUS, MHARTID, MTVAL are read-only and not restored. */ @@ -290,12 +352,14 @@ _return_from_exception: * from the stack. */ .global _interrupt_handler + .global _tee_interrupt_handler .type _interrupt_handler, @function _interrupt_handler: +_tee_interrupt_handler: /* Start by saving the general purpose registers and the PC value before * the interrupt happened. */ save_general_regs - save_mepc + save_xepc /* Though it is not necessary we save GP and SP here. * SP is necessary to help GDB to properly unwind @@ -317,8 +381,13 @@ _interrupt_handler: /* If this is a non-nested interrupt, SP now points to the interrupt stack */ /* Before dispatch c handler, restore interrupt to enable nested intr */ +#if CONFIG_SECURE_ENABLE_TEE + csrr s1, ucause + csrr s2, ustatus +#else csrr s1, mcause csrr s2, mstatus +#endif #if !SOC_INT_HW_NESTED_SUPPORTED /* Save the interrupt threshold level */ @@ -337,7 +406,7 @@ _interrupt_handler: fence #endif // !SOC_INT_HW_NESTED_SUPPORTED - csrsi mstatus, 0x8 + enable_intr /* MIE set. Nested interrupts can now occur */ #ifdef CONFIG_PM_TRACE @@ -366,7 +435,7 @@ _interrupt_handler: /* After dispatch c handler, disable interrupt to make freertos make context switch */ - csrci mstatus, 0x8 + disable_intr /* MIE cleared. Nested interrupts are disabled */ #if !SOC_INT_HW_NESTED_SUPPORTED @@ -386,10 +455,15 @@ _interrupt_handler: * In case the target uses the CLIC, it is mandatory to restore `mcause` register since it contains * the former CPU priority. When executing `mret`, the hardware will restore the former threshold, * from `mcause` to `mintstatus` CSR */ +#if CONFIG_SECURE_ENABLE_TEE + csrw ucause, s1 + csrw ustatus, a0 +#else csrw mcause, s1 csrw mstatus, a0 - restore_mepc +#endif + restore_xepc restore_general_regs /* exit, this will also re-enable the interrupts */ - mret + xret .size _interrupt_handler, .-_interrupt_handler From 420810ee77ed2164c669f1431d097c2d10a0f426 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 1 Jul 2024 17:17:36 +0530 Subject: [PATCH 09/10] feat(esp_tee): Support for ESP-TEE - `tools` directory --- .gitlab/CODEOWNERS | 1 + CMakeLists.txt | 29 +++++++++++++++++++---------- tools/ci/artifacts_handler.py | 3 +++ tools/ci/idf_ci/uploader.py | 3 +++ tools/cmake/build.cmake | 1 + 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 0ccfb93f8cf2..1d8ec1189c64 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -111,6 +111,7 @@ /components/esp_rom/ @esp-idf-codeowners/system @esp-idf-codeowners/bluetooth @esp-idf-codeowners/wifi /components/esp_security/ @esp-idf-codeowners/security /components/esp_system/ @esp-idf-codeowners/system +/components/esp_tee/ @esp-idf-codeowners/security /components/esp_timer/ @esp-idf-codeowners/system /components/esp-tls/ @esp-idf-codeowners/app-utilities /components/esp_vfs_*/ @esp-idf-codeowners/storage diff --git a/CMakeLists.txt b/CMakeLists.txt index b4dbce1bc235..4e21ee607cca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,9 @@ endif() # Add the following build specifications here, since these seem to be dependent # on config values on the root Kconfig. -if(NOT BOOTLOADER_BUILD) +if(BOOTLOADER_BUILD) - if(CONFIG_COMPILER_OPTIMIZATION_SIZE) + if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE) if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND compile_options "-Oz") else() @@ -24,20 +24,29 @@ if(NOT BOOTLOADER_BUILD) if(CMAKE_C_COMPILER_ID MATCHES "GNU") list(APPEND compile_options "-freorder-blocks") endif() - elseif(CONFIG_COMPILER_OPTIMIZATION_DEBUG) + elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG) list(APPEND compile_options "-Og") if(CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CONFIG_IDF_TARGET_LINUX) list(APPEND compile_options "-fno-shrink-wrap") # Disable shrink-wrapping to reduce binary size endif() - elseif(CONFIG_COMPILER_OPTIMIZATION_NONE) + elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE) list(APPEND compile_options "-O0") - elseif(CONFIG_COMPILER_OPTIMIZATION_PERF) + elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF) list(APPEND compile_options "-O2") endif() -else() # BOOTLOADER_BUILD +elseif(ESP_TEE_BUILD) - if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE) + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + list(APPEND compile_options "-Oz") + else() + list(APPEND compile_options "-Os") + list(APPEND compile_options "-freorder-blocks") + endif() + +else() + + if(CONFIG_COMPILER_OPTIMIZATION_SIZE) if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND compile_options "-Oz") else() @@ -46,14 +55,14 @@ else() # BOOTLOADER_BUILD if(CMAKE_C_COMPILER_ID MATCHES "GNU") list(APPEND compile_options "-freorder-blocks") endif() - elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG) + elseif(CONFIG_COMPILER_OPTIMIZATION_DEBUG) list(APPEND compile_options "-Og") if(CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CONFIG_IDF_TARGET_LINUX) list(APPEND compile_options "-fno-shrink-wrap") # Disable shrink-wrapping to reduce binary size endif() - elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE) + elseif(CONFIG_COMPILER_OPTIMIZATION_NONE) list(APPEND compile_options "-O0") - elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF) + elseif(CONFIG_COMPILER_OPTIMIZATION_PERF) list(APPEND compile_options "-O2") endif() diff --git a/tools/ci/artifacts_handler.py b/tools/ci/artifacts_handler.py index 06752b028304..3a2cd429e6db 100644 --- a/tools/ci/artifacts_handler.py +++ b/tools/ci/artifacts_handler.py @@ -30,6 +30,8 @@ class ArtifactType(str, Enum): ArtifactType.MAP_AND_ELF_FILES: [ '**/build*/bootloader/*.map', '**/build*/bootloader/*.elf', + '**/build*/esp_tee/*.map', + '**/build*/esp_tee/*.elf', '**/build*/*.map', '**/build*/*.elf', ], @@ -37,6 +39,7 @@ class ArtifactType(str, Enum): f'**/build*/{DEFAULT_BUILD_LOG_FILENAME}', '**/build*/*.bin', '**/build*/bootloader/*.bin', + '**/build*/esp_tee/*.bin', '**/build*/partition_table/*.bin', '**/build*/flasher_args.json', '**/build*/flash_project_args', diff --git a/tools/ci/idf_ci/uploader.py b/tools/ci/idf_ci/uploader.py index f04028b1e672..e9ef67599caa 100644 --- a/tools/ci/idf_ci/uploader.py +++ b/tools/ci/idf_ci/uploader.py @@ -45,6 +45,8 @@ class AppUploader(AppDownloader): ArtifactType.MAP_AND_ELF_FILES: [ 'bootloader/*.map', 'bootloader/*.elf', + 'esp_tee/*.map', + 'esp_tee/*.elf', '*.map', '*.elf', 'gdbinit/*', @@ -52,6 +54,7 @@ class AppUploader(AppDownloader): ArtifactType.BUILD_DIR_WITHOUT_MAP_AND_ELF_FILES: [ '*.bin', 'bootloader/*.bin', + 'esp_tee/*.bin', 'partition_table/*.bin', 'flasher_args.json', 'flash_project_args', diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index b3d72516e5a2..0c8a72ee1c8e 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -525,6 +525,7 @@ macro(idf_build_process target) idf_build_set_property(BOOTLOADER_BUILD "${BOOTLOADER_BUILD}") idf_build_set_property(NON_OS_BUILD "${NON_OS_BUILD}") + idf_build_set_property(ESP_TEE_BUILD "${ESP_TEE_BUILD}") idf_build_set_property(IDF_TOOLCHAIN "${IDF_TOOLCHAIN}") From 373930655ab26714ab0028fc01f85dacd48ae7ae Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 15 Nov 2024 10:45:29 +0530 Subject: [PATCH 10/10] feat(esp_tee): Support for ESP-TEE - the `main` component --- components/esp_tee/CMakeLists.txt | 138 ++++ components/esp_tee/Kconfig.projbuild | 144 ++++ components/esp_tee/include/esp_tee.h | 90 +++ .../esp_tee/include/private/esp_tee_app.h | 37 + .../esp_tee/include/private/esp_tee_binary.h | 100 +++ components/esp_tee/project_include.cmake | 52 ++ .../scripts/esp32c6/secure_service.tbl | 42 ++ .../esp_tee/scripts/secure_service_hdr.py | 76 ++ .../esp_tee/scripts/secure_service_tbl.py | 50 ++ .../esp_tee/scripts/secure_service_wrap.py | 24 + .../esp_tee/src/esp_secure_service_wrapper.c | 218 ++++++ components/esp_tee/src/esp_tee.c | 76 ++ components/esp_tee/src/esp_tee_config.c | 34 + components/esp_tee/src/esp_tee_u2m_switch.S | 13 + components/esp_tee/subproject/CMakeLists.txt | 87 +++ .../components/attestation/CMakeLists.txt | 21 + .../attestation/esp_att_utils_crypto.c | 341 +++++++++ .../attestation/esp_att_utils_json.c | 288 ++++++++ .../attestation/esp_att_utils_part_info.c | 545 ++++++++++++++ .../components/attestation/esp_attestation.c | 304 ++++++++ .../attestation/include/esp_attestation.h | 31 + .../private_include/esp_attestation_utils.h | 278 ++++++++ .../components/tee_attestation/CMakeLists.txt | 13 + .../tee_attestation/esp_tee_attestation.c | 27 + .../tee_attestation/esp_tee_attestation.h | 42 ++ .../components/tee_flash_mgr/CMakeLists.txt | 7 + .../components/tee_flash_mgr/esp_tee_flash.c | 151 ++++ .../tee_flash_mgr/include/esp_tee_flash.h | 78 ++ .../components/tee_ota_ops/CMakeLists.txt | 17 + .../components/tee_ota_ops/esp_tee_ota_ops.c | 143 ++++ .../tee_ota_ops/esp_tee_ota_ops_wrapper.c | 35 + .../tee_ota_ops/include/esp_tee_ota_ops.h | 60 ++ .../components/tee_sec_storage/CMakeLists.txt | 17 + .../include/esp_tee_sec_storage.h | 146 ++++ .../tee_sec_storage/tee_sec_storage.c | 669 ++++++++++++++++++ .../tee_sec_storage/tee_sec_storage_wrapper.c | 53 ++ .../esp_tee/subproject/main/CMakeLists.txt | 92 +++ .../main/arch/riscv/esp_tee_secure_entry.S | 42 ++ .../main/arch/riscv/esp_tee_vector_table.S | 79 +++ .../main/arch/riscv/esp_tee_vectors.S | 534 ++++++++++++++ .../esp_tee/subproject/main/common/brownout.c | 125 ++++ .../subproject/main/common/esp_app_desc_tee.c | 43 ++ .../subproject/main/common/multi_heap.c | 196 +++++ .../main/common/panic/esp_tee_panic.c | 123 ++++ .../main/common/panic/panic_helper.h | 13 + .../main/common/panic/panic_helper_riscv.c | 152 ++++ .../main/core/esp_secure_service_table.c | 25 + .../main/core/esp_secure_services.c | 410 +++++++++++ .../subproject/main/core/esp_tee_init.c | 186 +++++ .../subproject/main/core/esp_tee_intr.c | 185 +++++ .../esp_tee/subproject/main/idf_component.yml | 6 + .../main/include/esp_tee_brownout.h | 18 + .../subproject/main/include/esp_tee_intr.h | 78 ++ .../subproject/main/include/multi_heap.h | 100 +++ .../subproject/main/ld/esp32c6/esp_tee.ld.in | 243 +++++++ .../subproject/main/ld/esp_tee_ld.cmake | 29 + .../main/soc/esp32c6/esp_tee_aes_intr.c | 33 + .../main/soc/esp32c6/esp_tee_apm_intr.c | 126 ++++ .../main/soc/esp32c6/esp_tee_apm_prot_cfg.c | 306 ++++++++ .../soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c | 179 +++++ .../main/soc/esp32c6/esp_tee_secure_sys_cfg.c | 105 +++ .../soc/esp32c6/include/esp_tee_aes_intr.h | 16 + .../soc/esp32c6/include/esp_tee_apm_intr.h | 24 + .../soc/esp32c6/include/esp_tee_rv_utils.h | 128 ++++ .../esp_tee/test_apps/.build-test-rules.yml | 11 + .../test_apps/tee_cli_app/CMakeLists.txt | 16 + .../esp_tee/test_apps/tee_cli_app/README.md | 292 ++++++++ .../test_apps/tee_cli_app/https_server.py | 86 +++ .../test_apps/tee_cli_app/main/CMakeLists.txt | 6 + .../tee_cli_app/main/Kconfig.projbuild | 15 + .../test_apps/tee_cli_app/main/app_main.c | 80 +++ .../tee_cli_app/main/example_tee_srv.h | 33 + .../test_apps/tee_cli_app/main/tee_cmd_wifi.c | 134 ++++ .../test_apps/tee_cli_app/main/tee_srv_att.c | 57 ++ .../test_apps/tee_cli_app/main/tee_srv_ota.c | 300 ++++++++ .../tee_cli_app/main/tee_srv_sec_str.c | 547 ++++++++++++++ .../test_apps/tee_cli_app/pytest_tee_cli.py | 242 +++++++ .../tee_cli_app/sdkconfig.ci.default | 0 .../test_apps/tee_cli_app/sdkconfig.ci.sb_fe | 15 + .../test_apps/tee_cli_app/sdkconfig.defaults | 20 + .../tee_cli_app/test_certs/server_cert.pem | 20 + .../tee_cli_app/test_certs/server_key.pem | 28 + .../test_keys/secure_boot_signing_key.pem | 39 + .../esp_tee/test_apps/tee_cli_app/version.txt | 1 + .../test_apps/tee_test_fw/CMakeLists.txt | 18 + .../esp_tee/test_apps/tee_test_fw/README.md | 28 + .../components/test_sec_srv/CMakeLists.txt | 22 + .../test_sec_srv/include/esp_tee_test.h | 42 ++ .../test_sec_srv/src/test_dummy_srv.c | 14 + .../test_sec_srv/src/test_dummy_srv_wrapper.c | 14 + .../test_sec_srv/src/test_interrupt.c | 174 +++++ .../components/test_sec_srv/src/test_panic.c | 82 +++ .../test_sec_srv/src/test_sec_srv.c | 47 ++ .../components/test_sec_srv/test.tbl | 18 + .../test_sec_srv/test_tee_project.cmake | 15 + .../esp_tee/test_apps/tee_test_fw/conftest.py | 178 +++++ .../test_apps/tee_test_fw/main/CMakeLists.txt | 37 + .../test_apps/tee_test_fw/main/app_main.c | 46 ++ .../tee_test_fw/main/idf_component.yml | 2 + .../tee_test_fw/main/test_esp_tee_att.c | 301 ++++++++ .../main/test_esp_tee_ctx_switch.c | 95 +++ .../tee_test_fw/main/test_esp_tee_interrupt.c | 216 ++++++ .../tee_test_fw/main/test_esp_tee_ota.c | 173 +++++ .../tee_test_fw/main/test_esp_tee_panic.c | 165 +++++ .../tee_test_fw/main/test_esp_tee_sec_stg.c | 274 +++++++ .../test_apps/tee_test_fw/partitions.csv | 6 + .../tee_test_fw/partitions_tee_ota.csv | 10 + .../tee_test_fw/pytest_esp_tee_ut.py | 301 ++++++++ .../tee_test_fw/sdkconfig.ci.default | 0 .../test_apps/tee_test_fw/sdkconfig.ci.ota | 11 + .../test_apps/tee_test_fw/sdkconfig.defaults | 17 + 111 files changed, 12291 insertions(+) create mode 100644 components/esp_tee/CMakeLists.txt create mode 100644 components/esp_tee/Kconfig.projbuild create mode 100644 components/esp_tee/include/esp_tee.h create mode 100644 components/esp_tee/include/private/esp_tee_app.h create mode 100644 components/esp_tee/include/private/esp_tee_binary.h create mode 100644 components/esp_tee/project_include.cmake create mode 100644 components/esp_tee/scripts/esp32c6/secure_service.tbl create mode 100644 components/esp_tee/scripts/secure_service_hdr.py create mode 100644 components/esp_tee/scripts/secure_service_tbl.py create mode 100644 components/esp_tee/scripts/secure_service_wrap.py create mode 100644 components/esp_tee/src/esp_secure_service_wrapper.c create mode 100644 components/esp_tee/src/esp_tee.c create mode 100644 components/esp_tee/src/esp_tee_config.c create mode 100644 components/esp_tee/src/esp_tee_u2m_switch.S create mode 100644 components/esp_tee/subproject/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/attestation/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_json.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_attestation.c create mode 100644 components/esp_tee/subproject/components/attestation/include/esp_attestation.h create mode 100644 components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h create mode 100644 components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c create mode 100644 components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c create mode 100644 components/esp_tee/subproject/main/CMakeLists.txt create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S create mode 100644 components/esp_tee/subproject/main/common/brownout.c create mode 100644 components/esp_tee/subproject/main/common/esp_app_desc_tee.c create mode 100644 components/esp_tee/subproject/main/common/multi_heap.c create mode 100644 components/esp_tee/subproject/main/common/panic/esp_tee_panic.c create mode 100644 components/esp_tee/subproject/main/common/panic/panic_helper.h create mode 100644 components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c create mode 100644 components/esp_tee/subproject/main/core/esp_secure_service_table.c create mode 100644 components/esp_tee/subproject/main/core/esp_secure_services.c create mode 100644 components/esp_tee/subproject/main/core/esp_tee_init.c create mode 100644 components/esp_tee/subproject/main/core/esp_tee_intr.c create mode 100644 components/esp_tee/subproject/main/idf_component.yml create mode 100644 components/esp_tee/subproject/main/include/esp_tee_brownout.h create mode 100644 components/esp_tee/subproject/main/include/esp_tee_intr.h create mode 100644 components/esp_tee/subproject/main/include/multi_heap.h create mode 100644 components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in create mode 100644 components/esp_tee/subproject/main/ld/esp_tee_ld.cmake create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h create mode 100644 components/esp_tee/test_apps/.build-test-rules.yml create mode 100644 components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_cli_app/README.md create mode 100644 components/esp_tee/test_apps/tee_cli_app/https_server.py create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/app_main.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/version.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/README.md create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake create mode 100644 components/esp_tee/test_apps/tee_test_fw/conftest.py create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/app_main.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/partitions.csv create mode 100644 components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv create mode 100644 components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults diff --git a/components/esp_tee/CMakeLists.txt b/components/esp_tee/CMakeLists.txt new file mode 100644 index 000000000000..d66d84e447ff --- /dev/null +++ b/components/esp_tee/CMakeLists.txt @@ -0,0 +1,138 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) +idf_build_get_property(custom_secure_service_tbl CUSTOM_SECURE_SERVICE_TBL) +idf_build_get_property(custom_secure_service_dir CUSTOM_SECURE_SERVICE_COMPONENT_DIR) +idf_build_get_property(custom_secure_service_component CUSTOM_SECURE_SERVICE_COMPONENT) +idf_build_get_property(target IDF_TARGET) +# headers & sources here are compiled into the app, not the esp_tee binary +# (see subproject/ for the esp_tee binary build files) + +# ESP-TEE is currently supported only on the ESP32-C6 SoC +if(NOT ${target} STREQUAL "esp32c6") + return() +endif() + +if(BOOTLOADER_BUILD) + idf_component_register() + return() +elseif(esp_tee_build) + # TEE build currently only uses the shared headers. + idf_component_register(INCLUDE_DIRS include) +else() + if(CONFIG_SECURE_ENABLE_TEE) + if(NOT CMAKE_BUILD_EARLY_EXPANSION) + # Add custom flash target for TEE binary + partition_table_get_partition_info(partition "--partition-type app --partition-subtype tee_0" "name") + if(NOT partition) + message(FATAL_ERROR "Partition table missing TEE partition entry!") + endif() + add_dependencies(esp_tee partition_table_bin) + add_dependencies(flash esp_tee) + set(image_file ${TEE_BUILD_DIR}/esp_tee.bin) + partition_table_get_partition_info(offset "--partition-name ${partition}" "offset") + esptool_py_flash_target_image(flash "${partition}" "${offset}" "${image_file}") + endif() + + partition_table_get_partition_info(tee_otadata_offset + "--partition-type data --partition-subtype tee_ota" "offset") + partition_table_get_partition_info(tee_otadata_size + "--partition-type data --partition-subtype tee_ota" "size") + + # Add custom target for generating empty otadata partition for flashing + if(tee_otadata_offset AND tee_otadata_size) + idf_build_get_property(build_dir BUILD_DIR) + set(blank_tee_otadata_file ${build_dir}/tee_ota_data_initial.bin) + + idf_build_get_property(python PYTHON) + idf_component_get_property(partition_table_dir partition_table COMPONENT_DIR) + add_custom_command(OUTPUT ${blank_tee_otadata_file} + COMMAND ${python} ${partition_table_dir}/gen_empty_partition.py + ${tee_otadata_size} ${blank_tee_otadata_file}) + add_custom_target(blank_tee_ota_data ALL DEPENDS ${blank_tee_otadata_file}) + + add_dependencies(flash blank_tee_ota_data) + add_dependencies(encrypted-flash blank_tee_ota_data) + + partition_table_get_partition_info(tee_otadata_part + "--partition-type data --partition-subtype tee_ota" "name") + + idf_component_get_property(main_args esptool_py FLASH_ARGS) + idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS) + esptool_py_flash_target(tee_otadata-flash "${main_args}" "${sub_args}") + + esptool_py_flash_target_image(tee_otadata-flash + "${tee_otadata_part}" "${tee_otadata_offset}" "${blank_tee_otadata_file}") + esptool_py_flash_target_image(flash + "${tee_otadata_part}" "${tee_otadata_offset}" "${blank_tee_otadata_file}") + endif() + + set(srcs "src/esp_tee.c" + "src/esp_tee_config.c" + "src/esp_secure_service_wrapper.c" + "src/esp_tee_u2m_switch.S") + endif() + + idf_component_register(INCLUDE_DIRS include + SRCS ${srcs} + PRIV_REQUIRES efuse esp_system spi_flash) + + if(CONFIG_SECURE_ENABLE_TEE) + set(EXTRA_LINK_FLAGS) + list(APPEND EXTRA_LINK_FLAGS "-u esp_tee_app_config") + target_link_libraries(${COMPONENT_LIB} INTERFACE "${EXTRA_LINK_FLAGS}") + endif() +endif() + +set(secure_service_hdr_py + ${COMPONENT_DIR}/scripts/secure_service_hdr.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl + ) + +set(secure_service_tbl_py + ${COMPONENT_DIR}/scripts/secure_service_tbl.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl + ) + +set(secure_service_wrap_py + ${COMPONENT_DIR}/scripts/secure_service_wrap.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl + ) + +set(secure_service_num_h + ${CONFIG_DIR}/secure_service_num.h + ) +set(secure_service_dec_h + ${CONFIG_DIR}/secure_service_dec.h) + +set(secure_service_h + ${CONFIG_DIR}/secure_service.h + ) + +if(CONFIG_SECURE_ENABLE_TEE) + execute_process(COMMAND cat ${COMPONENT_DIR}/scripts/${target}/secure_service.tbl ${custom_secure_service_tbl} + OUTPUT_FILE secure_service.tbl + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + + execute_process(COMMAND python ${secure_service_hdr_py} ${secure_service_num_h} ${secure_service_dec_h} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + + execute_process(COMMAND python ${secure_service_tbl_py} ${secure_service_h} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + + set_property(DIRECTORY "${COMPONENT_DIR}" APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES ${secure_service_num_h} ${secure_service_dec_h} ${secure_service_h}) + + # For TEE implementation, we don't wrap the APIs since the TEE would also internally use the same API and + # it shouldn't route to secure service API. + # Instead of wrapping, we append _ss_* to the API name and then it must be defined in esp_secure_services.c + if(NOT esp_tee_build) + execute_process(COMMAND python ${secure_service_wrap_py} + OUTPUT_VARIABLE wrap_list + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(STRIP ${wrap_list} wrap_list) + + target_link_libraries(${COMPONENT_LIB} INTERFACE "${wrap_list}") + endif() +endif() diff --git a/components/esp_tee/Kconfig.projbuild b/components/esp_tee/Kconfig.projbuild new file mode 100644 index 000000000000..5037dcdd4411 --- /dev/null +++ b/components/esp_tee/Kconfig.projbuild @@ -0,0 +1,144 @@ +menu "ESP-TEE (Trusted Execution Environment)" + depends on IDF_TARGET_ESP32C6 + + config SECURE_ENABLE_TEE + bool "Enable the ESP-TEE framework" + depends on IDF_TARGET_ESP32C6 + select ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE + help + This configuration enables the Trusted Execution Environment (TEE) feature. + + menu "Memory Configuration" + depends on SECURE_ENABLE_TEE + + config SECURE_TEE_IRAM_SIZE + hex "IRAM region size" + default 0x8000 + range 0x8000 0x10000 + help + This configuration sets the IRAM size for the TEE module. + This should be a multiple of 0x1000. + + config SECURE_TEE_DRAM_SIZE + hex "DRAM region size" + default 0x8000 + range 0x8000 0x10000 + help + This configuration sets the DRAM size for the TEE module. + This should be a multiple of 0x1000. + + config SECURE_TEE_STACK_SIZE + hex "Stack size" + default 0xc00 + range 0x800 0x1000 + help + This configuration sets the stack size for the TEE module. + The TEE stack will be allocated from the TEE DRAM region. + This should be a multiple of 0x100. + + config SECURE_TEE_INTR_STACK_SIZE + hex "Interrupt Stack size" + default 0x400 + range 0x400 0x800 + help + This configuration sets the interrupt stack size for the TEE module. + The TEE interrupt stack will be allocated from the TEE DRAM region. + This should be a multiple of 0x100. + + config SECURE_TEE_IROM_SIZE + hex + default 0x10000 + help + This should be a multiple of MMU_PAGE_SIZE. + + config SECURE_TEE_DROM_SIZE + hex + default 0x10000 + help + This should be a multiple of MMU_PAGE_SIZE. + + endmenu + + choice SECURE_TEE_SEC_STG_MODE + prompt "Secure Storage: Mode" + depends on SECURE_ENABLE_TEE + default SECURE_TEE_SEC_STG_MODE_DEVELOPMENT + help + Select the TEE secure storage mode + + config SECURE_TEE_SEC_STG_MODE_DEVELOPMENT + bool "Development" + help + Secure storage will be encrypted by the data stored in eFuse BLK2 + + config SECURE_TEE_SEC_STG_MODE_RELEASE + depends on IDF_TARGET_ESP32C6 + bool "Release" + help + Secure storage will be encrypted by the data stored in eFuse block + configured through the SECURE_TEE_SEC_STG_KEY_EFUSE_BLK option + + endchoice + + config SECURE_TEE_SEC_STG_KEY_EFUSE_BLK + int "Secure Storage: Encryption key eFuse block" + depends on SECURE_TEE_SEC_STG_MODE_RELEASE + range 4 10 + default 10 + help + eFuse block ID storing the TEE secure storage encryption key + + config SECURE_TEE_ATT_KEY_SLOT_ID + depends on SECURE_ENABLE_TEE + int "Attestation: Secure Storage slot ID for EAT signing" + default 0 + range 0 14 + help + This configuration sets the slot ID from the TEE secure storage + storing the ECDSA keypair for executing sign/verify operations + from the TEE side (E.g. Attestation) + + config SECURE_TEE_DEBUG_MODE + bool "Enable Debug Mode" + default y + depends on SECURE_ENABLE_TEE + help + This configuration enables the logging from the TEE module. + + choice SECURE_TEE_LOG_LEVEL + bool "Log verbosity" + default SECURE_TEE_LOG_LEVEL_WARN + depends on SECURE_TEE_DEBUG_MODE + help + Specify how much output to see in TEE logs. + + config SECURE_TEE_LOG_LEVEL_NONE + bool "No output" + config SECURE_TEE_LOG_LEVEL_ERROR + bool "Error" + config SECURE_TEE_LOG_LEVEL_WARN + bool "Warning" + config SECURE_TEE_LOG_LEVEL_INFO + bool "Info" + config SECURE_TEE_LOG_LEVEL_DEBUG + bool "Debug" + config SECURE_TEE_LOG_LEVEL_VERBOSE + bool "Verbose" + endchoice + + config SECURE_TEE_LOG_LEVEL + int + default 0 if SECURE_TEE_LOG_LEVEL_NONE || !SECURE_TEE_DEBUG_MODE + default 1 if SECURE_TEE_LOG_LEVEL_ERROR + default 2 if SECURE_TEE_LOG_LEVEL_WARN + default 3 if SECURE_TEE_LOG_LEVEL_INFO + default 4 if SECURE_TEE_LOG_LEVEL_DEBUG + default 5 if SECURE_TEE_LOG_LEVEL_VERBOSE + + config SECURE_TEE_TEST_MODE + bool "Enable Test Mode" + depends on SECURE_ENABLE_TEE + help + This configuration sets up the TEE framework as required for executing the test suite. + +endmenu diff --git a/components/esp_tee/include/esp_tee.h b/components/esp_tee/include/esp_tee.h new file mode 100644 index 000000000000..3b4833c93770 --- /dev/null +++ b/components/esp_tee/include/esp_tee.h @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __ASSEMBLER__ +#include +#include +#include +#include "soc/soc.h" +#include "sdkconfig.h" +#include "esp_cpu.h" +#include "esp_attr.h" + +#include "riscv/rv_utils.h" + +#define ESP_TEE_APP_CFG_MAGIC 0x3348AAED + +#define ESP_TEE_API_MAJOR_VER 1 +#define ESP_TEE_API_MINOR_VER 0 +#define ESP_TEE_API_PATCH_VER 0 + +/** + * @brief CPU privilege mode + */ +typedef enum { + ESP_CPU_NS_MODE = 0, /* Corresponds to the RISC-V User (U) mode */ + ESP_CPU_S_MODE = 3, /* Corresponds to the RISC-V Machine (M) mode */ +} esp_cpu_priv_mode_t; + +/** + * @brief Configuration structure defining the interface between TEE and REE (user) app + * + * This configuration structure is embedded in the REE (user) app's IRAM section. + * The TEE reads and updates this structure before switching to the REE, and then + * write-protects it. + * + * @note All accesses to this structure must be 32-bit aligned since it resides in + * the (user app) IRAM section. + */ +typedef struct { + uint32_t magic_word; + uint32_t api_major_version; + uint32_t api_minor_version; + uint32_t reserved[2]; + /* TEE-related fields */ + void *s_entry_addr; + void *s_int_handler; + /* REE-related fields */ + void *ns_entry_addr; + void *ns_int_handler; + void *ns_iram_end; + void *ns_irom_end; + void *ns_drom_end; +} __attribute__((aligned(4))) __attribute__((__packed__)) esp_tee_config_t; + +extern esp_tee_config_t esp_tee_app_config; + +#endif // ifndef __ASSEMBLER__ + +#if !ESP_TEE_BUILD +#include "private/esp_tee_app.h" +#else +#include "private/esp_tee_binary.h" +#endif + +/* Offsets of some values in esp_tee_config_t that are used by assembly code */ +#define ESP_TEE_CFG_OFFS_S_ENTRY_ADDR 0x14 +#define ESP_TEE_CFG_OFFS_S_INTR_HANDLER 0x18 +#define ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR 0x1C +#define ESP_TEE_CFG_OFFS_NS_INTR_HANDLER 0x20 + +#ifndef __ASSEMBLER__ +/* Check the offsets are correct using the C compiler */ +ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, s_entry_addr) == ESP_TEE_CFG_OFFS_S_ENTRY_ADDR, "offset macro is wrong"); +ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, s_int_handler) == ESP_TEE_CFG_OFFS_S_INTR_HANDLER, "offset macro is wrong"); +ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, ns_entry_addr) == ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR, "offset macro is wrong"); +ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, ns_int_handler) == ESP_TEE_CFG_OFFS_NS_INTR_HANDLER, "offset macro is wrong"); +#endif // ifndef __ASSEMBLER__ + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/include/private/esp_tee_app.h b/components/esp_tee/include/private/esp_tee_app.h new file mode 100644 index 000000000000..7aa652bd6303 --- /dev/null +++ b/components/esp_tee/include/private/esp_tee_app.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Interface function that allows untrusted applications to invoke secure services through TEE + * + * @param argc Number of arguments being passed to the secure service + * + * @return Value returned by the secure service function + */ +uint32_t esp_tee_service_call(int argc, ...); + +/** + * @brief Interface function that allows untrusted applications to invoke secure services through TEE, + * with the scheduler and the non-IRAM interrupts disabled + * + * @param argc Number of arguments being passed to the secure service + * + * @return Value returned by the secure service function + */ +uint32_t esp_tee_service_call_with_noniram_intr_disabled(int argc, ...); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/include/private/esp_tee_binary.h b/components/esp_tee/include/private/esp_tee_binary.h new file mode 100644 index 000000000000..f0975f451b3f --- /dev/null +++ b/components/esp_tee/include/private/esp_tee_binary.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" + +/* Declarations used inside TEE binary, only */ + +#define portNUM_PROCESSORS (1) +#define configNUM_CORES (portNUM_PROCESSORS) +#define TEE_SECURE_INUM (14) + +#define ESP_TEE_M2U_SWITCH_MAGIC 0xfedef + +#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) +#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) + +/* NOTE: ESP32-C6 - TEE/REE memory regions */ +/* TEE I/DRAM */ +#define SOC_S_IRAM_START (SOC_IRAM_LOW) +#define SOC_S_IRAM_END (SOC_S_IRAM_START + CONFIG_SECURE_TEE_IRAM_SIZE) +#define SOC_S_DRAM_START (SOC_S_IRAM_END) +#define SOC_S_DRAM_END (SOC_S_IRAM_END + CONFIG_SECURE_TEE_DRAM_SIZE) +#define SOC_NS_IRAM_START (SOC_S_DRAM_END) +/* TEE I/DROM */ +#define SOC_S_IDROM_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) +#define SOC_S_IDROM_MMU_PAGE_NUM (SOC_S_IDROM_SIZE / SOC_MMU_PAGE_SIZE) +#define SOC_S_IROM_LOW (SOC_IROM_LOW) +#define SOC_S_IROM_HIGH (SOC_IROM_LOW + SOC_S_IDROM_SIZE) +#define SOC_S_DROM_LOW (SOC_DROM_LOW) +#define SOC_S_DROM_HIGH (SOC_DROM_LOW + SOC_S_IDROM_SIZE) + +#define SOC_MMU_TOTAL_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW) +#define SOC_MMU_END_VADDR (SOC_DROM_LOW + SOC_MMU_TOTAL_SIZE) +#define SOC_S_MMU_MMAP_RESV_PAGE_NUM (SOC_S_IDROM_MMU_PAGE_NUM + 1) +#define SOC_S_MMU_MMAP_RESV_START_VADDR (SOC_MMU_END_VADDR - SOC_S_MMU_MMAP_RESV_PAGE_NUM * SOC_MMU_PAGE_SIZE) + +#ifndef __ASSEMBLER__ +#include +#include +#include +#include "esp_rom_sys.h" + +/** + * @brief TEE initialization function called by the bootloader at boot time. + * Performs secure system initialization before switching to the REE. + * + * @param ree_entry_addr entry point to the App where TEE jump after completing secure initialization + * @param ree_drom_addr DROM address of the selected non-secure app for determining the running non-secure app partition + * @param tee_boot_part partition subtype of the active TEE partition + */ +void esp_tee_init(uint32_t ree_entry_addr, uint32_t ree_drom_addr, uint8_t tee_boot_part); + +/** + * @brief SoC-specific TEE secure initialization + */ +void esp_tee_soc_secure_sys_init(void); + +/** + * @brief Configure region protection through RISC-V PMP/PMA for TEE + */ +void esp_tee_configure_region_protection(void); + +/** + * @brief Configure APM protection for TEE + */ +void esp_tee_configure_apm_protection(void); + +/** + * @brief Switch to the REE app after TEE initialization is complete + * + * @param ree_entry_addr REE app entry address + */ +void esp_tee_switch_to_ree(uint32_t ree_entry_addr); + +/** + * @brief Secure service call entry point for the TEE binary. + * This function deciphers the call from the REE and + * dispatches the appropriate secure service API in the TEE. + * + * @param argc Number of arguments passed to the secure service API + * @param ap List of input arguments + * + * @return Return value from the secure service API + */ +int esp_tee_service_dispatcher(int argc, va_list ap); + +#endif // ifndef __ASSEMBLER__ + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/project_include.cmake b/components/esp_tee/project_include.cmake new file mode 100644 index 000000000000..b3c0e912659d --- /dev/null +++ b/components/esp_tee/project_include.cmake @@ -0,0 +1,52 @@ +idf_build_get_property(idf_path IDF_PATH) +idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(build_dir BUILD_DIR) +idf_build_get_property(sdkconfig SDKCONFIG) +idf_build_get_property(python PYTHON) +idf_build_get_property(extra_cmake_args EXTRA_CMAKE_ARGS) +idf_build_get_property(project_dir PROJECT_DIR) +idf_build_get_property(non_os_build NON_OS_BUILD) +idf_build_get_property(config_dir CONFIG_DIR) +idf_build_get_property(custom_secure_service_dir CUSTOM_SECURE_SERVICE_COMPONENT_DIR) +idf_build_get_property(custom_secure_service_component CUSTOM_SECURE_SERVICE_COMPONENT) + + +if(NOT CONFIG_SECURE_ENABLE_TEE OR non_os_build) + return() +endif() + +if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + get_filename_component(secure_boot_signing_key + "${CONFIG_SECURE_BOOT_SIGNING_KEY}" + ABSOLUTE BASE_DIR "${project_dir}") + + set(SECURE_BOOT_SIGNING_KEY ${secure_boot_signing_key}) + set(sign_key_arg "-DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key}") +else() + set(sign_key_arg) +endif() + +set(TEE_BUILD_DIR "${build_dir}/esp_tee") +set(tee_binary_files + "${TEE_BUILD_DIR}/esp_tee.elf" + "${TEE_BUILD_DIR}/esp_tee.bin" + "${TEE_BUILD_DIR}/esp_tee.map" + ) + +externalproject_add(esp_tee + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/subproject" + BINARY_DIR "${TEE_BUILD_DIR}" + CMAKE_ARGS -DSDKCONFIG=${sdkconfig} -DIDF_PATH=${idf_path} -DIDF_TARGET=${idf_target} + -DCONFIG_DIR=${config_dir} -DCUSTOM_SECURE_SERVICE_COMPONENT=${custom_secure_service_component} + -DCUSTOM_SECURE_SERVICE_COMPONENT_DIR=${custom_secure_service_dir} + ${extra_cmake_args} ${sign_key_arg} + INSTALL_COMMAND "" + BUILD_ALWAYS 1 # no easy way around this... + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + BUILD_BYPRODUCTS ${tee_binary_files} + ) + +set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + ${tee_binary_files}) diff --git a/components/esp_tee/scripts/esp32c6/secure_service.tbl b/components/esp_tee/scripts/esp32c6/secure_service.tbl new file mode 100644 index 000000000000..ee014a87892f --- /dev/null +++ b/components/esp_tee/scripts/esp32c6/secure_service.tbl @@ -0,0 +1,42 @@ +# SS no. API type Function Args +0 custom invalid_secure_service 0 +1 IDF esp_rom_route_intr_matrix 3 +2 IDF rv_utils_intr_enable 1 +3 IDF rv_utils_intr_disable 1 +4 IDF rv_utils_intr_set_priority 2 +5 IDF rv_utils_intr_set_type 2 +6 IDF rv_utils_intr_set_threshold 1 +7 IDF rv_utils_intr_edge_ack 1 +8 IDF rv_utils_intr_global_enable 0 +9 IDF efuse_hal_chip_revision 0 +10 IDF efuse_hal_get_chip_ver_pkg 1 +11 IDF efuse_hal_get_disable_wafer_version_major 0 +12 IDF efuse_hal_get_mac 1 +13 IDF esp_efuse_check_secure_version 1 +14 IDF esp_efuse_read_field_blob 3 +15 IDF esp_flash_encryption_enabled 0 +16 IDF wdt_hal_init 4 +17 IDF wdt_hal_deinit 1 +18 IDF esp_aes_intr_alloc 0 +19 IDF esp_aes_crypt_cbc 6 +20 IDF esp_aes_crypt_cfb8 6 +21 IDF esp_aes_crypt_cfb128 7 +22 IDF esp_aes_crypt_ctr 7 +23 IDF esp_aes_crypt_ecb 4 +24 IDF esp_aes_crypt_ofb 6 +25 IDF esp_sha 4 +26 IDF esp_sha_dma 6 +27 IDF esp_sha_read_digest_state 2 +28 IDF esp_sha_write_digest_state 2 +29 custom esp_tee_ota_begin 0 +30 custom esp_tee_ota_write 3 +31 custom esp_tee_ota_end 0 +32 custom esp_tee_sec_storage_init 0 +33 custom esp_tee_sec_storage_gen_key 1 +34 custom esp_tee_sec_storage_get_signature 4 +35 custom esp_tee_sec_storage_get_pubkey 2 +36 custom esp_tee_sec_storage_encrypt 8 +37 custom esp_tee_sec_storage_decrypt 8 +38 custom esp_tee_sec_storage_is_slot_empty 1 +39 custom esp_tee_sec_storage_clear_slot 1 +40 custom esp_tee_att_generate_token 6 diff --git a/components/esp_tee/scripts/secure_service_hdr.py b/components/esp_tee/scripts/secure_service_hdr.py new file mode 100644 index 000000000000..3a2bb2be4c26 --- /dev/null +++ b/components/esp_tee/scripts/secure_service_hdr.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import argparse +import re +from typing import List +from typing import Tuple + + +def parse_services(secure_service_tbl: str) -> List[Tuple[str, str, str]]: + services: List[Tuple[str, str, str]] = [] + pattern: re.Pattern = re.compile(r'^([0-9A-Fa-fXx]+)\s+\S+\s+(\S+)\s+(\d+)') + with open(secure_service_tbl, 'r') as f: + for line in f: + if match := pattern.match(line): + services.append((match.group(1), match.group(2), match.group(3))) + return sorted(services, key=lambda x: int(x[0])) + + +def generate_num_header(services: List[Tuple[str, str, str]], output_file: str) -> None: + header_text: str = '''/** + * This header file is used to generate secure service number macros. + * + * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT! + */ + +#pragma once +''' + with open(output_file, 'w') as f: + f.write(header_text) + for nr, name, _ in services: + f.write(f'#define SS_{name.upper()}\t{nr}\n') + total: int = int(services[-1][0]) + 1 if services else 0 + f.write(f'\n#define MAX_SECURE_SERVICES\t{total}\n\n') + + +def generate_dec_header(services: List[Tuple[str, str, str]], output_file: str) -> None: + header_text: str = '''/** + * This header file is used to provide function declarations + * for compiling secure_service_table.c source file. Please do not + * use it in application. + * + * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT! + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif +''' + with open(output_file, 'w') as f: + f.write(header_text) + for _, name, _ in services: + f.write(f'void _ss_{name}(void);\n') + f.write(''' +#ifdef __cplusplus +} +#endif +''') + + +def main() -> None: + parser = argparse.ArgumentParser(description='Generate secure service headers') + parser.add_argument('secure_service_tbl', type=str, help='Path to secure_service.tbl generated in build directory') + parser.add_argument('secure_service_num_h', type=str, help='Path to secure_service_num.h header file') + parser.add_argument('secure_service_dec_h', type=str, help='Path to secure_service_dec.h header file') + args = parser.parse_args() + + services: List[Tuple[str, str, str]] = parse_services(args.secure_service_tbl) + generate_num_header(services, args.secure_service_num_h) + generate_dec_header(services, args.secure_service_dec_h) + + +if __name__ == '__main__': + main() diff --git a/components/esp_tee/scripts/secure_service_tbl.py b/components/esp_tee/scripts/secure_service_tbl.py new file mode 100644 index 000000000000..f16b8d87035b --- /dev/null +++ b/components/esp_tee/scripts/secure_service_tbl.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import argparse +import re +import sys +from typing import Dict +from typing import Tuple + + +def emit(nr: int, entry: str, nargs: str) -> str: + return f'__SECURE_SERVICE({nr}, {entry}, {nargs})' + + +def main() -> None: + parser = argparse.ArgumentParser(description='Generate secure service table') + parser.add_argument('input_file', type=str, help='Path to input file') + parser.add_argument('output_file', type=str, help='Path to output file') + args = parser.parse_args() + + services: Dict[int, Tuple[str, str, str]] = {} + pattern: re.Pattern = re.compile(r'^([0-9A-Fa-fXx]+)\s+(\S+)\s+(\S+)\s+(\d+)') + + # Single pass through file to collect services and check duplicates + with open(args.input_file, 'r') as f: + for line in f: + if match := pattern.match(line): + nr = int(match.group(1)) + if nr in services: + print('ERROR: Found duplicate secure service numbers, exiting...') + sys.exit(1) + services[nr] = (match.group(2), match.group(3), match.group(4)) + + # Generate output + with open(args.output_file, 'w') as f: + f.write('''/** + * This header file is used to define secure services. + * + * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT! + */ + +#pragma once +''') + for nr in sorted(services): + _, name, nargs = services[nr] + f.write(emit(nr, name, nargs) + '\n') + + +if __name__ == '__main__': + main() diff --git a/components/esp_tee/scripts/secure_service_wrap.py b/components/esp_tee/scripts/secure_service_wrap.py new file mode 100644 index 000000000000..6deb5c9c3cda --- /dev/null +++ b/components/esp_tee/scripts/secure_service_wrap.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import argparse +import re +from typing import List + + +def main() -> None: + parser = argparse.ArgumentParser(description='Generate secure service wrap list') + parser.add_argument('secure_service_tbl', type=str, help='Path to secure service table file') + args = parser.parse_args() + + pattern: re.Pattern = re.compile(r'^[0-9A-Fa-fXx]+\s+IDF\s+(\S+)\s+\d+') + + with open(args.secure_service_tbl, 'r') as f: + wrap_list: List[str] = [f'-Wl,--wrap={match.group(1)}' + for line in f if (match := pattern.match(line))] + + print(' '.join(wrap_list), end='') + + +if __name__ == '__main__': + main() diff --git a/components/esp_tee/src/esp_secure_service_wrapper.c b/components/esp_tee/src/esp_secure_service_wrapper.c new file mode 100644 index 000000000000..c1b02fbd83d1 --- /dev/null +++ b/components/esp_tee/src/esp_secure_service_wrapper.c @@ -0,0 +1,218 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "secure_service_num.h" +#include "hal/sha_types.h" +#include "hal/sha_hal.h" +#include "hal/wdt_hal.h" +#include "esp_tee.h" +#include "esp_err.h" +#include "esp_hmac.h" +#include "esp_efuse.h" +#include "esp_random.h" +#include "soc/soc_caps.h" + +/* ---------------------------------------------- Interrupts ------------------------------------------------- */ + +IRAM_ATTR void __wrap_esp_rom_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num) +{ + esp_tee_service_call(4, SS_ESP_ROM_ROUTE_INTR_MATRIX, cpu_no, model_num, intr_num); +} + +/* ---------------------------------------------- eFuse ------------------------------------------------- */ + +uint32_t __wrap_efuse_hal_chip_revision(void) +{ + return esp_tee_service_call(1, SS_EFUSE_HAL_CHIP_REVISION); +} + +uint32_t __wrap_efuse_hal_get_chip_ver_pkg(void) +{ + return esp_tee_service_call(1, SS_EFUSE_HAL_GET_CHIP_VER_PKG); +} + +bool __wrap_efuse_hal_get_disable_wafer_version_major(void) +{ + return esp_tee_service_call(1, SS_EFUSE_HAL_GET_DISABLE_WAFER_VERSION_MAJOR); +} + +void __wrap_efuse_hal_get_mac(uint8_t *mac) +{ + esp_tee_service_call(2, SS_EFUSE_HAL_GET_MAC, mac); +} + +bool __wrap_esp_efuse_check_secure_version(uint32_t secure_version) +{ + return esp_tee_service_call(4, SS_ESP_EFUSE_CHECK_SECURE_VERSION, secure_version); +} + +esp_err_t __wrap_esp_efuse_read_field_blob(const esp_efuse_desc_t *field[], void *dst, size_t dst_size_bits) +{ + return esp_tee_service_call(4, SS_ESP_EFUSE_READ_FIELD_BLOB, (uint32_t)field, (uint32_t)dst, (uint32_t)dst_size_bits); +} + +bool __wrap_esp_flash_encryption_enabled(void) +{ + return esp_tee_service_call(1, SS_ESP_FLASH_ENCRYPTION_ENABLED); +} + +/* ---------------------------------------------- RTC_WDT ------------------------------------------------- */ + +void __wrap_wdt_hal_init(wdt_hal_context_t *hal, wdt_inst_t wdt_inst, uint32_t prescaler, bool enable_intr) +{ + esp_tee_service_call(5, SS_WDT_HAL_INIT, hal, wdt_inst, prescaler, enable_intr); +} + +void __wrap_wdt_hal_deinit(wdt_hal_context_t *hal) +{ + esp_tee_service_call(2, SS_WDT_HAL_DEINIT, hal); +} + +/* ---------------------------------------------- AES ------------------------------------------------- */ + +typedef struct { + uint8_t key_bytes; + volatile uint8_t key_in_hardware; /* This variable is used for fault injection checks, so marked volatile to avoid optimisation */ + uint8_t key[32]; +} esp_aes_context; + +int __wrap_esp_aes_intr_alloc(void) +{ + return esp_tee_service_call(1, SS_ESP_AES_INTR_ALLOC); +} + +int __wrap_esp_aes_crypt_cbc(esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_tee_service_call(7, SS_ESP_AES_CRYPT_CBC, ctx, mode, length, iv, input, output); +} + +int __wrap_esp_aes_crypt_cfb128(esp_aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_tee_service_call(8, SS_ESP_AES_CRYPT_CFB128, (uint32_t)ctx, + mode, length, iv_off, iv, (uint32_t)input, (uint32_t)output); +} + +int __wrap_esp_aes_crypt_cfb8(esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_tee_service_call(7, SS_ESP_AES_CRYPT_CFB8, ctx, + mode, length, iv, input, output); +} + +int __wrap_esp_aes_crypt_ctr(esp_aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_tee_service_call(8, SS_ESP_AES_CRYPT_CTR, ctx, length, nc_off, nonce_counter, stream_block, input, output); +} + +int __wrap_esp_aes_crypt_ecb(esp_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + return esp_tee_service_call(5, SS_ESP_AES_CRYPT_ECB, + (uint32_t)ctx, (uint32_t)mode, + (uint32_t)input, (uint32_t)output); +} + +int __wrap_esp_aes_crypt_ofb(esp_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_tee_service_call(7, SS_ESP_AES_CRYPT_OFB, (uint32_t)ctx, length, + iv_off, iv, (uint32_t)input, (uint32_t)output); +} + +/* ---------------------------------------------- SHA ------------------------------------------------- */ + +typedef enum { + ESP_SHA1_STATE_INIT, + ESP_SHA1_STATE_IN_PROCESS +} esp_sha1_state; + +typedef enum { + ESP_SHA256_STATE_INIT, + ESP_SHA256_STATE_IN_PROCESS +} esp_sha256_state; + +typedef enum { + ESP_SHA512_STATE_INIT, + ESP_SHA512_STATE_IN_PROCESS +} esp_sha512_state; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + int first_block; /*!< if first then true else false */ + esp_sha_type mode; + esp_sha1_state sha_state; +} esp_sha1_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[8]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + int first_block; /*!< if first then true, else false */ + esp_sha_type mode; + esp_sha256_state sha_state; +} esp_sha256_context; + +typedef struct { + uint64_t total[2]; /*!< number of bytes processed */ + uint64_t state[8]; /*!< intermediate digest state */ + unsigned char buffer[128]; /*!< data block being processed */ + int first_block; + esp_sha_type mode; + uint32_t t_val; /*!< t_val for 512/t mode */ + esp_sha512_state sha_state; +} esp_sha512_context; + +void __wrap_esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, unsigned char *output) +{ + esp_tee_service_call(5, SS_ESP_SHA, + (uint32_t)sha_type, (uint32_t)input, + (uint32_t)ilen, (uint32_t)output); +} + +int __wrap_esp_sha_dma(esp_sha_type sha_type, const void *input, uint32_t ilen, + const void *buf, uint32_t buf_len, bool is_first_block) +{ + return esp_tee_service_call(7, SS_ESP_SHA_DMA, sha_type, input, ilen, buf, buf_len, is_first_block); +} + +void __wrap_esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state) +{ + esp_tee_service_call(3, SS_ESP_SHA_READ_DIGEST_STATE, sha_type, digest_state); +} + +void __wrap_esp_sha_write_digest_state(esp_sha_type sha_type, void *digest_state) +{ + esp_tee_service_call(3, SS_ESP_SHA_WRITE_DIGEST_STATE, sha_type, digest_state); +} diff --git a/components/esp_tee/src/esp_tee.c b/components/esp_tee/src/esp_tee.c new file mode 100644 index 000000000000..c8fabb6749d1 --- /dev/null +++ b/components/esp_tee/src/esp_tee.c @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_attr.h" +#include "esp_private/cache_utils.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +/* See esp_tee_u2m_switch.S */ +extern uint32_t _u2m_switch(int argc, va_list ap); + +static SemaphoreHandle_t s_tee_mutex; +static StaticSemaphore_t s_tee_mutex_buf; + +static void init_mutex(void) +{ + static bool is_first_call = true; + if (is_first_call) { + s_tee_mutex = xSemaphoreCreateMutexStatic(&s_tee_mutex_buf); + is_first_call = false; + } +} + +/** + * TEE interface API used by untrusted side application + * to call secure service in trusted side + */ +uint32_t esp_tee_service_call(int argc, ...) +{ + init_mutex(); + + uint32_t val = UINT32_MAX; + va_list ap; + va_start(ap, argc); + + if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { + if (xSemaphoreTake(s_tee_mutex, portMAX_DELAY) == pdTRUE) { + val = _u2m_switch(argc, ap); + xSemaphoreGive(s_tee_mutex); + } + } else { + val = _u2m_switch(argc, ap); + } + + va_end(ap); + return val; +} + +IRAM_ATTR uint32_t esp_tee_service_call_with_noniram_intr_disabled(int argc, ...) +{ + uint32_t val = UINT32_MAX; + va_list ap; + va_start(ap, argc); + + /* NOTE: Disabling the scheduler and non-IRAM residing interrupts */ + spi_flash_op_lock(); + esp_intr_noniram_disable(); + + val = _u2m_switch(argc, ap); + + esp_intr_noniram_enable(); + spi_flash_op_unlock(); + + va_end(ap); + return val; +} diff --git a/components/esp_tee/src/esp_tee_config.c b/components/esp_tee/src/esp_tee_config.c new file mode 100644 index 000000000000..c506a0c6eef2 --- /dev/null +++ b/components/esp_tee/src/esp_tee_config.c @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_tee.h" + +/* U-mode interrupt handler */ +extern int _tee_interrupt_handler(void); +/* U-to-M mode switch */ +extern uint32_t _u2m_switch(int argc, va_list ap); +/* REE IRAM end */ +extern uint32_t _iram_end; +/* REE IROM end */ +extern uint32_t _instruction_reserved_end; +/* REE DROM end */ +extern uint32_t _rodata_reserved_end; + +esp_tee_config_t esp_tee_app_config __attribute__((section(".esp_tee_app_cfg"))) = { + .magic_word = ESP_TEE_APP_CFG_MAGIC, + .api_major_version = ESP_TEE_API_MAJOR_VER, + .api_minor_version = ESP_TEE_API_MINOR_VER, + + /* .s_entry_addr and .s_intr_handler are NULL in the + app binary, but will be written by the TEE before it loads the binary + */ + + .ns_int_handler = &_tee_interrupt_handler, + .ns_entry_addr = &_u2m_switch, + .ns_iram_end = &_iram_end, + .ns_irom_end = &_instruction_reserved_end, + .ns_drom_end = &_rodata_reserved_end, +}; diff --git a/components/esp_tee/src/esp_tee_u2m_switch.S b/components/esp_tee/src/esp_tee_u2m_switch.S new file mode 100644 index 000000000000..ae53876c3df1 --- /dev/null +++ b/components/esp_tee/src/esp_tee_u2m_switch.S @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + .section .iram1, "ax" + .balign 4 + .global _u2m_switch + .type _u2m_switch, @function +_u2m_switch: + ecall + fence + ret diff --git a/components/esp_tee/subproject/CMakeLists.txt b/components/esp_tee/subproject/CMakeLists.txt new file mode 100644 index 000000000000..a475a9ba9a78 --- /dev/null +++ b/components/esp_tee/subproject/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.16) + +set(ESP_TEE_VERSION_MAJOR 1) +set(ESP_TEE_VERSION_MINOR 0) +set(ESP_TEE_VERSION_PATCH 0) + +if(NOT SDKCONFIG) + message(FATAL_ERROR "esp_tee subproject expects the SDKCONFIG variable to be passed " + "in by the parent build process.") +endif() + +if(NOT IDF_PATH) + message(FATAL_ERROR "esp_tee subproject expects the IDF_PATH variable to be passed " + "in by the parent build process.") +endif() + +if(NOT IDF_TARGET) + message(FATAL_ERROR "esp_tee subproject expects the IDF_TARGET variable to be passed " + "in by the parent build process.") +endif() + +set(COMPONENTS esp_tee bootloader esptool_py partition_table main ${CUSTOM_SECURE_SERVICE_COMPONENT}) +list(APPEND EXTRA_COMPONENT_DIRS ${CUSTOM_SECURE_SERVICE_COMPONENT_DIR}) +set(ESP_TEE_BUILD 1) +set(NON_OS_BUILD 1) + +# TEE-specific components +list(APPEND COMPONENTS tee_flash_mgr tee_ota_ops tee_sec_storage attestation) + +# Include sdkconfig.h derived from the parent build. +include_directories(${CONFIG_DIR}) + +include("${IDF_PATH}/tools/cmake/project.cmake") +set(common_req esp_common esp_hw_support esp_rom freertos hal log newlib soc spi_flash) + +if(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND common_req riscv) +endif() + +# Included for `esp_app_desc` configuration structure +list(APPEND common_req esp_app_format) + +idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${common_req}") +idf_build_set_property(__OUTPUT_SDKCONFIG 0) +# NOTE: Helps to analyse the components built for the TEE binary by CMake Graphviz +idf_build_set_property(__BUILD_COMPONENT_DEPGRAPH_ENABLED 1) + +project(esp_tee VERSION ${ESP_TEE_VERSION_MAJOR}.${ESP_TEE_VERSION_MINOR}.${ESP_TEE_VERSION_PATCH}) + +idf_build_set_property(COMPILE_DEFINITIONS "ESP_TEE_BUILD=1" APPEND) +idf_build_set_property(COMPILE_DEFINITIONS "NON_OS_BUILD=1" APPEND) +idf_build_set_property(COMPILE_OPTIONS "-fno-stack-protector" APPEND) + +if(CONFIG_SECURE_BOOT_V2_ENABLED) + if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + get_filename_component(secure_boot_signing_key + "${SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}") + + if(NOT EXISTS "${secure_boot_signing_key}") + message(FATAL_ERROR + "Secure Boot Signing Key Not found." + "\nGenerate the Secure Boot V2 RSA-PSS 3072 Key." + "\nTo generate one, you can use this command:" + "\n\t${espsecurepy} generate_signing_key --version 2 ${SECURE_BOOT_SIGNING_KEY}") + endif() + + set(esp_tee_unsigned_bin "esp_tee-unsigned.bin") + add_custom_command(OUTPUT ".signed_bin_timestamp" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" + "${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}" + COMMAND ${ESPSECUREPY} sign_data --version 2 --keyfile "${secure_boot_signing_key}" + -o "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" "${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}" + "from ${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}" + COMMAND ${CMAKE_COMMAND} -E md5sum "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" + > "${CMAKE_BINARY_DIR}/.signed_bin_timestamp" + DEPENDS "${build_dir}/.bin_timestamp" + VERBATIM + COMMENT "Generated the signed TEE") + else() + add_custom_command(OUTPUT ".signed_bin_timestamp" + VERBATIM + COMMENT "TEE generated but not signed") + endif() + + add_custom_target(gen_signed_esp_tee ALL DEPENDS "${build_dir}/.signed_bin_timestamp") +endif() diff --git a/components/esp_tee/subproject/components/attestation/CMakeLists.txt b/components/esp_tee/subproject/components/attestation/CMakeLists.txt new file mode 100644 index 000000000000..411472021ce6 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/CMakeLists.txt @@ -0,0 +1,21 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + +set(srcs "esp_attestation.c" + "esp_att_utils_part_info.c" + "esp_att_utils_crypto.c" + "esp_att_utils_json.c") + +set(include_dirs "include") +set(priv_include_dirs "private_include") +set(priv_requires bootloader_support efuse esp_app_format esp_bootloader_format json_generator log mbedtls spi_flash) + +if(esp_tee_build) + list(APPEND priv_requires tee_sec_storage tee_flash_mgr) +else() + list(APPEND priv_requires app_update esp_partition) +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_INCLUDE_DIRS ${priv_include_dirs} + PRIV_REQUIRES ${priv_requires}) diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c new file mode 100644 index 000000000000..886a3295d258 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c @@ -0,0 +1,341 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#if ESP_TEE_BUILD +#include "bootloader_sha.h" +#include "esp_tee_sec_storage.h" +#endif + +#include "esp_random.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "esp_attestation_utils.h" + +#define ECDSA_PUBKEY_PREFIX_SZ (0x02) + +#define ECDSA_COMPRESSED_KEY_EVEN_PREFIX ("02") +#define ECDSA_COMPRESSED_KEY_ODD_PREFIX ("03") + +/* Forward declaration */ +static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair); +static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len, + uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len); + +static const char *TAG = "esp_att_utils_crypto"; + +#if ESP_TEE_BUILD + +static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair) +{ + if (keypair == NULL) { + return ESP_ERR_INVALID_ARG; + } + + memset(keypair, 0x00, sizeof(esp_att_ecdsa_keypair_t)); + + uint16_t slot_id = ESP_ATT_TK_KEY_ID; + esp_tee_sec_storage_pubkey_t pubkey = {0}; + + esp_err_t err = esp_tee_sec_storage_init(); + if (err != ESP_OK) { + return err; + } + + if (esp_tee_sec_storage_is_slot_empty(slot_id)) { + err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", err); + return err; + } + } + + err = esp_tee_sec_storage_get_pubkey(slot_id, &pubkey); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch ECDSA pubkey (%d)", err); + return err; + } + + memcpy(keypair->pub_key_x, pubkey.pub_x, sizeof(pubkey.pub_x)); + memcpy(keypair->pub_key_y, pubkey.pub_y, sizeof(pubkey.pub_y)); + + return ESP_OK; +} + +static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len, + uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len) +{ + if (sign_r == NULL || sign_s == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (sign_r_len == 0 || sign_s_len == 0) { + return ESP_ERR_INVALID_SIZE; + } + + esp_tee_sec_storage_sign_t sign = {}; + esp_err_t err = esp_tee_sec_storage_get_signature(ESP_ATT_TK_KEY_ID, (uint8_t *)digest, len, &sign); + if (err != ESP_OK) { + return err; + } + + memcpy(sign_r, sign.sign_r, sign_r_len); + memcpy(sign_s, sign.sign_s, sign_s_len); + + return ESP_OK; +} + +#else + +static int rng_func(void *rng_ctx, unsigned char *output, size_t len) +{ + esp_fill_random(output, len); + return 0; +} + +static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair) +{ + if (keypair == NULL) { + return ESP_ERR_INVALID_ARG; + } + + memset(keypair, 0x00, sizeof(esp_att_ecdsa_keypair_t)); + + int ret = -1; + esp_err_t err = ESP_FAIL; + + mbedtls_ecdsa_context ecdsa_ctx; + mbedtls_ecdsa_init(&ecdsa_ctx); + + ret = mbedtls_ecdsa_genkey(&ecdsa_ctx, MBEDTLS_ECP_DP_SECP256R1, rng_func, NULL); + if (ret != 0) { + goto exit; + } + + size_t pvt_len = mbedtls_mpi_size(&ecdsa_ctx.MBEDTLS_PRIVATE(d)); + ret = mbedtls_mpi_write_binary(&ecdsa_ctx.MBEDTLS_PRIVATE(d), (unsigned char *)keypair->pvt_key, pvt_len); + if (ret != 0) { + goto exit; + } + + size_t pubx_len = mbedtls_mpi_size(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X))); + ret = mbedtls_mpi_write_binary(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), (unsigned char *)(keypair->pub_key_x), pubx_len); + if (ret != 0) { + goto exit; + } + + size_t puby_len = mbedtls_mpi_size(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y))); + ret = mbedtls_mpi_write_binary(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)), (unsigned char *)(keypair->pub_key_y), puby_len); + if (ret != 0) { + goto exit; + } + + keypair->curve = 0; + err = ESP_OK; + +exit: + if (ret != 0) { + ESP_LOGE(TAG, "Failed to generate ECDSA keypair (-0x%X)", -ret); + } + mbedtls_ecdsa_free(&ecdsa_ctx); + return err; +} + +static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len, + uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len) +{ + if (sign_r == NULL || sign_s == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (sign_r_len == 0 || sign_s_len == 0) { + return ESP_ERR_INVALID_SIZE; + } + + esp_err_t err = ESP_FAIL; + + mbedtls_ecp_keypair pvt_key; + mbedtls_mpi r, s; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + mbedtls_ecp_keypair_init(&pvt_key); + + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &pvt_key, keypair->pvt_key, sizeof(keypair->pvt_key)); + if (ret != 0) { + goto exit; + } + + mbedtls_ecdsa_context ecdsa_ctx; + mbedtls_ecdsa_init(&ecdsa_ctx); + + ret = mbedtls_ecdsa_from_keypair(&ecdsa_ctx, &pvt_key); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_ecdsa_sign(&ecdsa_ctx.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa_ctx.MBEDTLS_PRIVATE(d), + digest, len, rng_func, NULL); + if (ret != 0) { + return ret; + } + + size_t r_len = mbedtls_mpi_size(&r); + if (r_len > sign_s_len) { + goto exit; + } + + ret = mbedtls_mpi_write_binary(&r, (unsigned char *)(sign_r), r_len); + if (ret != 0) { + goto exit; + } + + size_t s_len = mbedtls_mpi_size(&s); + if (s_len > sign_s_len) { + goto exit; + } + + ret = mbedtls_mpi_write_binary(&s, (unsigned char *)(sign_s), s_len); + if (ret != 0) { + goto exit; + } + + err = ESP_OK; + +exit: + if (ret != 0) { + ESP_LOGE(TAG, "Failed to generate ECDSA signature (-0x%X)", -ret); + } + + mbedtls_ecdsa_free(&ecdsa_ctx); + mbedtls_ecp_keypair_free(&pvt_key); + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&r); + + return err; +} + +#endif + +/* TODO: The public key generated here needs to be authorized with the relying party */ +esp_err_t esp_att_utils_ecdsa_gen_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair) +{ + return gen_ecdsa_keypair_secp256r1(keypair); +} + +esp_err_t esp_att_utils_ecdsa_get_pubkey(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_hexstr) +{ + if (keypair == NULL || pubkey_hexstr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_FAIL; + + size_t hexstr_len = sizeof(keypair->pub_key_x) * 2 + ECDSA_PUBKEY_PREFIX_SZ + 1; + char *hexstr = calloc(hexstr_len, sizeof(uint8_t)); + if (hexstr == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + /* Checking the parity of the y-component of the public key */ + char *pubkey_prefix = (keypair->pub_key_y[SECP256R1_ECDSA_KEY_LEN - 1] & 1) + ? ECDSA_COMPRESSED_KEY_ODD_PREFIX + : ECDSA_COMPRESSED_KEY_EVEN_PREFIX; + memcpy(hexstr, pubkey_prefix, ECDSA_PUBKEY_PREFIX_SZ); + + err = esp_att_utils_hexbuf_to_hexstr(keypair->pub_key_x, sizeof(keypair->pub_key_x), + &hexstr[ECDSA_PUBKEY_PREFIX_SZ], hexstr_len - ECDSA_PUBKEY_PREFIX_SZ); + if (err != ESP_OK) { + goto exit; + } + + *pubkey_hexstr = hexstr; + err = ESP_OK; + +exit: + return err; +} + +esp_err_t esp_att_utils_ecdsa_get_pubkey_digest(const esp_att_ecdsa_keypair_t *keypair, uint8_t *digest, const size_t len) +{ + if (keypair == NULL || digest == NULL || len == 0) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t pubkey_c[SECP256R1_ECDSA_KEY_LEN * 2] = {0}; + memcpy(pubkey_c, keypair->pub_key_x, SECP256R1_ECDSA_KEY_LEN); + memcpy(pubkey_c + SECP256R1_ECDSA_KEY_LEN, keypair->pub_key_y, SECP256R1_ECDSA_KEY_LEN); + + uint8_t pubkey_digest[SHA256_DIGEST_SZ]; + int ret = mbedtls_sha256((const unsigned char *)pubkey_c, sizeof(pubkey_c), pubkey_digest, false); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to calculate pubkey digest (-%X)", -ret); + return ESP_FAIL; + } + + memcpy(digest, pubkey_digest, len); + return ESP_OK; +} + +esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len, + char **sign_r_hexstr, char **sign_s_hexstr) +{ + if (keypair == NULL || digest == NULL || sign_r_hexstr == NULL || sign_s_hexstr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (len == 0) { + return ESP_ERR_INVALID_SIZE; + } + + esp_err_t err = ESP_FAIL; + + unsigned char sign_r[SECP256R1_ECDSA_KEY_LEN] = {0}, sign_s[SECP256R1_ECDSA_KEY_LEN] = {0}; + err = get_ecdsa_sign_secp256r1(keypair, digest, len, sign_r, sizeof(sign_r), sign_s, sizeof(sign_s)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate ECDSA signature"); + goto exit; + } + + size_t sign_hexstr_len = SECP256R1_ECDSA_KEY_LEN * 2 + 1; + + *sign_r_hexstr = calloc(sign_hexstr_len, sizeof(char)); + if (*sign_r_hexstr == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + err = esp_att_utils_hexbuf_to_hexstr(sign_r, sizeof(sign_r), *sign_r_hexstr, sign_hexstr_len); + if (err != ESP_OK) { + goto exit; + } + + *sign_s_hexstr = calloc(sign_hexstr_len, sizeof(char)); + if (*sign_s_hexstr == NULL) { + free(*sign_r_hexstr); + *sign_r_hexstr = NULL; + err = ESP_ERR_NO_MEM; + goto exit; + } + + err = esp_att_utils_hexbuf_to_hexstr(sign_s, sizeof(sign_s), *sign_s_hexstr, sign_hexstr_len); + if (err != ESP_OK) { + goto exit; + } + + err = ESP_OK; + +exit: + return err; +} diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c new file mode 100644 index 000000000000..79bce76c7e9f --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c @@ -0,0 +1,288 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#if ESP_TEE_BUILD +#include "bootloader_sha.h" +#include "esp_tee_sec_storage.h" +#endif + +#include "esp_random.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "json_generator.h" +#include "esp_attestation_utils.h" + +#define DIGEST_HEXSTR_LEN (MAX_DIGEST_SZ * 2 + 1) + +static const char *TAG = "esp_att_utils_json"; + +static const char *str_from_fw_type(esp_att_part_type_t fw, size_t *length) +{ + if (fw >= ESP_ATT_PART_TYPE_MAX) { + if (length) { + *length = SIZE_MAX; + } + return NULL; + } + static const char *fw_type_str[] = {"bootloader", "tee", "app", "other"}; + if (length) { + *length = strlen(fw_type_str[fw]); + } + return fw_type_str[fw]; +} + +static esp_err_t part_metadata_to_json(const esp_att_part_metadata_t *metadata, char **claim_json) +{ + if (metadata == NULL || claim_json == NULL) { + return ESP_ERR_INVALID_ARG; + } + + char *json_buf = calloc(ESP_ATT_CLAIM_JSON_MAX_SZ, sizeof(char)); + if (json_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + json_gen_str_t json_gen; + + // Initialize the JSON string generator + json_gen_str_start(&json_gen, json_buf, ESP_ATT_CLAIM_JSON_MAX_SZ, NULL, NULL); + + // Start the top-level JSON object + json_gen_start_object(&json_gen); + + // Add the properties within the "app" object + json_gen_obj_set_int(&json_gen, "type", metadata->type); + json_gen_obj_set_string(&json_gen, "ver", (char *)metadata->ver); + json_gen_obj_set_string(&json_gen, "idf_ver", (char *)metadata->idf_ver); + json_gen_obj_set_int(&json_gen, "secure_ver", metadata->secure_ver); + + // Add "part_chip_rev" object + json_gen_push_object(&json_gen, "part_chip_rev"); + json_gen_obj_set_int(&json_gen, "min", metadata->part_chip_rev.min_chip_rev); + json_gen_obj_set_int(&json_gen, "max", metadata->part_chip_rev.max_chip_rev); + json_gen_pop_object(&json_gen); + + // Add "part_digest" object + json_gen_push_object(&json_gen, "part_digest"); + json_gen_obj_set_int(&json_gen, "type", metadata->part_digest.type); + + char calc_digest_hexstr[DIGEST_HEXSTR_LEN]; + esp_err_t err = esp_att_utils_hexbuf_to_hexstr(metadata->part_digest.calc_digest, sizeof(metadata->part_digest.calc_digest), + calc_digest_hexstr, sizeof(calc_digest_hexstr)); + if (err != ESP_OK) { + return err; + } + json_gen_obj_set_string(&json_gen, "calc_digest", calc_digest_hexstr); + + json_gen_obj_set_bool(&json_gen, "digest_validated", metadata->part_digest.digest_validated); + json_gen_obj_set_bool(&json_gen, "sign_verified", metadata->part_digest.sign_verified); + if (metadata->type == ESP_ATT_PART_TYPE_TEE || metadata->type == ESP_ATT_PART_TYPE_APP) { + json_gen_obj_set_bool(&json_gen, "secure_padding", metadata->part_digest.secure_padding); + } + json_gen_pop_object(&json_gen); + + // End the top-level JSON object + json_gen_end_object(&json_gen); + + // Finalize the JSON string generation + json_gen_str_end(&json_gen); + + *claim_json = json_buf; + return ESP_OK; +} + +esp_err_t esp_att_utils_header_to_json(const esp_att_token_hdr_t *tk_hdr, char **header_json, int *len) +{ + /* NOTE: Token header is not yet configurable, thus will be left empty for now */ + if (tk_hdr == NULL || header_json == NULL || len == NULL) { + return ESP_ERR_INVALID_ARG; + } + + char *json_buf = calloc(ESP_ATT_HDR_JSON_MAX_SZ, sizeof(char)); + if (json_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + json_gen_str_t json_gen; + + // Initialize the JSON string generator + json_gen_str_start(&json_gen, json_buf, ESP_ATT_HDR_JSON_MAX_SZ, NULL, NULL); + + // Start the top-level JSON object + json_gen_start_object(&json_gen); + + json_gen_obj_set_string(&json_gen, "magic", ESP_ATT_TK_MAGIC_STR); + json_gen_obj_set_string(&json_gen, "encr_alg", NULL); + json_gen_obj_set_string(&json_gen, "sign_alg", ESP_ATT_TK_SIGN_ALG); + + json_gen_obj_set_int(&json_gen, "key_id", ESP_ATT_TK_KEY_ID); + + // End the top-level JSON object + json_gen_end_object(&json_gen); + + // Finalize the JSON string generation + *len = json_gen_str_end(&json_gen); + + *header_json = json_buf; + return ESP_OK; +} + +esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, const esp_att_token_cfg_t *cfg, char **eat_json, int *len) +{ + if (head == NULL || eat_json == NULL || len == NULL || cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + + char *json_buf = calloc(ESP_ATT_EAT_JSON_MAX_SZ, sizeof(char)); + if (json_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + json_gen_str_t json_gen; + + // Initialize the JSON string generator + json_gen_str_start(&json_gen, json_buf, ESP_ATT_EAT_JSON_MAX_SZ, NULL, NULL); + + // Start the top-level JSON object + json_gen_start_object(&json_gen); + esp_att_sw_claim_list_t *claim = NULL; + char *claim_json = NULL; + + json_gen_obj_set_int(&json_gen, "nonce", cfg->nonce); + json_gen_obj_set_int(&json_gen, "client_id", cfg->client_id); + json_gen_obj_set_int(&json_gen, "device_ver", cfg->device_ver); + + char dev_id_hexstr[ESP_ATT_EAT_DEV_ID_SZ * 2 + 1] = {0}; + esp_err_t err = esp_att_utils_hexbuf_to_hexstr(cfg->device_id, sizeof(cfg->device_id), dev_id_hexstr, sizeof(dev_id_hexstr)); + if (err != ESP_OK) { + return err; + } + json_gen_obj_set_string(&json_gen, "device_id", dev_id_hexstr); + + /* NOTE: Instance ID is the SHA256 of the ECDSA public key in usage */ + char inst_id_hexstr[DIGEST_HEXSTR_LEN] = {0}; + err = esp_att_utils_hexbuf_to_hexstr(cfg->instance_id, sizeof(cfg->instance_id), inst_id_hexstr, sizeof(inst_id_hexstr)); + if (err != ESP_OK) { + return err; + } + json_gen_obj_set_string(&json_gen, "instance_id", inst_id_hexstr); + + json_gen_obj_set_string(&json_gen, "psa_cert_ref", cfg->psa_cert_ref); + json_gen_obj_set_int(&json_gen, "device_status", cfg->device_stat); + + json_gen_push_object(&json_gen, "sw_claims"); + SLIST_FOREACH(claim, head, next) { + esp_err_t err = part_metadata_to_json(&claim->metadata, &claim_json); + if (err != ESP_OK || claim_json == NULL) { + ESP_LOGE(TAG, "Failed to format the FW metadata to JSON!"); + return err; + } + + json_gen_push_object_str(&json_gen, (char *)str_from_fw_type(claim->metadata.type, NULL), claim_json); + free(claim_json); + } + json_gen_pop_object(&json_gen); + + // End the top-level JSON object + json_gen_end_object(&json_gen); + + // Finalize the JSON string generation + *len = json_gen_str_end(&json_gen); + *eat_json = json_buf; + + return ESP_OK; +} + +esp_err_t esp_att_utils_pubkey_to_json(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_json, int *len) +{ + if (keypair == NULL || pubkey_json == NULL || len == NULL) { + return ESP_ERR_INVALID_ARG; + } + + char *json_buf = calloc(ESP_ATT_PUBKEY_JSON_MAX_SZ, sizeof(char)); + if (json_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + json_gen_str_t json_gen; + + // Initialize the JSON string generator + json_gen_str_start(&json_gen, json_buf, ESP_ATT_PUBKEY_JSON_MAX_SZ, NULL, NULL); + + // Start the top-level JSON object + json_gen_start_object(&json_gen); + + char *pubkey_hexstr = NULL; + esp_err_t err = esp_att_utils_ecdsa_get_pubkey(keypair, &pubkey_hexstr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get public key!"); + return err; + } + + json_gen_obj_set_string(&json_gen, "compressed", pubkey_hexstr); + + // End the top-level JSON object + json_gen_end_object(&json_gen); + + // Finalize the JSON string generation + *len = json_gen_str_end(&json_gen); + *pubkey_json = json_buf; + + free(pubkey_hexstr); + + return ESP_OK; +} + +esp_err_t esp_att_utils_sign_to_json(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t digest_len, + char **sign_json, int *len) +{ + if (keypair == NULL || digest == NULL || sign_json == NULL || len == NULL) { + return ESP_ERR_INVALID_ARG; + } + + char *json_buf = calloc(ESP_ATT_SIGN_JSON_MAX_SZ, sizeof(char)); + if (json_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + json_gen_str_t json_gen; + + // Initialize the JSON string generator + json_gen_str_start(&json_gen, json_buf, ESP_ATT_SIGN_JSON_MAX_SZ, NULL, NULL); + + // Start the top-level JSON object + json_gen_start_object(&json_gen); + + char *sign_r_hexstr = NULL, *sign_s_hexstr = NULL; + esp_err_t err = esp_att_utils_ecdsa_get_sign(keypair, digest, digest_len, &sign_r_hexstr, &sign_s_hexstr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to sign token!"); + return err; + } + + json_gen_obj_set_string(&json_gen, "r", sign_r_hexstr); + json_gen_obj_set_string(&json_gen, "s", sign_s_hexstr); + + // End the top-level JSON object + json_gen_end_object(&json_gen); + + // Finalize the JSON string generation + *len = json_gen_str_end(&json_gen); + *sign_json = json_buf; + + free(sign_r_hexstr); + free(sign_s_hexstr); + + return ESP_OK; +} diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c new file mode 100644 index 000000000000..5fb346b108a5 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c @@ -0,0 +1,545 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +#include "esp_log.h" +#include "esp_err.h" +#include "hal/efuse_hal.h" + +#include "esp_app_format.h" +#include "esp_bootloader_desc.h" +#include "esp_image_format.h" +#include "esp_app_desc.h" + +#if ESP_TEE_BUILD +#include "esp_flash_partitions.h" +#include "bootloader_utility_tee.h" +#include "esp_tee_flash.h" +#else +#include "esp_partition.h" +#include "esp_ota_ops.h" +#endif + +#if CONFIG_SECURE_BOOT_V2_ENABLED +#include "esp_secure_boot.h" +#include "bootloader_utility.h" +#if CONFIG_IDF_TARGET_ESP32C6 +#include "esp32c6/rom/secure_boot.h" +#endif +#endif + +#include "mbedtls/sha256.h" + +#include "bootloader_flash_priv.h" +#include "esp_attestation_utils.h" + +#define SECURE_BOOT_V2 (0x02) +#define ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1)) + +static const char *TAG = "esp_att_utils"; + +/* Forward declaration */ +static esp_err_t read_partition(uint32_t offset, void *buf, size_t size); +esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest); +static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos); +static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos); + +/* ---------------------------------------------- Helper APIs ------------------------------------------------- */ + +static size_t digest_type_to_len(esp_att_part_digest_type_t digest) +{ + size_t digest_len = 0; + + switch (digest) { + case ESP_ATT_DIGEST_TYPE_SHA256: + digest_len = SHA256_DIGEST_SZ; + break; + default: + break; + } + + return digest_len; +} + +#if ESP_TEE_BUILD + +static esp_err_t read_partition(uint32_t offset, void *buf, size_t size) +{ + return (esp_err_t)esp_tee_flash_read(offset, buf, size, true); +} + +esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest) +{ + if (digest == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t mmu_free_pages_count = esp_tee_flash_mmap_get_free_pages(); + uint32_t partial_image_len = mmu_free_pages_count * CONFIG_MMU_PAGE_SIZE; + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + int ret = mbedtls_sha256_starts(&ctx, false); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + + while (len > 0) { + uint32_t mmap_len = MIN(len, partial_image_len); + const void *image = esp_tee_flash_mmap(flash_offset, mmap_len); + if (image == NULL) { + mbedtls_sha256_finish(&ctx, NULL); + return ESP_FAIL; + } + mbedtls_sha256_update(&ctx, image, mmap_len); + esp_tee_flash_munmap(image); + + flash_offset += mmap_len; + len -= mmap_len; + } + + mbedtls_sha256_finish(&ctx, digest); + mbedtls_sha256_free(&ctx); + return ESP_OK; +} + +static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos) +{ + if (pos == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_partition_info_t *running_app = esp_tee_flash_get_running_ree_partition(); + if (running_app->magic != ESP_PARTITION_MAGIC) { + return ESP_ERR_NOT_FOUND; + } + + memcpy(pos, &running_app->pos, sizeof(esp_partition_pos_t)); + return ESP_OK; +} + +static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos) +{ + uint8_t tee_active_part = bootloader_utility_tee_get_boot_partition(NULL); + if (tee_active_part > PART_SUBTYPE_TEE_1) { + return ESP_ERR_NOT_FOUND; + } + + esp_partition_info_t part_info = {}; + esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_APP, tee_active_part, NULL, &part_info); + if (err != ESP_OK) { + return err; + } + + memcpy(pos, &part_info.pos, sizeof(esp_partition_pos_t)); + return ESP_OK; +} + +#else + +static esp_err_t read_partition(uint32_t offset, void *buf, size_t size) +{ + return esp_flash_read(NULL, buf, offset, size); +} + +esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest) +{ + if (digest == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_FAIL; + + uint32_t mmu_free_pages_count = bootloader_mmap_get_free_pages(); + uint32_t partial_image_len = mmu_free_pages_count * CONFIG_MMU_PAGE_SIZE; + + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + + if (mbedtls_sha256_starts(&sha256_ctx, false) != 0) { + goto exit; + } + + while (len > 0) { + uint32_t mmap_len = MIN(len, partial_image_len); + const void *image = bootloader_mmap(flash_offset, mmap_len); + if (image == NULL) { + goto exit; + } + if (mbedtls_sha256_update(&sha256_ctx, image, mmap_len) != 0) { + goto exit; + } + bootloader_munmap(image); + + flash_offset += mmap_len; + len -= mmap_len; + } + + if (mbedtls_sha256_finish(&sha256_ctx, digest) != 0) { + goto exit; + } + + err = ESP_OK; +exit: + mbedtls_sha256_free(&sha256_ctx); + return err; +} + +static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos) +{ + if (pos == NULL) { + return ESP_ERR_INVALID_ARG; + } + + const esp_partition_t *running_part = esp_ota_get_running_partition(); + if (running_part == NULL) { + return ESP_ERR_NOT_FOUND; + } + + pos->offset = running_part->address; + pos->size = running_part->size; + + return ESP_OK; +} + +static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +#endif + +static esp_err_t get_active_part_pos(esp_att_part_type_t part_type, esp_partition_pos_t *active_pos) +{ + if (active_pos == NULL || part_type > ESP_ATT_PART_TYPE_APP) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err; + esp_partition_pos_t pos = {}; + + switch (part_type) { + case ESP_ATT_PART_TYPE_BOOTLOADER: + pos.offset = CONFIG_BOOTLOADER_OFFSET_IN_FLASH; + pos.size = CONFIG_PARTITION_TABLE_OFFSET - CONFIG_BOOTLOADER_OFFSET_IN_FLASH; + break; + case ESP_ATT_PART_TYPE_APP: + err = get_active_app_part_pos(&pos); + if (err != ESP_OK) { + return err; + } + break; + case ESP_ATT_PART_TYPE_TEE: + err = get_active_tee_part_pos(&pos); + if (err != ESP_OK) { + return err; + } + break; + default: + ESP_LOGE(TAG, "Unsupported partition type!"); + return ESP_ERR_NOT_FOUND; + } + + memcpy(active_pos, &pos, sizeof(esp_partition_pos_t)); + return ESP_OK; +} + +static esp_err_t get_part_digest(const esp_partition_pos_t *pos, esp_att_part_digest_info_t *part_digest) +{ + if (pos == NULL || part_digest == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_image_metadata_t metadata = {}; + esp_err_t err = esp_image_get_metadata(pos, &metadata); + if (err != ESP_OK) { + return err; + } + + memset(part_digest, 0x00, sizeof(esp_att_part_digest_info_t)); + + part_digest->type = ESP_ATT_DIGEST_TYPE_SHA256; + size_t digest_len = digest_type_to_len(part_digest->type); + size_t image_len = metadata.image_len - digest_len; + + uint8_t *digest = calloc(digest_len, sizeof(uint8_t)); + if (digest == NULL) { + return ESP_ERR_NO_MEM; + } + + err = get_flash_contents_sha256(pos->offset, image_len, digest); + if (err != ESP_OK) { + goto exit; + } + + if (!memcmp(digest, &metadata.image_digest, digest_len)) { + part_digest->digest_validated = true; + } + + memset(part_digest->calc_digest, 0x00, sizeof(part_digest->calc_digest)); + memcpy(part_digest->calc_digest, digest, digest_len); + +#if CONFIG_SECURE_BOOT_V2_ENABLED + uint32_t signed_image_len = ALIGN_UP(metadata.image_len, FLASH_SECTOR_SIZE); + + if (signed_image_len % CONFIG_MMU_PAGE_SIZE == 0) { + part_digest->secure_padding = true; + } else { + part_digest->secure_padding = false; + } + + err = esp_secure_boot_verify_signature(metadata.start_addr, signed_image_len); + if (err == ESP_OK) { + part_digest->sign_verified = true; + } +#endif + + err = ESP_OK; + +exit: + free(digest); + return err; +} + +static esp_err_t get_part_btl_desc(const esp_partition_pos_t *pos, esp_bootloader_desc_t *btl_desc) +{ + if (pos == NULL || btl_desc == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t btl_desc_offset = pos->offset + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t); + esp_err_t err = read_partition(btl_desc_offset, btl_desc, sizeof(esp_bootloader_desc_t)); + if (err != ESP_OK) { + return err; + } + + if (btl_desc->magic_byte != ESP_BOOTLOADER_DESC_MAGIC_BYTE) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +static esp_err_t get_part_app_desc(const esp_partition_pos_t *pos, esp_app_desc_t *app_desc) +{ + if (pos == NULL || app_desc == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t app_desc_offset = pos->offset + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t); + esp_err_t err = read_partition(app_desc_offset, app_desc, sizeof(esp_app_desc_t)); + if (err != ESP_OK) { + return err; + } + + if (app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +static esp_err_t get_part_chip_rev(const esp_partition_pos_t *pos, esp_att_part_chip_rev_t *chip_rev) +{ + if (pos == NULL || chip_rev == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_image_header_t img_hdr = {}; + esp_err_t err = read_partition(pos->offset, &img_hdr, sizeof(esp_image_header_t)); + if (err != ESP_OK) { + return err; + } + + if (img_hdr.magic != ESP_IMAGE_HEADER_MAGIC) { + return ESP_ERR_NOT_FOUND; + } + + chip_rev->min_chip_rev = img_hdr.min_chip_rev_full; + chip_rev->max_chip_rev = img_hdr.max_chip_rev_full; + + return ESP_OK; +} + +esp_err_t esp_att_utils_hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len) +{ + if (hexbuf == NULL || hexstr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (hexbuf_sz == 0 || hexstr_len < (hexbuf_sz * 2 + 1)) { + return ESP_ERR_INVALID_SIZE; + } + + const uint8_t *bytes = (const uint8_t *)hexbuf; + + for (size_t i = 0; i < hexbuf_sz; i++) { + for (int shift = 0; shift < 2; shift++) { + uint8_t nibble = (bytes[i] >> (shift ? 0 : 4)) & 0x0F; + if (nibble < 10) { + hexstr[i * 2 + shift] = '0' + nibble; + } else { + hexstr[i * 2 + shift] = 'a' + nibble - 10; + } + } + } + hexstr[hexbuf_sz * 2] = '\0'; + + return ESP_OK; +} + +esp_err_t esp_att_utils_get_btl_claim_data(esp_att_part_metadata_t *btl_metadata) +{ + esp_partition_pos_t btl_pos = {}; + esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_BOOTLOADER, &btl_pos); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get running app partition!"); + return err; + } + + esp_att_part_digest_info_t btl_digest = {}; + err = get_part_digest(&btl_pos, &btl_digest); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get bootloader image digest!"); + return err; + } + + esp_bootloader_desc_t btl_desc = {}; + err = get_part_btl_desc(&btl_pos, &btl_desc); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get bootloader partition description!"); + return err; + } + + esp_att_part_chip_rev_t btl_chip_rev = {}; + err = get_part_chip_rev(&btl_pos, &btl_chip_rev); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get bootloader image chip rev!"); + return err; + } + + /* Clearing and populating the bootloader FW metadata */ + memset(btl_metadata, 0x00, sizeof(esp_att_part_metadata_t)); + + btl_metadata->type = ESP_ATT_PART_TYPE_BOOTLOADER; + btl_metadata->secure_ver = btl_desc.secure_version; + + err = esp_att_utils_hexbuf_to_hexstr(&btl_desc.version, sizeof(btl_desc.version), btl_metadata->ver, sizeof(btl_metadata->ver)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch bootloader version!"); + return err; + } + + memcpy(btl_metadata->idf_ver, &btl_desc.idf_ver, sizeof(btl_metadata->idf_ver)); + memcpy(&btl_metadata->part_digest, &btl_digest, sizeof(esp_att_part_digest_info_t)); + memcpy(&btl_metadata->part_chip_rev, &btl_chip_rev, sizeof(esp_att_part_chip_rev_t)); + + return ESP_OK; +} + +esp_err_t esp_att_utils_get_app_claim_data(esp_att_part_metadata_t *app_metadata) +{ + esp_partition_pos_t app_pos = {}; + esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_APP, &app_pos); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get running app partition!"); + return err; + } + + esp_att_part_digest_info_t app_digest = {}; + err = get_part_digest(&app_pos, &app_digest); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get app image digest!"); + return err; + } + + esp_app_desc_t ns_app_desc = {}; + err = get_part_app_desc(&app_pos, &ns_app_desc); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get app partition description!"); + return err; + } + + esp_att_part_chip_rev_t app_chip_rev = {}; + err = get_part_chip_rev(&app_pos, &app_chip_rev); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get app image chip rev!"); + return err; + } + + /* Clearing and populating the bootloader FW metadata */ + memset(app_metadata, 0x00, sizeof(esp_att_part_metadata_t)); + + app_metadata->type = ESP_ATT_PART_TYPE_APP; + app_metadata->secure_ver = ns_app_desc.secure_version; + + memcpy(app_metadata->ver, &ns_app_desc.version, sizeof(app_metadata->ver)); + memcpy(app_metadata->idf_ver, &ns_app_desc.idf_ver, sizeof(app_metadata->idf_ver)); + + memcpy(&app_metadata->part_digest, &app_digest, sizeof(esp_att_part_digest_info_t)); + memcpy(&app_metadata->part_chip_rev, &app_chip_rev, sizeof(esp_att_part_chip_rev_t)); + + return ESP_OK; +} + +esp_err_t esp_att_utils_get_tee_claim_data(esp_att_part_metadata_t *tee_metadata) +{ +#if ESP_TEE_BUILD + esp_partition_pos_t tee_pos = {}; + esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_TEE, &tee_pos); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get running tee partition!"); + return err; + } + + esp_att_part_digest_info_t tee_digest = {}; + err = get_part_digest(&tee_pos, &tee_digest); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get TEE image digest!"); + return err; + } + + esp_app_desc_t tee_app_desc = {}; + err = get_part_app_desc(&tee_pos, &tee_app_desc); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get TEE partition description!"); + return err; + } + + esp_att_part_chip_rev_t tee_chip_rev = {}; + err = get_part_chip_rev(&tee_pos, &tee_chip_rev); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get TEE image chip rev!"); + return err; + } + + /* Clearing and populating the TEE FW metadata */ + memset(tee_metadata, 0x00, sizeof(esp_att_part_metadata_t)); + + tee_metadata->type = ESP_ATT_PART_TYPE_TEE; + tee_metadata->secure_ver = tee_app_desc.secure_version; + + memcpy(tee_metadata->ver, &tee_app_desc.version, sizeof(tee_metadata->ver)); + memcpy(tee_metadata->idf_ver, &tee_app_desc.idf_ver, sizeof(tee_metadata->idf_ver)); + + memcpy(&tee_metadata->part_digest, &tee_digest, sizeof(esp_att_part_digest_info_t)); + memcpy(&tee_metadata->part_chip_rev, &tee_chip_rev, sizeof(esp_att_part_chip_rev_t)); + + return ESP_OK; +#else + ESP_LOGE(TAG, "TEE not supported!"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} diff --git a/components/esp_tee/subproject/components/attestation/esp_attestation.c b/components/esp_tee/subproject/components/attestation/esp_attestation.c new file mode 100644 index 000000000000..2fa56295f12d --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_attestation.c @@ -0,0 +1,304 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#include "hal/efuse_hal.h" + +#include "mbedtls/sha256.h" + +#include "esp_attestation.h" +#include "esp_attestation_utils.h" + +#include "json_generator.h" + +#include "sdkconfig.h" + +static const char *TAG = "esp_attestation"; + +/* ---------------------------------------------- Interface APIs ------------------------------------------------- */ + +static struct esp_att_sw_claim_list sw_claim_data = SLIST_HEAD_INITIALIZER(sw_claim_data); + +static esp_att_sw_claim_list_t *create_sw_claim_entry(esp_att_part_metadata_t *metadata) +{ + esp_att_sw_claim_list_t *sw_claim_entry = calloc(1, sizeof(esp_att_sw_claim_list_t)); + if (sw_claim_entry == NULL) { + ESP_LOGE(TAG, "Failed to allocate claim data!"); + return NULL; + } + memcpy(&sw_claim_entry->metadata, metadata, sizeof(esp_att_part_metadata_t)); + return sw_claim_entry; +} + +static void free_sw_claim_list(void) +{ + while (!SLIST_EMPTY(&sw_claim_data)) { + esp_att_sw_claim_list_t *claim = SLIST_FIRST(&sw_claim_data); + SLIST_REMOVE_HEAD(&sw_claim_data, next); + free(claim); + } +} + +static esp_err_t fetch_device_id(uint8_t *devid_buf) +{ + if (devid_buf == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t mac_addr[6] = {0}; + esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_MAC, mac_addr, sizeof(mac_addr) * 8); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to read MAC from eFuse!"); + goto exit; + } + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + int ret = mbedtls_sha256_starts(&ctx, false); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_sha256_update(&ctx, (const unsigned char *)mac_addr, sizeof(mac_addr)); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + err = ESP_FAIL; + goto exit; + } + + uint8_t digest[SHA256_DIGEST_SZ] = {0}; + ret = mbedtls_sha256_finish(&ctx, digest); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + err = ESP_FAIL; + goto exit; + } + + memcpy(devid_buf, digest, SHA256_DIGEST_SZ); + mbedtls_sha256_free(&ctx); + +exit: + return err; +} + +static esp_err_t populate_att_token_cfg(esp_att_token_cfg_t *cfg, const esp_att_ecdsa_keypair_t *keypair) +{ + if (cfg == NULL || keypair == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = fetch_device_id(cfg->device_id); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get the device ID!"); + return err; + } + + err = esp_att_utils_ecdsa_get_pubkey_digest(keypair, cfg->instance_id, sizeof(cfg->instance_id)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get ECDSA public key hash!"); + return err; + } + + cfg->device_ver = efuse_hal_chip_revision(); + /* TODO: Decide what all fields we need here */ + cfg->device_stat = 0xA5; + + return ESP_OK; +} + +static esp_err_t populate_sw_claim_data(void) +{ + esp_att_part_metadata_t metadata = {}; + esp_att_sw_claim_list_t *claim = NULL; + + esp_err_t err = esp_att_utils_get_btl_claim_data(&metadata); + if (err != ESP_OK) { + return err; + } + + SLIST_INIT(&sw_claim_data); + + claim = create_sw_claim_entry(&metadata); + if (claim == NULL) { + return ESP_ERR_NO_MEM; + } + SLIST_INSERT_HEAD(&sw_claim_data, claim, next); + + err = esp_att_utils_get_app_claim_data(&metadata); + if (err != ESP_OK) { + goto exit; + } + + claim = create_sw_claim_entry(&metadata); + if (claim == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + SLIST_INSERT_HEAD(&sw_claim_data, claim, next); + + err = esp_att_utils_get_tee_claim_data(&metadata); + if (err != ESP_OK) { + if (err == ESP_ERR_NOT_SUPPORTED) { + return ESP_OK; + } + return err; + } + + claim = create_sw_claim_entry(&metadata); + if (claim == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + SLIST_INSERT_HEAD(&sw_claim_data, claim, next); + + return ESP_OK; + +exit: + free_sw_claim_list(); + return err; +} + +esp_err_t esp_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref, + uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len) +{ + if (token_buf == NULL || token_len == NULL || psa_cert_ref == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (token_buf_size < ESP_ATT_TK_MIN_SIZE) { + ESP_LOGE(TAG, "EAT buffer too small: got %luB, need > %dB", token_buf_size, ESP_ATT_TK_MIN_SIZE); + return ESP_ERR_INVALID_SIZE; + } + + esp_err_t err = populate_sw_claim_data(); + if (err != ESP_OK || SLIST_EMPTY(&sw_claim_data)) { + ESP_LOGE(TAG, "Failed to fetch S/W claim data!"); + return err; + } + + esp_att_ecdsa_keypair_t keypair = {}; + err = esp_att_utils_ecdsa_gen_keypair_secp256r1(&keypair); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate ECDSA key-pair!"); + goto exit; + } + + esp_att_token_cfg_t cfg = { + .nonce = nonce, + .client_id = client_id, + }; + memcpy(cfg.psa_cert_ref, psa_cert_ref, sizeof(cfg.psa_cert_ref)); + + err = populate_att_token_cfg(&cfg, &keypair); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to populate token config data!"); + goto exit; + } + + memset(token_buf, 0x00, token_buf_size); + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + int ret = mbedtls_sha256_starts(&ctx, false); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + + json_gen_str_t jstr; + json_gen_str_start(&jstr, (char *)token_buf, token_buf_size, NULL, NULL); + json_gen_start_object(&jstr); + + /* Pushing the Header object */ + const esp_att_token_hdr_t tk_hdr = {}; + char *hdr_json = NULL; + int hdr_len = -1; + /* NOTE: Token header is not yet configurable */ + err = esp_att_utils_header_to_json(&tk_hdr, &hdr_json, &hdr_len); + if (err != ESP_OK || hdr_json == NULL || hdr_len <= 0) { + ESP_LOGE(TAG, "Failed to format the token header as JSON!"); + return err; + } + json_gen_push_object_str(&jstr, "header", hdr_json); + + ret = mbedtls_sha256_update(&ctx, (const unsigned char *)hdr_json, hdr_len - 1); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + free(hdr_json); + + /* Pushing the EAT object */ + char *eat_json = NULL; + int eat_len = -1; + err = esp_att_utils_eat_data_to_json(&sw_claim_data, &cfg, &eat_json, &eat_len); + if (err != ESP_OK || eat_json == NULL || eat_len <= 0) { + ESP_LOGE(TAG, "Failed to format the EAT data to JSON!"); + return err; + } + json_gen_push_object_str(&jstr, "eat", eat_json); + + ret = mbedtls_sha256_update(&ctx, (const unsigned char *)eat_json, eat_len - 1); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + free(eat_json); + + char *pubkey_json = NULL; + int pubkey_len = -1; + err = esp_att_utils_pubkey_to_json(&keypair, &pubkey_json, &pubkey_len); + if (err != ESP_OK || pubkey_json == NULL || pubkey_len <= 0) { + ESP_LOGE(TAG, "Failed to format the public key data to JSON!"); + return err; + } + json_gen_push_object_str(&jstr, "public_key", pubkey_json); + + ret = mbedtls_sha256_update(&ctx, (const unsigned char *)pubkey_json, pubkey_len - 1); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + free(pubkey_json); + + uint8_t digest[SHA256_DIGEST_SZ] = {0}; + ret = mbedtls_sha256_finish(&ctx, digest); + if (ret != 0) { + mbedtls_sha256_free(&ctx); + return ESP_FAIL; + } + mbedtls_sha256_free(&ctx); + + char *sign_json = NULL; + int sign_len = -1; + err = esp_att_utils_sign_to_json(&keypair, digest, sizeof(digest), &sign_json, &sign_len); + if (err != ESP_OK || sign_json == NULL || sign_len <= 0) { + ESP_LOGE(TAG, "Failed to format the token signature to JSON!"); + return err; + } + json_gen_push_object_str(&jstr, "sign", sign_json); + free(sign_json); + + json_gen_end_object(&jstr); + *token_len = json_gen_str_end(&jstr); + err = ESP_OK; + +exit: + free_sw_claim_list(); + return err; +} diff --git a/components/esp_tee/subproject/components/attestation/include/esp_attestation.h b/components/esp_tee/subproject/components/attestation/include/esp_attestation.h new file mode 100644 index 000000000000..d83725d0f58f --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/include/esp_attestation.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate an entity attestation token + * + * @param[in] nonce Nonce value to include in the token + * @param[in] client_id Client identifier to include in the token + * @param[in] psa_cert_ref PSA certificate reference to include in the token + * @param[in] token_buf Buffer to store the generated token + * @param[in] token_buf_size Size of the token buffer + * @param[out] token_len Pointer to store the actual length of the generated token + * + * @return esp_err_t ESP_OK on success, or an error code on failure + */ +esp_err_t esp_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref, + uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h b/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h new file mode 100644 index 000000000000..a5d2b2dac6da --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h @@ -0,0 +1,278 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" + +#include "esp_flash_partitions.h" +#include "esp_app_desc.h" + +#include "esp_attestation.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ATT_TK_MAGIC 0x44FEF7CC +#define ESP_ATT_TK_MAGIC_STR "44fef7cc" +#define ESP_ATT_TK_SIGN_ALG "ecdsa_secp256r1_sha256" + +#define SECP256R1_ECDSA_KEY_LEN (32) +#define SECP256R1_ECDSA_SIG_LEN (64) + +#define MAX_ECDSA_KEY_LEN (32) +#define MAX_ECDSA_SIG_LEN (64) + +#define SHA256_DIGEST_SZ (32) +#define MAX_DIGEST_SZ (32) + +#define ESP_ATT_HDR_JSON_MAX_SZ (128) + +#define ESP_ATT_EAT_DEV_ID_SZ (32) +#define ESP_ATT_CLAIM_JSON_MAX_SZ (448) +#define ESP_ATT_EAT_JSON_MAX_SZ (1344) + +#define ESP_ATT_PUBKEY_JSON_MAX_SZ (128) +#define ESP_ATT_SIGN_JSON_MAX_SZ (192) + +#define ESP_ATT_TK_MIN_SIZE (ESP_ATT_HDR_JSON_MAX_SZ + ESP_ATT_EAT_JSON_MAX_SZ + ESP_ATT_PUBKEY_JSON_MAX_SZ + ESP_ATT_SIGN_JSON_MAX_SZ) + +#if ESP_TEE_BUILD +#define ESP_ATT_TK_KEY_ID (CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID) +#else +#define ESP_ATT_TK_KEY_ID (-1) +#endif + +/** + * @brief Enumeration of partition types for attestation + */ +typedef enum { + ESP_ATT_PART_TYPE_BOOTLOADER = 0, + ESP_ATT_PART_TYPE_TEE = 1, + ESP_ATT_PART_TYPE_APP = 2, + ESP_ATT_PART_TYPE_OTHER = 3, + ESP_ATT_PART_TYPE_MAX = 4, +} esp_att_part_type_t; + +/** + * @brief Enumeration of digest types for attestation + */ +typedef enum { + ESP_ATT_DIGEST_TYPE_SHA256 = 0, /**< SHA-256 digest type */ +} esp_att_part_digest_type_t; + +/** + * @brief Structure to hold digest information for a partition + */ +typedef struct { + esp_att_part_digest_type_t type; /**< Type of digest */ + uint8_t calc_digest[MAX_DIGEST_SZ]; /**< Calculated digest */ + bool digest_validated; /**< Flag indicating if digest is validated */ + bool sign_verified; /**< Flag indicating if signature is verified */ + bool secure_padding; /**< Flag indicating if secure padding is present */ +} esp_att_part_digest_info_t; + +/** + * @brief Structure to hold chip revision information + */ +typedef struct { + uint16_t min_chip_rev; /**< Minimum chip revision */ + uint16_t max_chip_rev; /**< Maximum chip revision */ +} esp_att_part_chip_rev_t; + +/** + * @brief Structure to hold EAT claim metadata for a partition + */ +typedef struct { + esp_att_part_type_t type; /**< Type of partition */ + char ver[32]; /**< Version string */ + char idf_ver[32]; /**< ESP-IDF version string */ + uint32_t secure_ver; /**< Secure version number */ + esp_att_part_chip_rev_t part_chip_rev; /**< Chip revision information */ + esp_att_part_digest_info_t part_digest; /**< Digest information */ +} esp_att_part_metadata_t; + +/** + * @brief Structure to hold token header information + */ +typedef struct { + uint32_t magic; /**< Magic number for token identification */ + char encr_alg[32]; /**< Encryption algorithm */ + char sign_alg[32]; /**< Signing algorithm */ + uint16_t key_id; /**< Key identifier */ +} esp_att_token_hdr_t; + +/** + * @brief Structure to hold the Entity Attestation Token initial configuration + */ +typedef struct { + uint32_t nonce; /**< Nonce value */ + uint32_t client_id; /**< Client identifier (Attestation relying party) */ + uint32_t device_ver; /**< Device version */ + uint8_t device_id[SHA256_DIGEST_SZ]; /**< Device identifier */ + uint8_t instance_id[SHA256_DIGEST_SZ]; /**< Instance identifier */ + char psa_cert_ref[32]; /**< PSA certificate reference */ + uint8_t device_stat; /**< Flags indicating device status */ +} esp_att_token_cfg_t; +/** + * @brief Structure to hold an ECDSA key pair + */ +typedef struct { + uint32_t curve; /**< The elliptic curve used */ + uint8_t pvt_key[MAX_ECDSA_KEY_LEN]; /**< The private key */ + uint8_t pub_key_x[MAX_ECDSA_KEY_LEN]; /**< The x-coordinate of the public key */ + uint8_t pub_key_y[MAX_ECDSA_KEY_LEN]; /**< The y-coordinate of the public key */ +} __attribute__((aligned(4))) __attribute__((__packed__)) esp_att_ecdsa_keypair_t; + +/** + * @brief Structure for linked list element of software claims + */ +typedef struct _esp_att_sw_claim_list { + esp_att_part_metadata_t metadata; /**< Metadata for the partition */ + SLIST_ENTRY(_esp_att_sw_claim_list) next; /**< Pointer to next item in the list */ +} esp_att_sw_claim_list_t; + +/** + * @brief Linked list of software claims + */ +struct esp_att_sw_claim_list { + struct _esp_att_sw_claim_list *slh_first; +}; + +/** + * @brief Convert a hexadecimal buffer to a hexadecimal string + * + * @param hexbuf Input hexadecimal buffer + * @param hexbuf_sz Size of the input hexadecimal buffer + * @param hexstr Output hexadecimal string buffer + * @param hexstr_len Length of the output hexadecimal string buffer + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len); + +/** + * @brief Get claim data for the bootloader + * + * @param btl_metadata Bootloader metadata output context + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_get_btl_claim_data(esp_att_part_metadata_t *btl_metadata); + +/** + * @brief Get claim data for the REE/user application + * + * @param app_metadata REE/user application metadata output context + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_get_app_claim_data(esp_att_part_metadata_t *app_metadata); + +/** + * @brief Get claim data for the TEE application + * + * @param tee_metadata TEE metadata output context + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_get_tee_claim_data(esp_att_part_metadata_t *tee_metadata); + +/** + * @brief Convert token header to JSON format + * + * @param tk_hdr Token header structure + * @param header_json Output buffer to store the JSON string + * @param len Length of the generated JSON string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_header_to_json(const esp_att_token_hdr_t *tk_hdr, char **header_json, int *len); + +/** + * @brief Convert token claim data to JSON + * + * @param head Software claim list + * @param cfg Token configuration + * @param eat_json Output buffer to store the JSON string + * @param len Length of the generated JSON string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, const esp_att_token_cfg_t *cfg, char **eat_json, int *len); + +/** + * @brief Convert token public key to JSON + * + * @param keypair ECDSA key pair + * @param pubkey_json Output buffer to store the JSON string + * @param len Length of the generated JSON string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_pubkey_to_json(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_json, int *len); + +/** + * @brief Convert token signature to JSON + * + * @param keypair ECDSA key pair + * @param digest Digest to be signed + * @param digest_len Length of the digest + * @param sign_json Output buffer to store the JSON string + * @param len Length of the generated JSON string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_sign_to_json(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t digest_len, char **sign_json, int *len); + +/** + * @brief Generate an ECDSA key pair using the secp256r1 curve + * + * @param keypair Context to store the generated key pair + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_ecdsa_gen_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair); + +/** + * @brief Get ECDSA signature for a given digest + * + * @param keypair ECDSA key pair + * @param digest Digest to be signed + * @param len Length of the digest + * @param sign_r_hexstr Output buffer holding the signature r component as a hex string + * @param sign_s_hexstr Output buffer holding the signature s component as a hex string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len, + char **sign_r_hexstr, char **sign_s_hexstr); + +/** + * @brief Get the public key as a hex string + * + * @param keypair ECDSA key pair + * @param pubkey_hexstr Output buffer holding the public key as a hex string + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_ecdsa_get_pubkey(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_hexstr); + +/** + * @brief Get the digest of the public key + * + * @param keypair ECDSA key pair + * @param digest Digest buffer + * @param len Length of the digest buffer + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise + */ +esp_err_t esp_att_utils_ecdsa_get_pubkey_digest(const esp_att_ecdsa_keypair_t *keypair, uint8_t *digest, const size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt b/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt new file mode 100644 index 000000000000..ed3d3b4449ba --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt @@ -0,0 +1,13 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + +if(esp_tee_build) + return() +endif() + +set(srcs "esp_tee_attestation.c") +set(include_dirs ".") +set(priv_requires esp_tee) + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES ${priv_requires}) diff --git a/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c new file mode 100644 index 000000000000..1f1c27e97ca8 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +#include "sdkconfig.h" + +static __attribute__((unused)) const char *TAG = "esp_tee_att"; + +esp_err_t esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref, + uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len) +{ + return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(7, SS_ESP_TEE_ATT_GENERATE_TOKEN, nonce, client_id, + psa_cert_ref, token_buf, token_buf_size, token_len); +} diff --git a/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h new file mode 100644 index 000000000000..4de73aa69fd7 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate and return an entity attestation token (EAT) from the TEE + * + * The EAT consists of the below details: + * - For all firmware images (Bootloader, active TEE and non-secure app) + * - Project and ESP-IDF version + * - Digest (SHA256) + * - Public key corresponding to the private key used for signing (in compressed format) + * - Signature generated using the ECDSA key stored in the configured slot of the TEE's Secure Storage (`r` and `s` components) + * + * @param[in] nonce Attestation request identification + * @param[in] client_id Relying Party identification + * @param[in] psa_cert_ref PSA certification ID + * @param[in] token_buf Output buffer which will hold the resultant EAT in JSON format + * @param[in] token_buf_size Size of the output buffer + * @param[out] token_len Actual length of the output EAT JSON + * + * @return + * - `ESP_OK` on success + * - `ESP_ERR_INVALID_ARG` in case token_buf or token_len are NULL or token_buf_size is 0 + * - `ESP_ERR_NO_MEM` in case memory could not be allocated for the internal structures + * - `ESP_FAIL` other errors + */ +esp_err_t esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref, + uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt b/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt new file mode 100644 index 000000000000..569101f7a4ae --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt @@ -0,0 +1,7 @@ +set(srcs "esp_tee_flash.c") +set(include_dirs "include") + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES esp_tee log + REQUIRES bootloader_support) diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c b/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c new file mode 100644 index 000000000000..cdbddd16f6b7 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "esp_err.h" +#include "esp_log.h" + +#include "bootloader_utility_tee.h" +#include "esp_tee_ota_utils.h" + +#include "esp_tee.h" +#include "esp_tee_flash.h" +#include "sdkconfig.h" + +static const char *TAG = "esp_tee_flash"; + +// Structure containing the valid flash address range for flash operations through TEE +typedef struct { + uint32_t reserved; +} tee_flash_protect_info_t; + +static tee_flash_protect_info_t tee_prot_ctx; + +// Running REE (user) app partition +static esp_partition_info_t ree_running; + +// List storing all the partition table entries +typedef struct _partition_list { + SLIST_ENTRY(_partition_list) next; + esp_partition_info_t partition; +} partition_list_t; + +SLIST_HEAD(partition_list, _partition_list) partition_table_list; + +esp_err_t esp_tee_flash_setup_prot_ctx(uint8_t tee_boot_part) +{ + static bool is_first_call = true; + + if (is_first_call) { + // TODO: To-be-implemented for C6 + (void)tee_boot_part; + tee_prot_ctx.reserved = UINT32_MAX; + is_first_call = false; + } + + return ESP_OK; +} + +static partition_list_t *create_partition_entry(const esp_partition_info_t* partition_info) +{ + partition_list_t *partition_entry = calloc(1, sizeof(partition_list_t)); + assert(partition_entry != NULL); + memcpy(&partition_entry->partition, partition_info, sizeof(esp_partition_info_t)); + return partition_entry; +} + +static esp_err_t get_partition_table(void) +{ + if (SLIST_EMPTY(&partition_table_list)) { + const esp_partition_info_t *partition_table = esp_tee_flash_mmap(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN); + if (partition_table == NULL) { + ESP_LOGE(TAG, "esp_tee_flash_mmap failed!"); + return ESP_FAIL; + } + + int num_partitions = 0; + esp_err_t err = esp_partition_table_verify(partition_table, false, &num_partitions); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Partition table verification failed!"); + return err; + } + + SLIST_INIT(&partition_table_list); + + for (size_t num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { + const esp_partition_info_t *part = &partition_table[num_parts]; + if (part->type == PART_TYPE_END && part->subtype == PART_SUBTYPE_END) { + break; + } + + partition_list_t *partition = create_partition_entry(part); + SLIST_INSERT_HEAD(&partition_table_list, partition, next); + } + esp_tee_flash_munmap(partition_table); + } + + return ESP_OK; +} + +esp_err_t esp_tee_flash_find_partition(uint8_t type, uint8_t subtype, const char *label, esp_partition_info_t *dest_ptr) +{ + if (dest_ptr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + /* NOTE: Fetch the partition table */ + esp_err_t err = get_partition_table(); + if (err != ESP_OK) { + return err; + } + + partition_list_t *partition_entry; + + SLIST_FOREACH(partition_entry, &partition_table_list, next) { + if (partition_entry->partition.type == type && partition_entry->partition.subtype == subtype) { + if (label == NULL || !memcmp(label, partition_entry->partition.label, strnlen(label, sizeof(partition_entry->partition.label)))) { + memcpy(dest_ptr, &partition_entry->partition, sizeof(esp_partition_info_t)); + return ESP_OK; + } + } + } + return ESP_ERR_NOT_FOUND; +} + +esp_err_t esp_tee_flash_get_part_info_for_addr(uint32_t paddr, esp_partition_info_t *part_info) +{ + if (part_info == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = get_partition_table(); + if (err != ESP_OK) { + return err; + } + + partition_list_t *partition_entry; + SLIST_FOREACH(partition_entry, &partition_table_list, next) { + uint32_t start_addr = partition_entry->partition.pos.offset; + uint32_t end_addr = start_addr + partition_entry->partition.pos.size; + if (paddr >= start_addr && paddr < end_addr) { + memcpy(part_info, &partition_entry->partition, sizeof(esp_partition_info_t)); + return ESP_OK; + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t esp_tee_flash_set_running_ree_partition(uint32_t paddr) +{ + return esp_tee_flash_get_part_info_for_addr(paddr, &ree_running); +} + +esp_partition_info_t *esp_tee_flash_get_running_ree_partition(void) +{ + return &ree_running; +} diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h b/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h new file mode 100644 index 000000000000..f0551cae74f4 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include "esp_err.h" + +#include "bootloader_flash_priv.h" +#include "esp_flash_partitions.h" +#include "esp_image_format.h" + +/* Defining placeholders for the bootloader flash APIs */ +#define esp_tee_flash_mmap_get_free_pages bootloader_mmap_get_free_pages +#define esp_tee_flash_mmap bootloader_mmap +#define esp_tee_flash_munmap bootloader_munmap +#define esp_tee_flash_read bootloader_flash_read +#define esp_tee_flash_write bootloader_flash_write +#define esp_tee_flash_erase_range bootloader_flash_erase_range + +/** + * @brief Setup the context holding the permissible address ranges for flash operations through TEE + * + * @param tee_boot_part Active TEE partition + * + * @return esp_err_t ESP_OK: no error; ESP_FAIL otherwise. + */ +esp_err_t esp_tee_flash_setup_prot_ctx(uint8_t tee_boot_part); + +/** + * @brief Get the partition table entry of a partition to be found, + * given its type, subtype and label + * If label is NULL, the first partition with matching type and + * subtype is returned. + * + * @param type Partition type + * @param subtype Partition subtype + * @param label Partition label + * @param dest_ptr Pointer to where the the partition table entry of + * the requested partition is to be copied + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_flash_find_partition(uint8_t type, uint8_t subtype, const char *label, esp_partition_info_t *dest_ptr); + +/** + * @brief Get the partition table entry of a partition to be found, + * given an address present within its range + * + * @param paddr Physical address + * @param part_info Pointer to where the the partition table entry of + * the found partition is to be copied + * + * @return esp_err_t ESP_OK: no error; + * ESP_ERR_NO_INVALID_ARG: part_info is NULL; + * ESP_ERR_NOT_FOUND: No partition found for the given paddr; + * ESP_FAIL otherwise. + */ +esp_err_t esp_tee_flash_get_part_info_for_addr(uint32_t paddr, esp_partition_info_t *part_info); + +/** + * @brief Set the running REE (user) app partition as per the argument given by the bootloader + * + * @param paddr Physical address within the running REE (user) app partition's range + * + * @return esp_err_t ESP_OK: no error; otherwise refer esp_tee_flash_get_part_info_for_addr() error codes + */ +esp_err_t esp_tee_flash_set_running_ree_partition(uint32_t paddr); + +/** + * @brief Fetch the running REE (user) app partition + * + * @return esp_partition_info_t* Partition table entry for the running REE (user) app partition + */ +esp_partition_info_t *esp_tee_flash_get_running_ree_partition(void); diff --git a/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt b/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt new file mode 100644 index 000000000000..e5ac22068a24 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt @@ -0,0 +1,17 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + +set(srcs) +set(priv_requires) +set(include_dirs "include") + +if(esp_tee_build) + list(APPEND srcs "esp_tee_ota_ops.c") + list(APPEND priv_requires bootloader_support esp_tee log spi_flash tee_flash_mgr) +else() + list(APPEND srcs "esp_tee_ota_ops_wrapper.c") + list(APPEND priv_requires esp_tee) +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES ${priv_requires}) diff --git a/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c new file mode 100644 index 000000000000..5f6efa00adf4 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" +#include "hal/efuse_hal.h" + +#include "spi_flash_mmap.h" +#include "esp_image_format.h" +#include "bootloader_utility_tee.h" + +#include "esp_tee.h" +#include "esp_tee_flash.h" + +#include "sdkconfig.h" + +typedef enum { + ESP_TEE_OTA_UNDEFINED = 0, + ESP_TEE_OTA_BEGIN, + ESP_TEE_OTA_IN_PROGRESS, + ESP_TEE_OTA_END, +} esp_tee_ota_state_t; + +typedef struct { + esp_tee_ota_state_t tee_ota_state; + esp_partition_pos_t tee_ota_data; + esp_partition_info_t tee_next; +} esp_tee_ota_handle_t; + +/* Global handle for managing TEE OTA */ +static esp_tee_ota_handle_t ota_handle = {}; + +static const char *TAG = "esp_tee_ota_ops"; + +static esp_err_t get_tee_otadata_part_pos(esp_partition_pos_t *tee_ota_pos) +{ + if (tee_ota_pos == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_partition_info_t tee_ota_info = {}; + esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_OTA, NULL, &tee_ota_info); + if (err != ESP_OK) { + return err; + } + + memcpy(tee_ota_pos, &tee_ota_info.pos, sizeof(esp_partition_pos_t)); + return ESP_OK; +} + +esp_err_t esp_tee_ota_begin(void) +{ + /* Initialising the TEE OTA handle */ + memset(&ota_handle, 0x00, sizeof(esp_tee_ota_handle_t)); + ota_handle.tee_ota_state = ESP_TEE_OTA_UNDEFINED; + + esp_err_t err = get_tee_otadata_part_pos(&ota_handle.tee_ota_data); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch TEE OTA data partition!"); + return err; + } + + ota_handle.tee_ota_state = ESP_TEE_OTA_BEGIN; + + int tee_boot_part = bootloader_utility_tee_get_boot_partition(&ota_handle.tee_ota_data); + int tee_next_boot_part = bootloader_utility_tee_get_next_update_partition(&ota_handle.tee_ota_data); + + esp_partition_info_t tee_next = {}; + err = esp_tee_flash_find_partition(PART_TYPE_APP, (uint8_t)tee_next_boot_part, NULL, &tee_next); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to find partition!"); + return err; + } + + ESP_LOGI(TAG, "Running partition - Subtype: 0x%x", (uint8_t)tee_boot_part); + ESP_LOGI(TAG, "Next partition - Subtype: 0x%x (Offset: 0x%" PRIx32 ")", (uint8_t)tee_next_boot_part, tee_next.pos.offset); + + const uint32_t aligned_erase_size = (tee_next.pos.size + SPI_FLASH_SEC_SIZE - 1) & ~(SPI_FLASH_SEC_SIZE - 1); + int ret = esp_tee_flash_erase_range(tee_next.pos.offset, aligned_erase_size); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to erase partition!"); + return ESP_ERR_FLASH_OP_FAIL; + } + + memcpy(&ota_handle.tee_next, &tee_next, sizeof(esp_partition_info_t)); + ota_handle.tee_ota_state = ESP_TEE_OTA_IN_PROGRESS; + + return ESP_OK; +} + +esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size) +{ + if (data == NULL || size == 0) { + ESP_LOGE(TAG, "Data cannot be NULL!"); + return ESP_ERR_INVALID_ARG; + } + + if (ota_handle.tee_ota_state != ESP_TEE_OTA_IN_PROGRESS) { + ESP_LOGE(TAG, "TEE OTA found to be in an invalid state!"); + return ESP_ERR_INVALID_STATE; + } + + if (rel_offset + size > ota_handle.tee_next.pos.size) { + ESP_LOGE(TAG, "Out of region write not allowed!"); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "Writing at offset: 0x%"PRIx32" | size: 0x%"PRIx32, rel_offset, size); + + bool write_encrypted = efuse_hal_flash_encryption_enabled(); + int ret = esp_tee_flash_write(ota_handle.tee_next.pos.offset + rel_offset, (void *)data, size, write_encrypted); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to write partition!"); + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; +} + +esp_err_t esp_tee_ota_end(void) +{ + if (ota_handle.tee_ota_state != ESP_TEE_OTA_IN_PROGRESS) { + ESP_LOGE(TAG, "TEE OTA found to be in an invalid state!"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = bootloader_utility_tee_set_boot_partition(&ota_handle.tee_ota_data, &ota_handle.tee_next); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set TEE boot partition (0x%"PRIx32")", err); + return err; + } + + ota_handle.tee_ota_state = ESP_TEE_OTA_END; + return ESP_OK; +} diff --git a/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c new file mode 100644 index 000000000000..ccc4213d4e9c --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +#include "sdkconfig.h" + +static __attribute__((unused)) const char *TAG = "esp_tee_ota_ops_wrapper"; + +esp_err_t esp_tee_ota_begin(void) +{ + return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_OTA_BEGIN); +} + +esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size) +{ + return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(4, SS_ESP_TEE_OTA_WRITE, rel_offset, data, size); +} + +esp_err_t esp_tee_ota_end(void) +{ + return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_OTA_END); +} diff --git a/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h b/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h new file mode 100644 index 000000000000..736230e65367 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Commence the TEE OTA update + * + * This function does the following: + * - Initialize the internal TEE OTA state machine + * - Set the passive TEE partition as the destination for the new image and erase it + * + * @return + * - `ESP_OK` on success + * - `ESP_ERR_NOT_FOUND` in case the `tee_otadata` or the passive TEE partition is not found + * - `ESP_ERR_FLASH_OP_FAIL` in case erasing the passive TEE partition fails + */ +esp_err_t esp_tee_ota_begin(void); + +/** + * @brief Write TEE OTA update data to the partition. + * This function can be called multiple times as data is received during the OTA operation + * and can write data in non-contiguous manner. + * + * @param rel_offset Address offset at which the given data should be written at - + * relative to the start address of the passive TEE partition + * @param data Data buffer to write + * @param size Size of data buffer in bytes + * + * @return + * - `ESP_OK` on success + * - `ESP_ERR_INVALID_ARG` in case the `tee_otadata` or the passive TEE partition is not found + * - `ESP_ERR_INVALID_STATE` in case the TEE OTA state machine is in an invalid state + * - `ESP_ERR_FLASH_OP_FAIL` in case writing the new TEE image fails + * - `ESP_FAIL` for other errors + * + */ +esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size); + +/** + * @brief Finish the TEE OTA update and validate newly written TEE image + * + * @return + * - `ESP_OK` on success + * - `ESP_ERR_INVALID_STATE` in case the TEE OTA state machine is in an invalid state + * - `ESP_ERR_IMAGE_INVALID` in case the new TEE OTA image verification fails + * - `ESP_FAIL` for other errors + */ +esp_err_t esp_tee_ota_end(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt new file mode 100644 index 000000000000..dc7650dd56a6 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt @@ -0,0 +1,17 @@ +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + +set(srcs) +set(priv_requires efuse mbedtls spi_flash) + +if(esp_tee_build) + list(APPEND srcs "tee_sec_storage.c") + list(APPEND priv_requires log tee_flash_mgr) +else() + list(APPEND srcs "tee_sec_storage_wrapper.c") + set(priv_requires esp_tee) +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS include + PRIV_REQUIRES ${priv_requires} + ) diff --git a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h new file mode 100644 index 000000000000..d1a48fd11b3a --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define MIN_SEC_STG_SLOT_ID 0 /*!< Minimum secure storage slot ID */ +#define MAX_SEC_STG_SLOT_ID 14 /*!< Maximum secure storage slot ID */ + +#define MAX_ECDSA_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the ECDSA key */ +#define MAX_AES_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the AES key */ + +/** + * @brief Enum to represent the type of key stored in the secure storage + * + */ +typedef enum { + ESP_SEC_STG_KEY_ECDSA_SECP256R1 = 0, + ESP_SEC_STG_KEY_AES256, + ESP_SEC_STG_MAX, +} esp_tee_sec_storage_type_t; + +/** + * @brief Structure holding the X and Y components + * of the ECDSA public key + * + */ +typedef struct { + uint8_t pub_x[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< X component */ + uint8_t pub_y[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< Y component */ +} __attribute__((__packed__)) esp_tee_sec_storage_pubkey_t; + +/** + * @brief Structure holding the R and S components + * of the ECDSA signature + * + */ +typedef struct { + uint8_t sign_r[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< R component */ + uint8_t sign_s[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< S component */ +} __attribute__((__packed__)) esp_tee_sec_storage_sign_t; + +/** + * @brief Initialize the TEE secure storage partition + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_init(void); + +/** + * @brief Generate a unique key and store it in the given secure storage slot + * + * @param slot_id secure storage slot ID + * @param key_type secure storage key type to generate + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type); + +/** + * @brief Generate and return the signature for the specified message digest using + * the key pair located in the given secure storage slot. + * + * @param[in] slot_id secure storage slot ID + * @param[in] hash Message digest + * @param[in] hlen Digest length + * @param[out] out_sign Output context holding the signature + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign); + +/** + * @brief Return the public key for the given secure storage slot + * + * @param[in] slot_id secure storage slot ID + * @param[out] pubkey Output context holding the public key + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey); + +/** + * @brief Check whether the given slot in the secure storage is empty or not + * + * @param slot_id secure storage slot ID + * + * @return bool true: slot is empty; false otherwise. + */ +bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id); + +/** + * @brief Erase the given secure storage slot + * + * @param slot_id secure storage slot ID + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id); + +/** + * @brief Perform encryption using AES256-GCM with the key stored in the specified slot + * + * @param[in] slot_id Secure storage slot ID containing the AES-GCM key + * @param[in] input Pointer to the input data buffer + * @param[in] len Length of the input data + * @param[in] aad Pointer to the Additional Authenticated Data (AAD) + * @param[in] aad_len Length of the AAD + * @param[out] tag Pointer to the authentication tag buffer + * @param[out] tag_len Length of the authentication tag + * @param[out] output Pointer to the output data buffer + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output); + +/** + * @brief Perform decryption using AES256-GCM with the key stored in the specified slot + * + * @param[in] slot_id Secure storage slot ID containing the AES-GCM key + * @param[in] input Pointer to the input data buffer + * @param[in] len Length of the input data + * @param[in] aad Pointer to the Additional Authenticated Data (AAD) + * @param[in] aad_len Length of the AAD + * @param[in] tag Pointer to the authentication tag buffer + * @param[in] tag_len Length of the authentication tag + * @param[out] output Pointer to the output data buffer + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c new file mode 100644 index 000000000000..4d39a6a6e5b7 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -0,0 +1,669 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_cpu.h" + +#include "mbedtls/aes.h" +#include "mbedtls/gcm.h" +#include "mbedtls/sha256.h" + +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/error.h" + +#include "esp_flash.h" +#include "esp_efuse.h" +#include "soc/efuse_reg.h" + +#include "esp_random.h" +#include "esp_tee_flash.h" +#include "esp_tee_sec_storage.h" + +#include "secure_service_num.h" +#include "esp_rom_sys.h" +#include "esp_log.h" +#include "spi_flash_mmap.h" + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +#define SECURE_STORAGE_SIZE 2048 + +#define AES256_GCM_KEY_LEN 32 +#define AES256_GCM_KEY_BITS (AES256_GCM_KEY_LEN * 8) +#define AES256_GCM_IV_LEN 12 +#define AES256_GCM_TAG_LEN 16 +#define AES256_GCM_AAD_LEN 16 + +#define ECDSA_SECP256R1_KEY_LEN 32 + +/* Structure to hold metadata for secure storage slots */ +typedef struct { + uint16_t owner_id; /* Identifier for the owner of this slot */ + uint16_t slot_id; /* Unique identifier for this storage slot */ + uint8_t reserved; /* Reserved for future use */ + uint8_t iv[AES256_GCM_IV_LEN]; /* Initialization vector for AES-GCM */ + uint8_t tag[AES256_GCM_TAG_LEN]; /* Authentication tag for AES-GCM */ + uint8_t data_type; /* Type of data stored in this slot */ + uint16_t data_len; /* Length of the data stored in this slot */ +} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_metadata_t; + +/* Structure to hold ECDSA SECP256R1 key pair */ +typedef struct { + uint8_t priv_key[ECDSA_SECP256R1_KEY_LEN]; /* Private key for ECDSA SECP256R1 */ + uint8_t pub_key[2 * ECDSA_SECP256R1_KEY_LEN]; /* Public key for ECDSA SECP256R1 (X and Y coordinates) */ +} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_ecdsa_secp256r1_t; + +/* Structure to hold AES-256 GCM key and IV */ +typedef struct { + uint8_t key[AES256_GCM_KEY_LEN]; /* Key for AES-256 GCM */ + uint8_t iv[AES256_GCM_IV_LEN]; /* Initialization vector for AES-256 GCM */ +} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_aes256_gcm_t; + +/* Union to hold different types of cryptographic keys */ +typedef union { + sec_stg_ecdsa_secp256r1_t ecdsa_secp256r1; /* ECDSA SECP256R1 key pair */ + sec_stg_aes256_gcm_t aes256_gcm; /* AES-256 GCM key and IV */ +} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_key_t; + +_Static_assert(sizeof(sec_stg_metadata_t) == 36, "Incorrect sec_stg_metadata_t size"); +_Static_assert(sizeof(sec_stg_key_t) == 96, "Incorrect sec_stg_key_t size"); + +// Need this buffer to read the flash data and then modify and write it back +// esp_rom_spiflash_write requires that we erase the region before writing to it +// TODO: IDF-7586 +static uint8_t tmp_buf[SECURE_STORAGE_SIZE]; + +// AAD buffer +static uint8_t aad_buf[AES256_GCM_AAD_LEN]; + +// Partition for the secure storage partition +static esp_partition_pos_t part_pos; + +static const char *TAG = "secure_storage"; + +/* ---------------------------------------------- Helper APIs ------------------------------------------------- */ +#if CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK > 9 +#error "TEE Secure Storage: Configured eFuse block for encryption key out of range! (see CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK)" +#endif + +static int buffer_hexdump(const char *label, const void *buffer, size_t length) +{ +#if CONFIG_SECURE_TEE_LOG_LEVEL >= 4 + if (label == NULL || buffer == NULL || length == 0) { + return -1; + } + + const uint8_t *bytes = (const uint8_t *)buffer; + const size_t max_bytes_per_line = 16; + char hexbuf[max_bytes_per_line * 3]; + + ESP_LOGD(TAG, "%s -", label); + + for (size_t i = 0; i < length; i += max_bytes_per_line) { + size_t chunk_len = MIN(max_bytes_per_line, length - i); + size_t hexbuf_idx = 0; + + for (size_t j = 0; j < chunk_len; j++) { + uint8_t byte = bytes[i + j]; + hexbuf[hexbuf_idx++] = (byte >> 4) < 10 ? '0' + (byte >> 4) : 'a' + (byte >> 4) - 10; + hexbuf[hexbuf_idx++] = (byte & 0x0F) < 10 ? '0' + (byte & 0x0F) : 'a' + (byte & 0x0F) - 10; + + hexbuf[hexbuf_idx++] = ' '; + if ((j + 1) % 8 == 0 && j + 1 < chunk_len) { + hexbuf[hexbuf_idx++] = ' '; + } + } + hexbuf[hexbuf_idx] = '\0'; + esp_rom_printf("%s\n", hexbuf); + } +#else + (void) label; + (void) buffer; + (void) length; +#endif + + return 0; +} + +static esp_err_t get_sec_stg_encr_key(uint8_t *key_buf, size_t key_buf_len) +{ + // NOTE: Key should strictly be of 256-bits + if (!key_buf || key_buf_len != AES256_GCM_KEY_LEN) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_OK; + +#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE + esp_efuse_block_t blk = (esp_efuse_block_t)(CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK); + + if (blk < EFUSE_BLK_KEY0 || blk >= EFUSE_BLK_KEY_MAX) { + ESP_LOGE(TAG, "Invalid eFuse block - %d", blk); + return ESP_ERR_INVALID_ARG; + } + + esp_efuse_purpose_t blk_purpose = esp_efuse_get_key_purpose(blk); + if (blk_purpose != ESP_EFUSE_KEY_PURPOSE_USER) { + ESP_LOGE(TAG, "Invalid eFuse block purpose - %d", blk_purpose); + return ESP_ERR_INVALID_STATE; + } + + memset(key_buf, 0x00, key_buf_len); + err = esp_efuse_read_block(blk, key_buf, 0, AES256_GCM_KEY_BITS); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to read eFuse block (err - %d)", err); + return err; + } +#else + memset(key_buf, 0xA5, key_buf_len); +#endif + + // Check if eFuse is empty + uint8_t empty_key_buf[AES256_GCM_KEY_LEN] = {0}; + if (memcmp(empty_key_buf, key_buf, key_buf_len) == 0) { + ESP_LOGE(TAG, "All-zeroes key read from eFuse"); + return ESP_FAIL; + } + + return err; +} + +static int rand_func(void *rng_state, unsigned char *output, size_t len) +{ + esp_fill_random(output, len); + return 0; +} + +static int secure_storage_write(uint16_t slot_id, uint8_t *data, size_t len, uint8_t type) +{ + uint8_t iv[AES256_GCM_IV_LEN]; + uint8_t tag[AES256_GCM_TAG_LEN]; + uint8_t key[AES256_GCM_KEY_LEN]; + uint8_t out_data[256] = {0}; + + buffer_hexdump("Plaintext data", data, len); + + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + + esp_err_t err = get_sec_stg_encr_key(key, sizeof(key)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch key from eFuse!"); + goto exit; + } + + int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, AES256_GCM_KEY_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Error in setting key: %d", ret); + err = ESP_FAIL; + goto exit; + } + + // Generate different IV every time GCM encrypt is called + esp_fill_random(iv, AES256_GCM_IV_LEN); + + ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, iv, AES256_GCM_IV_LEN, + aad_buf, AES256_GCM_AAD_LEN, data, out_data, AES256_GCM_TAG_LEN, tag); + if (ret != 0) { + ESP_LOGE(TAG, "Error in encrypting data: %d", ret); + err = ESP_FAIL; + goto exit; + } + + buffer_hexdump("Encrypted data", out_data, len); + buffer_hexdump("TAG data", tag, sizeof(tag)); + + // Currently keeping the owner ID as 0 + sec_stg_metadata_t metadata; + metadata.owner_id = 0; + metadata.slot_id = slot_id; + memcpy(metadata.iv, iv, AES256_GCM_IV_LEN); + memcpy(metadata.tag, tag, AES256_GCM_TAG_LEN); + metadata.data_type = type; + metadata.data_len = len; + + uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id; + + /* ROM flash APIs require the region to be erased before writing to it. + * For that, we read the entire sector, make changes in read buffer, and then write + * the entire data back in flash. + * + * This opens up a small window when the sector has been erased but the device resets before writing the + * data back in flash. This can lead to loss of data. + * + * TODO: IDF-7586 + */ + ret = esp_tee_flash_read(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false); + if (ret != 0) { + ESP_LOGE(TAG, "Error reading flash contents: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + + memcpy(&tmp_buf[slot_offset], &metadata, sizeof(sec_stg_metadata_t)); + memcpy(&tmp_buf[slot_offset + sizeof(sec_stg_metadata_t)], out_data, len); + + ret = esp_tee_flash_erase_range(part_pos.offset, ALIGN_UP(SECURE_STORAGE_SIZE, FLASH_SECTOR_SIZE)); + ret |= esp_tee_flash_write(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false); + if (ret != 0) { + ESP_LOGE(TAG, "Error writing encrypted data: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + err = ESP_OK; + +exit: + mbedtls_gcm_free(&gcm); + return err; +} + +static esp_err_t secure_storage_read(uint16_t slot_id, uint8_t *data, size_t len, uint8_t type) +{ + esp_err_t err; + + sec_stg_metadata_t metadata; + uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id; + + uint8_t key[AES256_GCM_KEY_BITS / 8]; + uint8_t flash_data[256] = {0}; + + int ret = esp_tee_flash_read(part_pos.offset + slot_offset, (uint32_t *)&metadata, sizeof(sec_stg_metadata_t), false); + if (ret != 0) { + ESP_LOGE(TAG, "Error reading metadata: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + + if (metadata.data_type != type || metadata.data_len != len) { + ESP_LOGE(TAG, "Data type/length mismatch"); + err = ESP_ERR_NOT_FOUND; + goto exit; + } + + ret = esp_tee_flash_read(part_pos.offset + slot_offset + sizeof(sec_stg_metadata_t), (uint32_t *)flash_data, metadata.data_len, false); + if (ret != 0) { + ESP_LOGE(TAG, "Error reading data: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + + buffer_hexdump("Encrypted data", flash_data, len); + buffer_hexdump("TAG data", metadata.tag, AES256_GCM_TAG_LEN); + + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + + err = get_sec_stg_encr_key(key, sizeof(key)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch key from eFuse!"); + goto cleanup; + } + + ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, AES256_GCM_KEY_BITS); + if (ret != 0) { + err = ESP_FAIL; + goto cleanup; + } + + ret = mbedtls_gcm_auth_decrypt(&gcm, metadata.data_len, metadata.iv, AES256_GCM_IV_LEN, + aad_buf, AES256_GCM_AAD_LEN, metadata.tag, AES256_GCM_TAG_LEN, flash_data, data); + if (ret != 0) { + ESP_LOGE(TAG, "Error in decrypting data: %d", ret); + err = ESP_FAIL; + goto cleanup; + } + + buffer_hexdump("Decrypted data", data, len); + err = ESP_OK; + +cleanup: + mbedtls_gcm_free(&gcm); +exit: + return err; +} + +/* ---------------------------------------------- Interface APIs ------------------------------------------------- */ + +esp_err_t esp_tee_sec_storage_init(void) +{ + ESP_LOGI(TAG, "Initializing secure storage..."); + esp_partition_info_t part_info = {}; + esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL, &part_info); + if (err != ESP_OK) { + ESP_LOGE(TAG, "No secure storage partition found (0x%08x)", err); + return err; + } else { +#if CONFIG_SECURE_TEE_SEC_STG_MODE_DEVELOPMENT + ESP_LOGW(TAG, "TEE Secure Storage enabled in insecure DEVELOPMENT mode"); +#endif + // Take backup of the partition for future usage + part_pos = part_info.pos; + } + + return ESP_OK; +} + +static int generate_ecdsa_secp256r1_key(sec_stg_key_t *keyctx) +{ + if (keyctx == NULL) { + return -1; + } + + ESP_LOGI(TAG, "Generating ECDSA-SECP256R1 private key..."); + + mbedtls_ecdsa_context ctxECDSA; + mbedtls_ecdsa_init(&ctxECDSA); + + int ret = mbedtls_ecdsa_genkey(&ctxECDSA, MBEDTLS_ECP_DP_SECP256R1, rand_func, NULL); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to generate ECDSA key"); + goto exit; + } + + ret = mbedtls_mpi_write_binary(&ctxECDSA.MBEDTLS_PRIVATE(d), keyctx->ecdsa_secp256r1.priv_key, ECDSA_SECP256R1_KEY_LEN); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_write_binary(&(ctxECDSA.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), &keyctx->ecdsa_secp256r1.pub_key[0], ECDSA_SECP256R1_KEY_LEN); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_write_binary(&(ctxECDSA.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)), &keyctx->ecdsa_secp256r1.pub_key[ECDSA_SECP256R1_KEY_LEN], ECDSA_SECP256R1_KEY_LEN); + if (ret != 0) { + goto exit; + } + + buffer_hexdump("Private key", keyctx->ecdsa_secp256r1.priv_key, sizeof(keyctx->ecdsa_secp256r1.priv_key)); + +exit: + mbedtls_ecdsa_free(&ctxECDSA); + return ret; +} + +static int generate_aes256_gcm_key(sec_stg_key_t *keyctx) +{ + if (keyctx == NULL) { + return -1; + } + + ESP_LOGI(TAG, "Generating AES-256-GCM key..."); + + esp_fill_random(&keyctx->aes256_gcm.key, AES256_GCM_KEY_LEN); + esp_fill_random(&keyctx->aes256_gcm.iv, AES256_GCM_IV_LEN); + + return 0; +} + +esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID) { + ESP_LOGE(TAG, "Invalid slot ID"); + return ESP_ERR_INVALID_ARG; + } + + if (!esp_tee_sec_storage_is_slot_empty(slot_id)) { + ESP_LOGE(TAG, "Slot already occupied - clear before reuse"); + return ESP_ERR_INVALID_STATE; + } + + int ret = -1; + sec_stg_key_t keyctx; + + switch (key_type) { + case ESP_SEC_STG_KEY_ECDSA_SECP256R1: + if (generate_ecdsa_secp256r1_key(&keyctx) != 0) { + ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", ret); + return ESP_FAIL; + } + break; + case ESP_SEC_STG_KEY_AES256: + if (generate_aes256_gcm_key(&keyctx) != 0) { + ESP_LOGE(TAG, "Failed to generate AES key (%d)", ret); + return ESP_FAIL; + } + break; + default: + ESP_LOGE(TAG, "Unsupported key-type!"); + return ESP_ERR_NOT_SUPPORTED; + } + + return secure_storage_write(slot_id, (uint8_t *)&keyctx, sizeof(keyctx), key_type); +} + +esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID || hash == NULL || out_sign == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (hlen == 0) { + return ESP_ERR_INVALID_SIZE; + } + + sec_stg_key_t keyctx; + + esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), ESP_SEC_STG_KEY_ECDSA_SECP256R1); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch key from slot"); + return err; + } + + mbedtls_mpi r, s; + mbedtls_ecp_keypair priv_key; + mbedtls_ecdsa_context sign_ctx; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + mbedtls_ecp_keypair_init(&priv_key); + mbedtls_ecdsa_init(&sign_ctx); + + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &priv_key, keyctx.ecdsa_secp256r1.priv_key, sizeof(keyctx.ecdsa_secp256r1.priv_key)); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_ecdsa_from_keypair(&sign_ctx, &priv_key); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ESP_LOGI(TAG, "Generating ECDSA-SECP256R1 signature..."); + + ret = mbedtls_ecdsa_sign(&sign_ctx.MBEDTLS_PRIVATE(grp), &r, &s, &sign_ctx.MBEDTLS_PRIVATE(d), hash, hlen, + rand_func, NULL); + if (ret != 0) { + ESP_LOGE(TAG, "Error generating signature: %d", ret); + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_mpi_write_binary(&r, out_sign->sign_r, ECDSA_SECP256R1_KEY_LEN); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_mpi_write_binary(&s, out_sign->sign_s, ECDSA_SECP256R1_KEY_LEN); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + err = ESP_OK; + +exit: + mbedtls_ecdsa_free(&sign_ctx); + mbedtls_ecp_keypair_free(&priv_key); + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&r); + + return err; +} + +esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID || pubkey == NULL) { + return ESP_ERR_INVALID_ARG; + } + + sec_stg_key_t keyctx; + + esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), ESP_SEC_STG_KEY_ECDSA_SECP256R1); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch key from slot"); + return err; + } + + memcpy(pubkey->pub_x, &keyctx.ecdsa_secp256r1.pub_key[0], ECDSA_SECP256R1_KEY_LEN); + memcpy(pubkey->pub_y, &keyctx.ecdsa_secp256r1.pub_key[ECDSA_SECP256R1_KEY_LEN], ECDSA_SECP256R1_KEY_LEN); + + return ESP_OK; +} + +bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID) { + ESP_LOGE(TAG, "Invalid slot ID"); + return false; + } + + sec_stg_metadata_t metadata, blank_metadata; + memset(&blank_metadata, 0xFF, sizeof(sec_stg_metadata_t)); + + uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id; + bool ret = false; + + int err = esp_tee_flash_read(part_pos.offset + slot_offset, (uint32_t *)&metadata, sizeof(sec_stg_metadata_t), false); + if (err != 0) { + goto exit; + } + + if (memcmp(&metadata, &blank_metadata, sizeof(sec_stg_metadata_t)) && metadata.slot_id == slot_id) { + goto exit; + } + ret = true; + +exit: + return ret; +} + +esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID) { + ESP_LOGE(TAG, "Invalid slot ID"); + return ESP_ERR_INVALID_ARG; + } + + if (esp_tee_sec_storage_is_slot_empty(slot_id)) { + return ESP_OK; + } + + sec_stg_key_t blank_data; + memset(&blank_data, 0xFF, sizeof(blank_data)); + + sec_stg_metadata_t blank_metadata; + memset(&blank_metadata, 0xFF, sizeof(sec_stg_metadata_t)); + + uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id; + esp_err_t err; + + int ret = esp_tee_flash_read(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false); + if (ret != 0) { + ESP_LOGE(TAG, "Error reading flash contents: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + + memcpy(&tmp_buf[slot_offset], &blank_metadata, sizeof(sec_stg_metadata_t)); + memcpy(&tmp_buf[slot_offset + sizeof(sec_stg_metadata_t)], &blank_data, sizeof(sec_stg_key_t)); + + ret = esp_tee_flash_erase_range(part_pos.offset, ALIGN_UP(SECURE_STORAGE_SIZE, FLASH_SECTOR_SIZE)); + ret |= esp_tee_flash_write(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error clearing slot: %d", ret); + err = ESP_ERR_FLASH_OP_FAIL; + goto exit; + } + err = ESP_OK; + +exit: + return err; +} + +static esp_err_t tee_sec_storage_crypt_common(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output, + bool is_encrypt) +{ + if (slot_id > MAX_SEC_STG_SLOT_ID) { + ESP_LOGE(TAG, "Invalid slot ID"); + return ESP_ERR_INVALID_ARG; + } + + if (input == NULL || output == NULL || tag == NULL) { + ESP_LOGE(TAG, "Invalid input/output/tag buffer"); + return ESP_ERR_INVALID_ARG; + } + + if (len == 0 || tag_len == 0) { + ESP_LOGE(TAG, "Invalid length/tag length"); + return ESP_ERR_INVALID_SIZE; + } + + sec_stg_key_t keyctx; + esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(keyctx), 1); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch key from slot"); + return err; + } + + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + + int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, keyctx.aes256_gcm.key, AES256_GCM_KEY_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Error in setting key: %d", ret); + err = ESP_FAIL; + goto exit; + } + + if (is_encrypt) { + ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, keyctx.aes256_gcm.iv, AES256_GCM_IV_LEN, + aad, aad_len, input, output, tag_len, tag); + if (ret != 0) { + ESP_LOGE(TAG, "Error in encrypting data: %d", ret); + err = ESP_FAIL; + } + } else { + ret = mbedtls_gcm_auth_decrypt(&gcm, len, keyctx.aes256_gcm.iv, AES256_GCM_IV_LEN, + aad, aad_len, tag, tag_len, input, output); + if (ret != 0) { + ESP_LOGE(TAG, "Error in decrypting data: %d", ret); + err = ESP_FAIL; + } + } + err = ESP_OK; + +exit: + mbedtls_gcm_free(&gcm); + return err; +} + +esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return tee_sec_storage_crypt_common(slot_id, input, len, aad, aad_len, tag, tag_len, output, true); +} + +esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return tee_sec_storage_crypt_common(slot_id, input, len, aad, aad_len, tag, tag_len, output, false); +} diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c new file mode 100644 index 000000000000..94b69a99e7f5 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "secure_service_num.h" +#include "esp_tee.h" +#include "esp_err.h" +#include "esp_tee_sec_storage.h" + +esp_err_t esp_tee_sec_storage_init(void) +{ + return esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_SEC_STORAGE_INIT); +} + +esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type) +{ + return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_GEN_KEY, slot_id, key_type); +} + +esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *sign) +{ + return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_GET_SIGNATURE, slot_id, hash, hlen, sign); +} + +esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey) +{ + return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_GET_PUBKEY, slot_id, pubkey); +} + +bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id) +{ + return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_IS_SLOT_EMPTY, slot_id); +} + +esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id) +{ + return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_CLEAR_SLOT, slot_id); +} + +esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return esp_tee_service_call_with_noniram_intr_disabled(9, SS_ESP_TEE_SEC_STORAGE_ENCRYPT, slot_id, + input, len, aad, aad_len, tag, tag_len, output); +} + +esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return esp_tee_service_call_with_noniram_intr_disabled(9, SS_ESP_TEE_SEC_STORAGE_DECRYPT, slot_id, + input, len, aad, aad_len, tag, tag_len, output); +} diff --git a/components/esp_tee/subproject/main/CMakeLists.txt b/components/esp_tee/subproject/main/CMakeLists.txt new file mode 100644 index 000000000000..ebdf46b7c040 --- /dev/null +++ b/components/esp_tee/subproject/main/CMakeLists.txt @@ -0,0 +1,92 @@ +idf_build_get_property(target IDF_TARGET) +idf_build_get_property(arch IDF_TARGET_ARCH) +idf_build_get_property(idf_path IDF_PATH) + +idf_component_get_property(efuse_dir efuse COMPONENT_DIR) +idf_component_get_property(esp_hw_support_dir esp_hw_support COMPONENT_DIR) +idf_component_get_property(hal_dir hal COMPONENT_DIR) +idf_component_get_property(heap_dir heap COMPONENT_DIR) +idf_component_get_property(mbedtls_dir mbedtls COMPONENT_DIR) + +set(srcs) +set(include) + +# Common core implementation for TEE +set(srcs "core/esp_tee_init.c" + "core/esp_tee_intr.c" + "core/esp_secure_services.c" + "core/esp_secure_service_table.c") + +# Arch specific implementation for TEE +list(APPEND srcs "arch/${arch}/esp_tee_vectors.S" + "arch/${arch}/esp_tee_vector_table.S" + "arch/${arch}/esp_tee_secure_entry.S") + +# SoC specific implementation for TEE +list(APPEND srcs "soc/${target}/esp_tee_secure_sys_cfg.c" + "soc/${target}/esp_tee_pmp_pma_prot_cfg.c" + "soc/${target}/esp_tee_apm_prot_cfg.c" + "soc/${target}/esp_tee_apm_intr.c" + "soc/${target}/esp_tee_aes_intr.c") + +# Common module implementation for TEE + +# Panic handler implementation for TEE +list(APPEND srcs "common/panic/esp_tee_panic.c" + "common/panic/panic_helper_riscv.c") + +# Brownout detector +list(APPEND srcs "common/brownout.c") + +list(APPEND include "include" + "common" + "soc/${target}/include") + +# Heap +list(APPEND srcs "common/multi_heap.c") + +# Sources and headers shared with IDF +list(APPEND include "${efuse_dir}/private_include" + "${efuse_dir}/${target}/private_include") + +list(APPEND srcs "${hal_dir}/apm_hal.c" + "${hal_dir}/brownout_hal.c" + "${hal_dir}/wdt_hal_iram.c") + +# TLSF implementation for heap +list(APPEND include "${heap_dir}/include" + "${heap_dir}/tlsf" + "${heap_dir}/tlsf/include") +list(APPEND srcs "${heap_dir}/tlsf/tlsf.c") + +# Crypto +# AES +list(APPEND include "${mbedtls_dir}/port/include" + "${mbedtls_dir}/port/aes/include" + "${mbedtls_dir}/port/aes/dma/include") +# SHA +list(APPEND include "${mbedtls_dir}/port/sha/dma/include") + +# esp_app_desc_t configuration structure for TEE +list(APPEND srcs "common/esp_app_desc_tee.c") + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include}) + +set_source_files_properties("core/esp_secure_services.c" PROPERTIES COMPILE_FLAGS -Wno-deprecated) +include(${CMAKE_CURRENT_LIST_DIR}/ld/esp_tee_ld.cmake) + +# esp_app_desc_t configuration structure for TEE: Linking symbol and trimming project version and name +target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_app_desc_tee_include_impl") + +# cut PROJECT_VER and PROJECT_NAME to required 32 characters. +idf_build_get_property(project_ver PROJECT_VER) +idf_build_get_property(project_name PROJECT_NAME) +string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT) +string(SUBSTRING "${project_name}" 0 31 PROJECT_NAME_CUT) +message(STATUS "App \"${PROJECT_NAME_CUT}\" version: ${PROJECT_VER_CUT}") + +set_source_files_properties( + SOURCE "common/esp_app_desc_tee.c" + PROPERTIES COMPILE_DEFINITIONS + "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"${PROJECT_NAME_CUT}\"") diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S new file mode 100644 index 000000000000..dc5487d48d0a --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/tee_reg.h" +#include "soc/plic_reg.h" + + .global esp_tee_service_dispatcher + +/* Entry point to the secure world (i.e. M-mode) - responsible for + * setting up the execution environment for the secure world */ + .section .text + .align 4 + .global _sec_world_entry + .type _sec_world_entry, @function +_sec_world_entry: + /* Setup the APM for HP CPU in TEE mode */ + li t0, TEE_M0_MODE_CTRL_REG + sw zero, 0(t0) /* APM_LL_SECURE_MODE_TEE = 0 */ + + /* Disable the U-mode delegation of all interrupts */ + csrwi mideleg, 0 + + /* Jump to the secure service dispatcher */ + jal esp_tee_service_dispatcher + + /* Setup the APM for HP CPU in REE mode */ + li t0, TEE_M0_MODE_CTRL_REG + li t1, 0x1 /* APM_LL_SECURE_MODE_REE = 1 */ + sw t1, 0(t0) + + /* Enable the U-mode delegation of all interrupts (except the TEE secure interrupt) */ + li t0, 0xffffbfff + csrw mideleg, t0 + + /* Fire an M-ecall */ + mv a1, zero + ecall + fence + + ret diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S new file mode 100644 index 000000000000..9826ea129c0d --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "sdkconfig.h" +#include "soc/soc.h" + +#if ETS_INT_WDT_INUM != 24 + #error "ETS_INT_WDT_INUM expected to be 24" +#endif + + /* Handlers defined in the `esp_tee_vectors.S` file */ + .global _panic_handler + .global _tee_ns_intr_handler + .global _tee_s_intr_handler + + .section .exception_vectors_table.text, "ax" + + /* This is the vector table. MTVEC points here. + * + * Use 4-byte instructions here. 1 instruction = 1 entry of the table. + * The CPU jumps to MTVEC (i.e. the first entry) in case of an exception, + * and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt. + * + * Note: for our CPU, we need to place this on a 256-byte boundary, as CPU + * only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00). + */ + .balign 0x100 + + /* Since each entry must take 4-byte, let's temporarily disable the compressed + * instruction set that could potentially generate 2-byte instructions. */ + .option push + .option norvc + + .global _vector_table + .type _vector_table, @function +_vector_table: + j _panic_handler /* 0: Exception entry */ + /* NOTE: All of the free interrupts are used by the REE */ + j _tee_ns_intr_handler /* 1: Free interrupt number */ + j _tee_ns_intr_handler /* 2: Free interrupt number */ + j _tee_ns_intr_handler /* 3: Free interrupt number */ + j _tee_ns_intr_handler /* 4: Free interrupt number */ + j _tee_ns_intr_handler /* 5: Free interrupt number */ + j _tee_ns_intr_handler /* 6: Free interrupt number */ + j _tee_ns_intr_handler /* 7: Free interrupt number */ + j _tee_ns_intr_handler /* 8: Free interrupt number */ + j _tee_ns_intr_handler /* 9: Free interrupt number */ + j _tee_ns_intr_handler /* 10: Free interrupt number */ + j _tee_ns_intr_handler /* 11: Free interrupt number */ + j _tee_ns_intr_handler /* 12: Free interrupt number */ + j _tee_ns_intr_handler /* 13: Free interrupt number */ + j _tee_s_intr_handler /* 14: ESP-TEE: Secure interrupt handler entry */ + j _tee_ns_intr_handler /* 15: Free interrupt number */ + j _tee_ns_intr_handler /* 16: Free interrupt number */ + j _tee_ns_intr_handler /* 17: Free interrupt number */ + j _tee_ns_intr_handler /* 18: Free interrupt number */ + j _tee_ns_intr_handler /* 19: Free interrupt number */ + j _tee_ns_intr_handler /* 20: Free interrupt number */ + j _tee_ns_intr_handler /* 21: Free interrupt number */ + j _tee_ns_intr_handler /* 22: Free interrupt number */ + j _tee_ns_intr_handler /* 23: Free interrupt number */ + j _panic_handler /* 24: ETS_INT_WDT_INUM panic-interrupt (soc-level panic) */ + j _panic_handler /* 25: ETS_CACHEERR_INUM panic-interrupt (soc-level panic) */ + /* NOTE: Triggers panic irrespective of the Kconfig setting with ESP-TEE */ + j _panic_handler /* 26: ETS_MEMPROT_ERR_INUM handler (soc-level panic) */ + /* TODO: [IDF-10770] Not supported yet with ESP-TEE */ + j _panic_handler /* 27: ETS_ASSIST_DEBUG_INUM handler (soc-level panic) */ + j _tee_ns_intr_handler /* 28: Free interrupt number */ + j _tee_ns_intr_handler /* 29: Free interrupt number */ + j _tee_ns_intr_handler /* 30: Free interrupt number */ + j _tee_ns_intr_handler /* 31: Free interrupt number */ + j _panic_handler /* exception handler, entry 0 */ + + .size _vector_table, .-_vector_table + + /* Re-enable the compressed instruction set it is was enabled before */ + .option pop diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S new file mode 100644 index 000000000000..aded4c17a5e2 --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S @@ -0,0 +1,534 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "soc/soc_caps.h" + +#include "soc/interrupt_reg.h" +#include "soc/interrupt_matrix_reg.h" + +#include "riscv/encoding.h" +#include "riscv/rvruntime-frames.h" + +#include "esp_tee.h" +#include "sdkconfig.h" + + .equ SAVE_REGS, 32 + .equ CONTEXT_SIZE, (SAVE_REGS * 4) + .equ panic_from_exception, tee_panic_from_exc + .equ panic_from_isr, tee_panic_from_isr + .equ MAGIC, 0x1f + .equ RTNVAL, 0xc0de + .equ ECALL_U_MODE, 0x8 + .equ ECALL_M_MODE, 0xb + .equ TEE_APM_INTR_MASK_0, 0x00300000 + .equ TEE_APM_INTR_MASK_1, 0x000000F8 + + .global esp_tee_global_interrupt_handler + + .section .data + .align 4 + .global _ns_sp +_ns_sp: + .word 0 + + .section .data + .align 4 + .global _s_sp +_s_sp: + .word 0 + +/* Macro which first allocates space on the stack to save general + * purpose registers, and then save them. GP register is excluded. + * The default size allocated on the stack is CONTEXT_SIZE, but it + * can be overridden. */ +.macro save_general_regs cxt_size=CONTEXT_SIZE + addi sp, sp, -\cxt_size + sw ra, RV_STK_RA(sp) + sw tp, RV_STK_TP(sp) + sw t0, RV_STK_T0(sp) + sw t1, RV_STK_T1(sp) + sw t2, RV_STK_T2(sp) + sw s0, RV_STK_S0(sp) + sw s1, RV_STK_S1(sp) + sw a0, RV_STK_A0(sp) + sw a1, RV_STK_A1(sp) + sw a2, RV_STK_A2(sp) + sw a3, RV_STK_A3(sp) + sw a4, RV_STK_A4(sp) + sw a5, RV_STK_A5(sp) + sw a6, RV_STK_A6(sp) + sw a7, RV_STK_A7(sp) + sw s2, RV_STK_S2(sp) + sw s3, RV_STK_S3(sp) + sw s4, RV_STK_S4(sp) + sw s5, RV_STK_S5(sp) + sw s6, RV_STK_S6(sp) + sw s7, RV_STK_S7(sp) + sw s8, RV_STK_S8(sp) + sw s9, RV_STK_S9(sp) + sw s10, RV_STK_S10(sp) + sw s11, RV_STK_S11(sp) + sw t3, RV_STK_T3(sp) + sw t4, RV_STK_T4(sp) + sw t5, RV_STK_T5(sp) + sw t6, RV_STK_T6(sp) +.endm + +.macro save_mepc + csrr t0, mepc + sw t0, RV_STK_MEPC(sp) +.endm + +.macro save_mcsr + csrr t0, mstatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, mtvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, mtval + sw t0, RV_STK_MTVAL(sp) + csrr t0, mhartid + sw t0, RV_STK_MHARTID(sp) +.endm + +/* Restore the general purpose registers (excluding gp) from the context on + * the stack. The context is then deallocated. The default size is CONTEXT_SIZE + * but it can be overridden. */ +.macro restore_general_regs cxt_size=CONTEXT_SIZE + lw ra, RV_STK_RA(sp) + lw tp, RV_STK_TP(sp) + lw t0, RV_STK_T0(sp) + lw t1, RV_STK_T1(sp) + lw t2, RV_STK_T2(sp) + lw s0, RV_STK_S0(sp) + lw s1, RV_STK_S1(sp) + lw a0, RV_STK_A0(sp) + lw a1, RV_STK_A1(sp) + lw a2, RV_STK_A2(sp) + lw a3, RV_STK_A3(sp) + lw a4, RV_STK_A4(sp) + lw a5, RV_STK_A5(sp) + lw a6, RV_STK_A6(sp) + lw a7, RV_STK_A7(sp) + lw s2, RV_STK_S2(sp) + lw s3, RV_STK_S3(sp) + lw s4, RV_STK_S4(sp) + lw s5, RV_STK_S5(sp) + lw s6, RV_STK_S6(sp) + lw s7, RV_STK_S7(sp) + lw s8, RV_STK_S8(sp) + lw s9, RV_STK_S9(sp) + lw s10, RV_STK_S10(sp) + lw s11, RV_STK_S11(sp) + lw t3, RV_STK_T3(sp) + lw t4, RV_STK_T4(sp) + lw t5, RV_STK_T5(sp) + lw t6, RV_STK_T6(sp) + addi sp,sp, \cxt_size +.endm + +.macro restore_mepc + lw t0, RV_STK_MEPC(sp) + csrw mepc, t0 +.endm + +.macro store_magic_general_regs + lui ra, MAGIC + lui tp, MAGIC + lui t0, MAGIC + lui t1, MAGIC + lui t2, MAGIC + lui s0, MAGIC + lui s1, MAGIC + lui a0, MAGIC + lui a1, MAGIC + lui a2, MAGIC + lui a3, MAGIC + lui a4, MAGIC + lui a5, MAGIC + lui a6, MAGIC + lui a7, MAGIC + lui s2, MAGIC + lui s3, MAGIC + lui s4, MAGIC + lui s5, MAGIC + lui s6, MAGIC + lui s7, MAGIC + lui s8, MAGIC + lui s9, MAGIC + lui s10, MAGIC + lui s11, MAGIC + lui t3, MAGIC + lui t4, MAGIC + lui t5, MAGIC + lui t6, MAGIC +.endm + + .section .exception_vectors.text, "ax" + + /* Exception handler. */ + .global _panic_handler + .type _panic_handler, @function +_panic_handler: + /* Backup t0 on the stack before using it */ + addi sp, sp, -16 + sw t0, 0(sp) + + /* Check whether the exception is an M-mode ecall */ + csrr t0, mcause + xori t0, t0, ECALL_M_MODE + beqz t0, _machine_ecall + + /* Check whether the exception is an U-mode ecall */ + csrr t0, mcause + xori t0, t0, ECALL_U_MODE + beqz t0, _user_ecall + + /* Restore t0 from the stack */ + lw t0, 0(sp) + addi sp, sp, 16 + + /* Not an ecall, proceed to the panic handler */ + /* Allocate space on the stack and store general purpose registers */ + save_general_regs RV_STK_FRMSZ + + /* As gp register is not saved by the macro, save it here */ + sw gp, RV_STK_GP(sp) + + /* Same goes for the SP value before trapping */ + addi t0, sp, RV_STK_FRMSZ /* restore sp with the value when trap happened */ + + /* Save CSRs */ + sw t0, RV_STK_SP(sp) + save_mepc + save_mcsr + + /* Call panic_from_exception(sp) or panic_from_isr(sp) + * depending on whether we have a pseudo excause or not. + * If mcause's highest bit is 1, then an interrupt called this routine, + * so we have a pseudo excause. Else, it is due to a exception, we don't + * have an pseudo excause */ + mv a0, sp + csrr a1, mcause + /* Branches instructions don't accept immediates values, so use t1 to + * store our comparator */ + li t0, 0x80000000 + bgeu a1, t0, _call_panic_handler + sw a1, RV_STK_MCAUSE(sp) + /* exception_from_panic never returns */ + jal panic_from_exception + /* We arrive here if the exception handler has returned. */ + j _return_from_exception + +_call_panic_handler: + /* Remove highest bit from mcause (a1) register and save it in the + * structure */ + not t0, t0 + and a1, a1, t0 + sw a1, RV_STK_MCAUSE(sp) + jal panic_from_isr + + /* We arrive here if the exception handler has returned. This means that + * the exception was handled, and the execution flow should resume. + * Restore the registers and return from the exception. + */ +_return_from_exception: + restore_mepc + /* MTVEC and SP are assumed to be unmodified. + * MSTATUS, MHARTID, MTVAL are read-only and not restored. */ + lw gp, RV_STK_GP(sp) + restore_general_regs RV_STK_FRMSZ + mret + + .size _panic_handler, .-_panic_handler + + /* ECALL handler. */ + .type _ecall_handler, @function +_ecall_handler: + /* M-mode ecall handler */ +_machine_ecall: + /* Check whether this is the first M-mode ecall (see esp_tee_init) and skip context restoration */ + lui t0, ESP_TEE_M2U_SWITCH_MAGIC + beq a1, t0, _skip_ctx_restore + + /* Switching back to the saved REE stack */ + la t0, _ns_sp + lw sp, 0(t0) + fence + + /* Backup the A0 register + * This point is reached after an ecall is triggered after executing the secure service. + * The A0 register contains the return value of the corresponding service. + * After restoring the entire register context, we assign A0 the value back to the return value. */ + csrw mscratch, a0 + restore_general_regs RV_STK_FRMSZ + csrrw a0, mscratch, zero + + /* This point is reached only after the first M-mode ecall, never again (see esp_tee_init) */ +_skip_ctx_restore: + /* Copy the ra register to mepc which contains the user app entry point (i.e. call_start_cpu0) */ + csrw mepc, ra + + /* Set the privilege mode to transition to after mret to U-mode */ + li t3, MSTATUS_MPP + csrc mstatus, t3 + + /* Jump to the REE */ + mret + + /* U-mode ecall handler */ +_user_ecall: + /* Check whether we are returning after servicing an U-mode interrupt */ + lui t0, RTNVAL + csrr t1, mscratch + beq t0, t1, _rtn_from_ns_int + csrwi mscratch, 0 + + /* Restore t0 from the stack */ + lw t0, 0(sp) + addi sp, sp, 16 + + /* This point is reached after a secure service call is issued from the REE */ + /* Save register context and the mepc */ + save_general_regs RV_STK_FRMSZ + save_mepc + + /* Saving the U-mode (i.e. REE) stack pointer */ + la t0, _ns_sp + sw sp, 0(t0) + + /* Switching to the M-mode (i.e. TEE) stack */ + la sp, _tee_stack + + /* Load the TEE entry point (see sec_world_entry) in the mepc */ + la t2, esp_tee_app_config + lw t2, ESP_TEE_CFG_OFFS_S_ENTRY_ADDR(t2) + csrw mepc, t2 + + /* Set the privilege mode to transition to after mret to M-mode */ + li t3, MSTATUS_MPP + csrs mstatus, t3 + + mret + + /* This point is reached after servicing a U-mode interrupt occurred + * while executing a secure service */ +_rtn_from_ns_int: + /* Disable the U-mode interrupt delegation */ + csrwi mideleg, 0 + + /* Restore the secure stack pointer */ + la t0, _s_sp + lw sp, 0(t0) + + /* Clear the flag set marking the completion of interrupt service */ + csrwi mscratch, 0 + + /* Set the privilege mode to transition to after mret to M-mode */ + li t0, MSTATUS_MPP + csrs mstatus, t0 + + /* Restore register context and resume the secure service */ + restore_mepc + restore_general_regs + + mret + + .size _ecall_handler, .-_ecall_handler + + /* This is the interrupt handler for the U-mode interrupts. + * It saves the registers on the stack, re-enables the interrupt delegation, + * then jumps to the U-mode global interrupt handler, */ + .global _tee_ns_intr_handler + .type _tee_ns_intr_handler, @function +_tee_ns_intr_handler: + /* Start by saving the general purpose registers and the PC value before + * the interrupt happened. */ + save_general_regs + save_mepc + + /* Though it is not necessary we save GP and SP here. + * SP is necessary to help GDB to properly unwind + * the backtrace of threads preempted by interrupts (OS tick etc.). + * GP is saved just to have its proper value in GDB. */ + /* As gp register is not saved by the macro, save it here */ + sw gp, RV_STK_GP(sp) + /* Same goes for the SP value before trapping */ + addi t0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */ + /* Save SP */ + sw t0, RV_STK_SP(sp) + + /* For U-mode interrupts, we use mret to switch to U-mode after executing the below steps - */ + /* Disable the U-mode global interrupts */ + csrci ustatus, USTATUS_UIE + + /* Pass the interrupt ID to be serviced to U-mode */ + csrr t0, mcause + csrw ucause, t0 + + /* Configure `uepc` with the U-mode ecall handler (see u2m_switch) so that we can + * return to M-mode after handling the interrupt */ + la t0, esp_tee_app_config + lw t1, ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR(t0) + csrw uepc, t1 + + /* Set the program counter to the U-mode global interrupt handler (see _interrupt_handler) */ + lw t1, ESP_TEE_CFG_OFFS_NS_INTR_HANDLER(t0) + csrw mepc, t1 + + /* Set the privilege mode to transition to after mret to U-mode */ + li t1, MSTATUS_MPP + csrc mstatus, t1 + + /* Save the current secure stack pointer and switch to the U-mode interrupt stack + * saved while entering the secure service call routine (see `sec_world_entry`) */ + la t0, _s_sp + sw sp, 0(t0) + la t1, _ns_sp + lw sp, 0(t1) + + /* Set a flag to identify the next U2M switch would be after handling a U-mode interrupt */ + lui t0, RTNVAL + csrw mscratch, t0 + + /* Enable the U-mode interrupt delegation (except for the TEE secure interrupt) */ + li t0, 0xffffbfff + csrw mideleg, t0 + + /* Place magic bytes in all the general registers */ + store_magic_general_regs + + mret + + .size _tee_ns_intr_handler, .-_tee_ns_intr_handler + + /* This is the interrupt handler for the M-mode interrupts. + * It saves the registers on the stack, prepares for interrupt nesting, + * re-enables the interrupts, then jumps to the C dispatcher in esp_tee_intr.c. */ + .global _tee_s_intr_handler + .type _tee_s_intr_handler, @function +_tee_s_intr_handler: + /* Start by saving the general purpose registers and the PC value before + * the interrupt happened. */ + save_general_regs + save_mepc + + /* Though it is not necessary we save GP and SP here. + * SP is necessary to help GDB to properly unwind + * the backtrace of threads preempted by interrupts (OS tick etc.). + * GP is saved just to have its proper value in GDB. */ + /* As gp register is not saved by the macro, save it here */ + sw gp, RV_STK_GP(sp) + /* Same goes for the SP value before trapping */ + addi t0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */ + /* Save SP */ + sw t0, RV_STK_SP(sp) + + /* Check if the interrupt source is related to an APM exception */ + /* Define the addresses of the registers */ + li t0, INTMTX_CORE0_INT_STATUS_REG_0_REG + /* Load the values from the registers */ + lw t1, 0(t0) + /* Define the masks */ + li t2, TEE_APM_INTR_MASK_0 + /* Apply the masks */ + and t1, t1, t2 + /* Check if any of the masked bits are set */ + bnez t1, _save_reg_ctx + + /* Repeat for the other status register */ + li t0, INTMTX_CORE0_INT_STATUS_REG_1_REG + lw t1, 0(t0) + li t2, TEE_APM_INTR_MASK_1 + and t1, t1, t2 + bnez t1, _save_reg_ctx + + /* Continue normal execution */ + j _continue + +_save_reg_ctx: + /* Save CSR context here */ + save_mcsr + j _intr_hdlr_exec + +_continue: + /* Before doing anything preserve the stack pointer */ + mv s11, sp + /* Switching to the TEE interrupt stack */ + la sp, _tee_intr_stack + /* If this is a non-nested interrupt, SP now points to the interrupt stack */ + + /* Before dispatch c handler, restore interrupt to enable nested intr */ + csrr s1, mcause + csrr s2, mstatus + + /* TODO: [IDF-9972] Nested interrupts are not supported yet */ + # /* Save the interrupt threshold level */ + # li t0, PLIC_MXINT_THRESH_REG /*INTERRUPT_CORE0_CPU_INT_THRESH_REG*/ + # lw s3, 0(t0) + + # /* Increase interrupt threshold level */ + # li t2, 0x7fffffff + # and t1, s1, t2 /* t1 = mcause & mask */ + # slli t1, t1, 2 /* t1 = mcause * 4 */ + # li t2, PLIC_MXINT0_PRI_REG /*INTC_INT_PRIO_REG(0)*/ + # add t1, t2, t1 /* t1 = INTC_INT_PRIO_REG + 4 * mcause */ + # lw t2, 0(t1) /* t2 = INTC_INT_PRIO_REG[mcause] */ + # addi t2, t2, 1 /* t2 = t2 +1 */ + # sw t2, 0(t0) /* INTERRUPT_CORE0_CPU_INT_THRESH_REG = t2 */ + # fence + + # csrsi mstatus, MSTATUS_MIE + # /* MIE set. Nested interrupts can now occur */ + + #ifdef CONFIG_PM_TRACE + li a0, 0 /* = ESP_PM_TRACE_IDLE */ + #if SOC_CPU_CORES_NUM == 1 + li a1, 0 /* No need to check core ID on single core hardware */ + #else + csrr a1, mhartid + #endif + la t0, esp_pm_trace_exit + jalr t0 /* absolute jump, avoid the 1 MiB range constraint */ + #endif + + #ifdef CONFIG_PM_ENABLE + la t0, esp_pm_impl_isr_hook + jalr t0 /* absolute jump, avoid the 1 MiB range constraint */ + #endif + +_intr_hdlr_exec: + /* call the C dispatcher */ + mv a0, sp /* argument 1, stack pointer */ + mv a1, s1 /* argument 2, interrupt number (mcause) */ + /* mask off the interrupt flag of mcause */ + li t0, 0x7fffffff + and a1, a1, t0 + + jal esp_tee_global_interrupt_handler + + /* TODO: [IDF-9972] Nested interrupts are not supported yet */ + # csrci mstatus, MSTATUS_MIE + # /* MIE cleared. Nested interrupts are disabled */ + + # /* restore the interrupt threshold level */ + # li t0, PLIC_MXINT_THRESH_REG /*INTERRUPT_CORE0_CPU_INT_THRESH_REG*/ + # sw s3, 0(t0) + # fence + + /* restore the rest of the registers */ + csrw mcause, s1 + csrw mstatus, s2 + + /* Restoring the stack pointer */ + mv sp, s11 + + restore_mepc + restore_general_regs + /* exit, this will also re-enable the interrupts */ + mret + + .size _tee_s_intr_handler, .-_tee_s_intr_handler diff --git a/components/esp_tee/subproject/main/common/brownout.c b/components/esp_tee/subproject/main/common/brownout.c new file mode 100644 index 000000000000..e0038abeb3d0 --- /dev/null +++ b/components/esp_tee/subproject/main/common/brownout.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "esp_macros.h" +#include "esp_log.h" +#include "esp_cpu.h" +#include "soc/soc.h" +#include "esp_attr.h" +#include "bootloader_flash.h" +#include "hal/brownout_hal.h" +#include "hal/brownout_ll.h" +#include "esp_rom_sys.h" +#include "esp_rom_uart.h" +#include "sdkconfig.h" + +#include "esp_tee_intr.h" + +/* + * NOTE: Brownout threshold levels + * + * +-----------------+-------------------+ + * | Threshold Level | Voltage (Approx.) | + * +-----------------+-------------------+ + * | 7 | 2.51V | + * | 6 | 2.64V | + * | 5 | 2.76V | + * | 4 | 2.92V | + * | 3 | 3.10V | + * | 2 | 3.27V | + * +-----------------+-------------------+ + */ +#if defined(CONFIG_ESP_BROWNOUT_DET_LVL) +#define BROWNOUT_DET_LVL CONFIG_ESP_BROWNOUT_DET_LVL +#else +#define BROWNOUT_DET_LVL 0 +#endif + +static __attribute__((unused)) DRAM_ATTR const char *TAG = "BOD"; + +#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR +IRAM_ATTR static void rtc_brownout_isr_handler(void *arg) +{ + /* Normally RTC ISR clears the interrupt flag after the application-supplied + * handler returns. Since restart is called here, the flag needs to be + * cleared manually. + */ + brownout_ll_intr_clear(); + + // Stop the other core. +#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + const uint32_t core_id = esp_cpu_get_core_id(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_stall(other_core_id); +#endif + + // TODO: Support for resetting the flash during brownout for stability + ESP_DRAM_LOGI(TAG, "Brownout detector was triggered\r\n\r\n"); + + // Flush any data left in UART FIFOs + for (int i = 0; i < SOC_UART_HP_NUM; ++i) { + if (uart_ll_is_enabled(i)) { + esp_rom_output_tx_wait_idle(i); + } + } + + // generate core reset + esp_rom_software_reset_system(); + + ESP_INFINITE_LOOP(); +} +#endif // CONFIG_ESP_SYSTEM_BROWNOUT_INTR + +void esp_tee_brownout_init(void) +{ +#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR + brownout_hal_config_t cfg = { + .threshold = BROWNOUT_DET_LVL, + .enabled = true, + .reset_enabled = false, + .flash_power_down = true, + .rf_power_down = true, + }; + + brownout_hal_config(&cfg); + brownout_ll_intr_clear(); + // TODO IDF-6606: LP_RTC_TIMER interrupt source is shared by lp_timer and brownout detector, but lp_timer interrupt + // is not used now. An interrupt allocator is needed when lp_timer intr gets supported. + struct vector_desc_t bod_vd = { 0, NULL, NULL, NULL }; + bod_vd.source = ETS_LP_RTC_TIMER_INTR_SOURCE; + bod_vd.isr = rtc_brownout_isr_handler; + esp_tee_intr_register((void *)&bod_vd); + brownout_ll_intr_enable(true); + +#else // brownout without interrupt + + brownout_hal_config_t cfg = { + .threshold = BROWNOUT_DET_LVL, + .enabled = true, + .reset_enabled = true, + .flash_power_down = true, + .rf_power_down = true, + }; + + brownout_hal_config(&cfg); +#endif +} + +void esp_tee_brownout_disable(void) +{ + brownout_hal_config_t cfg = { + .enabled = false, + }; + + brownout_hal_config(&cfg); +#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR + brownout_ll_intr_enable(false); +#endif // CONFIG_ESP_SYSTEM_BROWNOUT_INTR +} diff --git a/components/esp_tee/subproject/main/common/esp_app_desc_tee.c b/components/esp_tee/subproject/main/common/esp_app_desc_tee.c new file mode 100644 index 000000000000..9dbc0040788a --- /dev/null +++ b/components/esp_tee/subproject/main/common/esp_app_desc_tee.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_app_desc.h" +#include "esp_attr.h" +#include "sdkconfig.h" + +// Application version info +const esp_app_desc_t __attribute__((section(".rodata_desc"))) esp_app_desc = { + .magic_word = ESP_APP_DESC_MAGIC_WORD, + .version = PROJECT_VER, + .project_name = PROJECT_NAME, + .idf_ver = IDF_VER, +#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION + .secure_version = CONFIG_BOOTLOADER_APP_SECURE_VERSION, +#else + .secure_version = 0, +#endif +#ifdef CONFIG_APP_COMPILE_TIME_DATE + .time = __TIME__, + .date = __DATE__, +#else + .time = "", + .date = "", +#endif + .min_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL, + .max_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL, + .mmu_page_size = 31 - __builtin_clz(CONFIG_MMU_PAGE_SIZE), +}; + +void esp_app_desc_tee_include_impl(void) +{ + // Linker hook, exists for no other purpose +} + +_Static_assert(sizeof(PROJECT_VER) <= sizeof(esp_app_desc.version), "[esp_tee] PROJECT_VER is longer than version field in structure"); +_Static_assert(sizeof(IDF_VER) <= sizeof(esp_app_desc.idf_ver), "[esp_tee] IDF_VER is longer than idf_ver field in structure"); +_Static_assert(sizeof(PROJECT_NAME) <= sizeof(esp_app_desc.project_name), "[esp_tee] PROJECT_NAME is longer than project_name field in structure"); diff --git a/components/esp_tee/subproject/main/common/multi_heap.c b/components/esp_tee/subproject/main/common/multi_heap.c new file mode 100644 index 000000000000..5da57c4f4dd5 --- /dev/null +++ b/components/esp_tee/subproject/main/common/multi_heap.c @@ -0,0 +1,196 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "tlsf.h" +#include "tlsf_block_functions.h" +#include "multi_heap.h" + +/* Handle to a registered TEE heap */ +static multi_heap_handle_t tee_heap; + +inline static void multi_heap_assert(bool condition, const char *format, int line, intptr_t address) +{ + /* Can't use libc assert() here as it calls printf() which can cause another malloc() for a newlib lock. + Also, it's useful to be able to print the memory address where corruption was detected. + */ + (void) condition; +} + +#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) \ + multi_heap_assert((CONDITION), "CORRUPT HEAP: multi_heap.c:%d detected at 0x%08x\n", \ + __LINE__, (intptr_t)(ADDRESS)) + +/* Check a block is valid for this heap. Used to verify parameters. */ +static void assert_valid_block(const heap_t *heap, const block_header_t *block) +{ + pool_t pool = tlsf_get_pool(heap->heap_data); + void *ptr = block_to_ptr(block); + + MULTI_HEAP_ASSERT((ptr >= pool) && + (ptr < pool + heap->pool_size), + (uintptr_t)ptr); +} + +int tee_heap_register(void *start_ptr, size_t size) +{ + assert(start_ptr); + if (size < (sizeof(heap_t))) { + //Region too small to be a heap. + return -1; + } + + heap_t *result = (heap_t *)start_ptr; + size -= sizeof(heap_t); + + /* Do not specify any maximum size for the allocations so that the default configuration is used */ + const size_t max_bytes = 0; + + result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size, max_bytes); + if (result->heap_data == NULL) { + return -1; + } + + result->lock = NULL; + result->free_bytes = size - tlsf_size(result->heap_data); + result->pool_size = size; + result->minimum_free_bytes = result->free_bytes; + + tee_heap = (multi_heap_handle_t)result; + + return 0; +} + +void *tee_heap_malloc(size_t size) +{ + if (tee_heap == NULL || size == 0) { + return NULL; + } + + void *result = tlsf_malloc(tee_heap->heap_data, size); + if (result) { + tee_heap->free_bytes -= tlsf_block_size(result); + tee_heap->free_bytes -= tlsf_alloc_overhead(); + if (tee_heap->free_bytes < tee_heap->minimum_free_bytes) { + tee_heap->minimum_free_bytes = tee_heap->free_bytes; + } + } + + return result; +} + +void *tee_heap_calloc(size_t n, size_t size) +{ + size_t reg_size = n * size; + void *ptr = tee_heap_malloc(reg_size); + if (ptr != NULL) { + memset(ptr, 0x00, reg_size); + } + return ptr; +} + +void *tee_heap_aligned_alloc(size_t size, size_t alignment) +{ + if (tee_heap == NULL || size == 0) { + return NULL; + } + + // Alignment must be a power of two + if (((alignment & (alignment - 1)) != 0) || (!alignment)) { + return NULL; + } + + void *result = tlsf_memalign_offs(tee_heap->heap_data, alignment, size, 0x00); + if (result) { + tee_heap->free_bytes -= tlsf_block_size(result); + tee_heap->free_bytes -= tlsf_alloc_overhead(); + if (tee_heap->free_bytes < tee_heap->minimum_free_bytes) { + tee_heap->minimum_free_bytes = tee_heap->free_bytes; + } + } + + return result; +} + +void tee_heap_free(void *p) +{ + if (tee_heap == NULL || p == NULL) { + return; + } + + assert_valid_block(tee_heap, block_from_ptr(p)); + + tee_heap->free_bytes += tlsf_block_size(p); + tee_heap->free_bytes += tlsf_alloc_overhead(); + tlsf_free(tee_heap->heap_data, p); +} + +void *malloc(size_t size) +{ + return tee_heap_malloc(size); +} + +void *calloc(size_t n, size_t size) +{ + return tee_heap_calloc(n, size); +} + +void free(void *ptr) +{ + tee_heap_free(ptr); +} + +void tee_heap_dump_free_size(void) +{ + if (tee_heap == NULL) { + return; + } + printf("Free: %uB | Minimum free: %uB\n", tee_heap->free_bytes, tee_heap->minimum_free_bytes); +} + +static bool tee_heap_dump_tlsf(void* ptr, size_t size, int used, void* user) +{ + (void)user; + printf("Block %p data, size: %d bytes, Free: %s\n", + (void *)ptr, + size, + used ? "No" : "Yes"); + return true; +} + +void tee_heap_dump_info(void) +{ + if (tee_heap == NULL) { + return; + } + printf("Showing data for TEE heap: %p\n", (void *)tee_heap); + tee_heap_dump_free_size(); + tlsf_walk_pool(tlsf_get_pool(tee_heap->heap_data), tee_heap_dump_tlsf, NULL); +} + +/* Definitions for functions from the heap component, used in files shared with ESP-IDF */ + +void *heap_caps_malloc(size_t alignment, size_t size, uint32_t caps) +{ + (void) caps; + return tee_heap_malloc(size); +} + +void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps) +{ + (void) caps; + return tee_heap_aligned_alloc(size, alignment); +} + +void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps) +{ + (void) caps; + uint32_t reg_size = n * size; + + void *ptr = tee_heap_aligned_alloc(reg_size, alignment); + if (ptr != NULL) { + memset(ptr, 0x00, reg_size); + } + return ptr; +} diff --git a/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c b/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c new file mode 100644 index 000000000000..8558a5e0e716 --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include "esp_attr.h" +#include "esp_macros.h" +#include "esp_rom_sys.h" +#include "esp_rom_uart.h" +#include "hal/apm_hal.h" + +#include "riscv/rvruntime-frames.h" + +#include "esp_tee.h" +#include "panic_helper.h" +#include "esp_tee_apm_intr.h" + +#define RV_FUNC_STK_SZ (32) + +#define tee_panic_print(format, ...) esp_rom_printf(DRAM_STR(format), ##__VA_ARGS__) + +static void tee_panic_end(void) +{ + // make sure all the panic handler output is sent from UART FIFO + if (CONFIG_ESP_CONSOLE_UART_NUM >= 0) { + esp_rom_output_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM); + } + + // generate core reset + esp_rom_software_reset_system(); +} + +void __assert_func(const char *file, int line, const char *func, const char *expr) +{ + tee_panic_print("Assert failed in %s, %s:%d (%s)\r\n", func, file, line, expr); + tee_panic_print("\n\n"); + + tee_panic_end(); + ESP_INFINITE_LOOP(); +} + +void abort(void) +{ +#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT + tee_panic_print("abort() was called at PC 0x%08x\r\n\n", (intptr_t)__builtin_return_address(0) - 3); + tee_panic_print("\n\n"); +#endif + + tee_panic_end(); + ESP_INFINITE_LOOP(); +} + +static void panic_handler(void *frame, bool pseudo_exccause) +{ + int fault_core = esp_cpu_get_core_id(); + + tee_panic_print("\n=================================================\n"); + tee_panic_print("Secure exception occurred on Core %d\n", fault_core); + if (pseudo_exccause) { + panic_print_isrcause((const void *)frame, fault_core); + } else { + panic_print_exccause((const void *)frame, fault_core); + } + tee_panic_print("=================================================\n"); + + panic_print_registers((const void *)frame, fault_core); + tee_panic_print("\n"); + panic_print_backtrace((const void *)frame, 100); + tee_panic_print("\n"); + tee_panic_print("Rebooting...\r\n\n"); + + tee_panic_end(); + ESP_INFINITE_LOOP(); +} + +void tee_panic_from_exc(void *frame) +{ + panic_handler(frame, false); +} + +void tee_panic_from_isr(void *frame) +{ + panic_handler(frame, true); +} + +void tee_apm_violation_isr(void *arg) +{ + intptr_t exc_sp = RV_READ_CSR(mscratch); + RvExcFrame *frame = (RvExcFrame *)exc_sp; + + apm_ctrl_path_t *apm_excp_type = NULL; + apm_ctrl_exception_info_t excp_info; + + apm_excp_type = (apm_ctrl_path_t *)arg; + + excp_info.apm_path.apm_ctrl = apm_excp_type->apm_ctrl; + excp_info.apm_path.apm_m_path = apm_excp_type->apm_m_path; + apm_hal_apm_ctrl_get_exception_info(&excp_info); + + /* Clear APM M path interrupt. */ + apm_hal_apm_ctrl_exception_clear(apm_excp_type); + + int fault_core = esp_cpu_get_core_id(); + + tee_panic_print("\n=================================================\n"); + tee_panic_print("APM permission violation occurred on Core %d\n", fault_core); + tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). ", fault_core, esp_tee_apm_excp_type_to_str(excp_info.excp_type)); + tee_panic_print("Exception was unhandled.\n"); + tee_panic_print("Fault addr: 0x%x | Mode: %s\n", excp_info.excp_addr, esp_tee_apm_excp_mode_to_str(excp_info.excp_mode)); + tee_panic_print("Module: %s | Path: 0x%02x\n", esp_tee_apm_excp_ctrl_to_str(excp_info.apm_path.apm_ctrl), excp_info.apm_path.apm_m_path); + tee_panic_print("Master: %s | Region: 0x%02x\n", esp_tee_apm_excp_mid_to_str(excp_info.excp_id), excp_info.excp_regn); + tee_panic_print("=================================================\n"); + panic_print_registers((const void *)frame, fault_core); + tee_panic_print("\n"); + panic_print_backtrace((const void *)frame, 100); + tee_panic_print("\n"); + tee_panic_print("Rebooting...\r\n\n"); + + tee_panic_end(); + ESP_INFINITE_LOOP(); +} diff --git a/components/esp_tee/subproject/main/common/panic/panic_helper.h b/components/esp_tee/subproject/main/common/panic/panic_helper.h new file mode 100644 index 000000000000..5a4b90da45ba --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/panic_helper.h @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void panic_print_backtrace(const void *f, int depth); + +void panic_print_registers(const void *f, int core); + +void panic_print_exccause(const void *f, int core); + +void panic_print_isrcause(const void *f, int core); diff --git a/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c b/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c new file mode 100644 index 000000000000..de2eae60fdff --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_tee.h" +#include "esp_attr.h" +#include "esp_private/panic_reason.h" +#include "riscv/csr.h" +#include "riscv/encoding.h" +#include "riscv/rvruntime-frames.h" + +#define tee_panic_print(format, ...) esp_rom_printf(DRAM_STR(format), ##__VA_ARGS__) + +void panic_print_backtrace(const void *f, int depth) +{ + // Basic backtrace + tee_panic_print("\r\nStack memory\r\n"); + uint32_t sp = (uint32_t)((RvExcFrame *)f)->sp; + const int per_line = 8; + for (int x = 0; x < depth; x += per_line * sizeof(uint32_t)) { + uint32_t *spp = (uint32_t *)(sp + x); + tee_panic_print("0x%08x: ", sp + x); + for (int y = 0; y < per_line; y++) { + tee_panic_print("0x%08x%s", spp[y], y == per_line - 1 ? "\r\n" : " "); + } + } +} + +void panic_print_registers(const void *f, int core) +{ + uint32_t *regs = (uint32_t *)f; + + // only print ABI name + const char *desc[] = { + "MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ", + "S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ", + "A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ", + "S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ", + "MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID " + }; + + tee_panic_print("\nCore %d register dump:", ((RvExcFrame *)f)->mhartid); + + for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) { + tee_panic_print("\r\n"); + for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) { + if (desc[x + y][0] != 0) { + tee_panic_print("%s: 0x%08x ", desc[x + y], regs[x + y]); + } + } + } + + tee_panic_print("MIE : 0x%08x ", RV_READ_CSR(mie)); + tee_panic_print("MIP : 0x%08x ", RV_READ_CSR(mip)); + tee_panic_print("MSCRATCH: 0x%08x\n", RV_READ_CSR(mscratch)); + tee_panic_print("UEPC : 0x%08x ", RV_READ_CSR(uepc)); + tee_panic_print("USTATUS : 0x%08x ", RV_READ_CSR(ustatus)); + tee_panic_print("UTVEC : 0x%08x ", RV_READ_CSR(utvec)); + tee_panic_print("UCAUSE : 0x%08x\n", RV_READ_CSR(ucause)); + tee_panic_print("UTVAL : 0x%08x ", RV_READ_CSR(utval)); + tee_panic_print("UIE : 0x%08x ", RV_READ_CSR(uie)); + tee_panic_print("UIP : 0x%08x\n", RV_READ_CSR(uip)); +} + +void panic_print_exccause(const void *f, int core) +{ + RvExcFrame *regs = (RvExcFrame *) f; + + //Please keep in sync with PANIC_RSN_* defines + static const char *reason[] = { + "Instruction address misaligned", + "Instruction access fault", + "Illegal instruction", + "Breakpoint", + "Load address misaligned", + "Load access fault", + "Store address misaligned", + "Store access fault", + "Environment call from U-mode", + "Environment call from S-mode", + NULL, + "Environment call from M-mode", + "Instruction page fault", + "Load page fault", + NULL, + "Store page fault", + }; + + const char *rsn = NULL; + if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) { + if (reason[regs->mcause] != NULL) { + rsn = (reason[regs->mcause]); + } + } + + const char *desc = "Exception was unhandled."; + const void *addr = (void *) regs->mepc; + tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). %s\n", core, rsn, desc); + + const char *exc_origin = "U-mode"; + if (regs->mstatus & MSTATUS_MPP) { + exc_origin = "M-mode"; + } + tee_panic_print("Fault addr: %p | Exception origin: %s\n", addr, exc_origin); +} + +void panic_print_isrcause(const void *f, int core) +{ + RvExcFrame *regs = (RvExcFrame *) f; + + /* Please keep in sync with PANIC_RSN_* defines */ + static const char *pseudo_reason[] = { + "Unknown reason", + "Interrupt wdt timeout on CPU0", +#if SOC_CPU_NUM > 1 + "Interrupt wdt timeout on CPU1", +#endif + "Cache error", + }; + + const void *addr = (void *) regs->mepc; + const char *rsn = pseudo_reason[0]; + + /* The mcause has been set by the CPU when the panic occurred. + * All SoC-level panic will call this function, thus, this register + * lets us know which error was triggered. */ + if (regs->mcause == ETS_CACHEERR_INUM) { + /* Panic due to a cache error, multiple cache error are possible, + * assign function print_cache_err_details to our structure's + * details field. As its name states, it will give more details + * about why the error happened. */ + rsn = pseudo_reason[PANIC_RSN_CACHEERR]; + } else if (regs->mcause == ETS_INT_WDT_INUM) { + /* Watchdog interrupt occurred, get the core on which it happened + * and update the reason/message accordingly. */ +#if SOC_CPU_NUM > 1 + _Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1, + "PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1"); +#endif + rsn = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core]; + } + + const char *desc = "Exception was unhandled."; + tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). %s\n", core, rsn, desc); + + const char *exc_origin = "U-mode"; + if (regs->mstatus & MSTATUS_MPP) { + exc_origin = "M-mode"; + } + tee_panic_print("Fault addr: %p | Exception origin: %s\n", addr, exc_origin); +} diff --git a/components/esp_tee/subproject/main/core/esp_secure_service_table.c b/components/esp_tee/subproject/main/core/esp_secure_service_table.c new file mode 100644 index 000000000000..e094c3a6a493 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_secure_service_table.c @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "stddef.h" +#include "secure_service_num.h" +#include "secure_service_dec.h" + +typedef void (*secure_service_t)(void); + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverride-init" +#endif + +const secure_service_t tee_secure_service_table[] = { + [0 ... MAX_SECURE_SERVICES - 1] = (secure_service_t)NULL, + +#define __SECURE_SERVICE(nr, symbol, nargs) [nr] = (secure_service_t)_ss_##symbol, +#include "secure_service.h" +}; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/components/esp_tee/subproject/main/core/esp_secure_services.c b/components/esp_tee/subproject/main/core/esp_secure_services.c new file mode 100644 index 000000000000..79bbed2b05b3 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_secure_services.c @@ -0,0 +1,410 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_cpu.h" +#include "esp_efuse.h" +#include "hal/efuse_hal.h" +#include "hal/wdt_hal.h" + +#include "esp_rom_efuse.h" +#include "esp_flash_encrypt.h" +#include "hal/sha_hal.h" +#include "soc/soc_caps.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +#include "esp_tee_intr.h" +#include "esp_tee_aes_intr.h" +#include "esp_tee_rv_utils.h" + +#include "aes/esp_aes.h" +#include "sha/sha_dma.h" +#include "esp_tee_flash.h" +#include "esp_tee_sec_storage.h" +#include "esp_tee_ota_ops.h" +#include "esp_attestation.h" + +#define ESP_TEE_MAX_INPUT_ARG 10 + +static const char *TAG = "esp_tee_sec_srv"; + +typedef void (*secure_service_t)(void); + +extern const secure_service_t tee_secure_service_table[]; + +void _ss_invalid_secure_service(void) +{ + assert(0); +} + +/* ---------------------------------------------- Interrupts ------------------------------------------------- */ + +void _ss_esp_rom_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num) +{ + return esp_tee_route_intr_matrix(cpu_no, model_num, intr_num); +} + +void _ss_rv_utils_intr_enable(uint32_t intr_mask) +{ + rv_utils_tee_intr_enable(intr_mask); +} + +void _ss_rv_utils_intr_disable(uint32_t intr_mask) +{ + rv_utils_tee_intr_disable(intr_mask); +} + +void _ss_rv_utils_intr_set_priority(int rv_int_num, int priority) +{ + rv_utils_tee_intr_set_priority(rv_int_num, priority); +} + +void _ss_rv_utils_intr_set_type(int intr_num, enum intr_type type) +{ + rv_utils_tee_intr_set_type(intr_num, type); +} + +void _ss_rv_utils_intr_set_threshold(int priority_threshold) +{ + rv_utils_tee_intr_set_threshold(priority_threshold); +} + +void _ss_rv_utils_intr_edge_ack(uint32_t intr_num) +{ + rv_utils_tee_intr_edge_ack(intr_num); +} + +void _ss_rv_utils_intr_global_enable(void) +{ + rv_utils_tee_intr_global_enable(); +} + +/* ---------------------------------------------- eFuse ------------------------------------------------- */ + +uint32_t _ss_efuse_hal_chip_revision(void) +{ + return efuse_hal_chip_revision(); +} + +uint32_t _ss_efuse_hal_get_chip_ver_pkg(void) +{ + return efuse_hal_get_chip_ver_pkg(); +} + +bool _ss_efuse_hal_get_disable_wafer_version_major(void) +{ + return efuse_hal_get_disable_wafer_version_major(); +} + +void _ss_efuse_hal_get_mac(uint8_t *mac) +{ + efuse_hal_get_mac(mac); +} + +bool _ss_esp_efuse_check_secure_version(uint32_t secure_version) +{ + return esp_efuse_check_secure_version(secure_version); +} + +esp_err_t _ss_esp_efuse_read_field_blob(const esp_efuse_desc_t *field[], void *dst, size_t dst_size_bits) +{ + if ((field != NULL) && (field[0]->efuse_block >= EFUSE_BLK4)) { + return ESP_ERR_INVALID_ARG; + } + + return esp_efuse_read_field_blob(field, dst, dst_size_bits); +} + +bool _ss_esp_flash_encryption_enabled(void) +{ + uint32_t flash_crypt_cnt = 0; +#ifndef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH + flash_crypt_cnt = efuse_ll_get_flash_crypt_cnt(); +#else + esp_efuse_read_field_blob(ESP_EFUSE_SPI_BOOT_CRYPT_CNT, &flash_crypt_cnt, ESP_EFUSE_SPI_BOOT_CRYPT_CNT[0]->bit_count) ; +#endif + /* __builtin_parity is in flash, so we calculate parity inline */ + bool enabled = false; + while (flash_crypt_cnt) { + if (flash_crypt_cnt & 1) { + enabled = !enabled; + } + flash_crypt_cnt >>= 1; + } + return enabled; +} + +/* ---------------------------------------------- RTC_WDT ------------------------------------------------- */ + +void _ss_wdt_hal_init(wdt_hal_context_t *hal, wdt_inst_t wdt_inst, uint32_t prescaler, bool enable_intr) +{ + wdt_hal_init(hal, wdt_inst, prescaler, enable_intr); +} + +void _ss_wdt_hal_deinit(wdt_hal_context_t *hal) +{ + wdt_hal_deinit(hal); +} + +/* ---------------------------------------------- AES ------------------------------------------------- */ + +void _ss_esp_aes_intr_alloc(void) +{ + esp_tee_aes_intr_alloc(); +} + +int _ss_esp_aes_crypt_cbc(esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_aes_crypt_cbc(ctx, mode, length, iv, input, output); +} + +int _ss_esp_aes_crypt_cfb128(esp_aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_aes_crypt_cfb128(ctx, mode, length, iv_off, iv, input, output); +} + +int _ss_esp_aes_crypt_cfb8(esp_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_aes_crypt_cfb8(ctx, mode, length, iv, input, output); +} + +int _ss_esp_aes_crypt_ctr(esp_aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_aes_crypt_ctr(ctx, length, nc_off, nonce_counter, stream_block, input, output); +} + +int _ss_esp_aes_crypt_ecb(esp_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + return esp_aes_crypt_ecb(ctx, mode, input, output); +} + +int _ss_esp_aes_crypt_ofb(esp_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + return esp_aes_crypt_ofb(ctx, length, iv_off, iv, input, output); +} + +/* ---------------------------------------------- SHA ------------------------------------------------- */ + +void _ss_esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, unsigned char *output) +{ + esp_sha(sha_type, input, ilen, output); +} + +int _ss_esp_sha_dma(esp_sha_type sha_type, const void *input, uint32_t ilen, + const void *buf, uint32_t buf_len, bool is_first_block) +{ + return esp_sha_dma(sha_type, input, ilen, buf, buf_len, is_first_block); +} + +void _ss_esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state) +{ + sha_hal_read_digest(sha_type, digest_state); +} + +void _ss_esp_sha_write_digest_state(esp_sha_type sha_type, void *digest_state) +{ + sha_hal_write_digest(sha_type, digest_state); +} + +/* ---------------------------------------------- OTA ------------------------------------------------- */ + +int _ss_esp_tee_ota_begin(void) +{ + return esp_tee_ota_begin(); +} + +int _ss_esp_tee_ota_write(uint32_t rel_offset, void *data, size_t size) +{ + return esp_tee_ota_write(rel_offset, data, size); +} + +int _ss_esp_tee_ota_end(void) +{ + return esp_tee_ota_end(); +} + +/* ---------------------------------------------- Secure Storage ------------------------------------------------- */ + +esp_err_t _ss_esp_tee_sec_storage_init(void) +{ + return esp_tee_sec_storage_init(); +} + +esp_err_t _ss_esp_tee_sec_storage_gen_key(uint16_t slot_id, uint8_t key_type) +{ + return esp_tee_sec_storage_gen_key(slot_id, key_type); +} + +esp_err_t _ss_esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign) +{ + return esp_tee_sec_storage_get_signature(slot_id, hash, hlen, out_sign); +} + +esp_err_t _ss_esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey) +{ + return esp_tee_sec_storage_get_pubkey(slot_id, pubkey); +} + +esp_err_t _ss_esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return esp_tee_sec_storage_encrypt(slot_id, input, len, aad, aad_len, tag, tag_len, output); +} + +esp_err_t _ss_esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad, + uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output) +{ + return esp_tee_sec_storage_decrypt(slot_id, input, len, aad, aad_len, tag, tag_len, output); +} + +bool _ss_esp_tee_sec_storage_is_slot_empty(uint16_t slot_id) +{ + return esp_tee_sec_storage_is_slot_empty(slot_id); +} + +esp_err_t _ss_esp_tee_sec_storage_clear_slot(uint16_t slot_id) +{ + return esp_tee_sec_storage_clear_slot(slot_id); +} + +/* ---------------------------------------------- Attestation ------------------------------------------------- */ + +esp_err_t _ss_esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref, + uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len) +{ + return esp_att_generate_token(nonce, client_id, psa_cert_ref, token_buf, token_buf_size, token_len); +} + +/* ---------------------------------------------- Secure Service Dispatcher ------------------------------------------------- */ + +/** + * @brief Entry point to the TEE binary during secure service call. It decipher the call and dispatch it + * to corresponding Secure Service API in secure world. + * TODO: Fix the assembly routine here for compatibility with all levels of compiler optimizations + */ +#pragma GCC push_options +#pragma GCC optimize ("Og") + +int esp_tee_service_dispatcher(int argc, va_list ap) +{ + if (argc > ESP_TEE_MAX_INPUT_ARG) { + ESP_LOGE(TAG, "Input arguments overflow! Received %d, Permitted %d", + argc, ESP_TEE_MAX_INPUT_ARG); + return -1; + } + + int ret = -1; + void *fp_secure_service; + uint32_t argv[ESP_TEE_MAX_INPUT_ARG], *argp; + + uint32_t sid = va_arg(ap, uint32_t); + argc--; + + if (sid >= MAX_SECURE_SERVICES) { + ESP_LOGE(TAG, "Invalid Service ID!"); + va_end(ap); + return -1; + } + + fp_secure_service = (void *)tee_secure_service_table[sid]; + + for (int i = 0; i < argc; i++) { + argv[i] = va_arg(ap, uint32_t); + } + argp = &argv[0]; + va_end(ap); + + asm volatile( + "mv t0, %1 \n" + "beqz t0, service_call \n" + + "lw a0, 0(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a1, 4(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a2, 8(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a3, 12(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a4, 16(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a5, 20(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a6, 24(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "lw a7, 28(%3) \n" + "addi t0, t0, -1 \n" + "beqz t0, service_call \n" + + "addi %3, %3, 32 \n" + "mv t2, sp \n" + "loop: \n" + "lw t1, 0(%3) \n" + "sw t1, 0(t2) \n" + "addi t0, t0, -1 \n" + "addi t2, t2, 4 \n" + "addi %3, %3, 4 \n" + "bnez t0, loop \n" + + "service_call: \n" + "mv t1, %2 \n" + "jalr 0(t1) \n" + "mv %0, a0 \n" + : "=r"(ret) + : "r"(argc), "r"(fp_secure_service), "r"(argp) + : "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "t0", "t1", "t2" + ); + + return ret; +} + +#pragma GCC pop_options diff --git a/components/esp_tee/subproject/main/core/esp_tee_init.c b/components/esp_tee/subproject/main/core/esp_tee_init.c new file mode 100644 index 000000000000..88c7f804ba67 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_tee_init.c @@ -0,0 +1,186 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_macros.h" +#include "esp_log.h" +#include "esp_rom_sys.h" +#include "riscv/rv_utils.h" + +#include "esp_tee.h" +#include "multi_heap.h" + +#include "esp_tee_brownout.h" +#include "esp_tee_flash.h" +#include "bootloader_utility_tee.h" + +#if __has_include("esp_app_desc.h") +#define WITH_APP_IMAGE_INFO +#include "esp_app_desc.h" +#endif + +/* TEE symbols */ +extern uint32_t _tee_stack; +extern uint32_t _tee_intr_stack_bottom; +extern uint32_t _tee_heap_start; +extern uint32_t _tee_heap_end; +extern uint32_t _tee_bss_start; +extern uint32_t _tee_bss_end; + +extern uint32_t _sec_world_entry; +extern uint32_t _tee_s_intr_handler; + +#define TEE_HEAP_SIZE (((uint32_t)&_tee_heap_end - (uint32_t)&_tee_heap_start)) + +static const char *TAG = "esp_tee_init"; + +/* Initializes the TEE configuration structure with fields required for + * the REE-TEE interface from the TEE binary + */ +static void tee_init_app_config(void) +{ + /* TODO: Integrate these compatibility checks into the bootloader + * so it can provide fallback behavior + */ + if (esp_tee_app_config.magic_word != ESP_TEE_APP_CFG_MAGIC) { + ESP_LOGE(TAG, "Configuration structure missing from the TEE app!"); + ESP_INFINITE_LOOP(); // WDT will reset us + } + + if (esp_tee_app_config.api_major_version != ESP_TEE_API_MAJOR_VER) { + ESP_LOGE(TAG, "TEE API version mismatch: app v%d != binary v%d", + esp_tee_app_config.api_major_version, ESP_TEE_API_MAJOR_VER); + ESP_INFINITE_LOOP(); + } + + /* Set the TEE API minor version */ + esp_tee_app_config.api_minor_version = ESP_TEE_API_MINOR_VER; + + /* Set the TEE-related fields (from the TEE binary) that the REE will use to interface with TEE */ + esp_tee_app_config.s_entry_addr = &_sec_world_entry; + esp_tee_app_config.s_int_handler = &_tee_s_intr_handler; +} + +/* Print the TEE application info */ +static void tee_print_app_info(void) +{ +#ifdef WITH_APP_IMAGE_INFO + const esp_app_desc_t *app_desc = esp_app_get_description(); + ESP_LOGI(TAG, "TEE information:"); + ESP_LOGI(TAG, "Project name: %s", app_desc->project_name); + ESP_LOGI(TAG, "App version: %s", app_desc->version); +#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION + ESP_LOGI(TAG, "Secure version: %d", app_desc->secure_version); +#endif + ESP_LOGI(TAG, "Compile time: %s %s", app_desc->date, app_desc->time); + + char buf[17]; + esp_app_get_elf_sha256(buf, sizeof(buf)); + ESP_LOGI(TAG, "ELF file SHA256: %s...", buf); + ESP_LOGI(TAG, "ESP-IDF: %s", app_desc->idf_ver); +#endif +} + +/* Mark the current TEE image as valid and cancel rollback */ +static void tee_mark_app_and_valid_cancel_rollback(void) +{ + esp_partition_info_t tee_ota_info; + esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_OTA, NULL, &tee_ota_info); + if (err != ESP_OK) { + ESP_LOGD(TAG, "No TEE OTA data partition found"); + return; + } + + const esp_partition_pos_t tee_ota_pos = tee_ota_info.pos; + err = bootloader_utility_tee_mark_app_valid_and_cancel_rollback(&tee_ota_pos); + if (err != ESP_OK) { + if (err == ESP_ERR_INVALID_STATE) { + /* NOTE: App already marked valid */ + return; + } + ESP_LOGE(TAG, "Failed to cancel rollback (0x%08x)", err); + esp_rom_software_reset_system(); + } + + ESP_LOGW(TAG, "Rollback succeeded, erasing the passive TEE partition..."); + uint8_t tee_next_part = bootloader_utility_tee_get_next_update_partition(&tee_ota_pos); + esp_partition_info_t tee_next_part_info; + + int ret = esp_tee_flash_find_partition(PART_TYPE_APP, tee_next_part, NULL, &tee_next_part_info); + ret |= esp_tee_flash_erase_range(tee_next_part_info.pos.offset, tee_next_part_info.pos.size); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to find/erase the passive TEE partition!"); + return; + } +} + +void __attribute__((noreturn)) esp_tee_init(uint32_t ree_entry_addr, uint32_t ree_drom_addr, uint8_t tee_boot_part) +{ + /* Clear BSS */ + memset(&_tee_bss_start, 0, (&_tee_bss_end - &_tee_bss_start) * sizeof(_tee_bss_start)); + + static uint32_t btld_sp; + + /* Take backup of bootloader stack. */ + asm volatile("mv %0, sp" : "=r"(btld_sp)); + + /* Switch to secure world stack. */ + asm volatile("mv sp, %0" :: "r"((uint32_t)&_tee_stack)); + + /* TEE compatibility check and App config data initialization. */ + tee_init_app_config(); + + /* TEE Secure World heap initialization. */ + assert(tee_heap_register(((void *)&_tee_heap_start), TEE_HEAP_SIZE) == 0); + + /* SoC specific secure initialization. */ + esp_tee_soc_secure_sys_init(); + + /* Brownout detection initialization */ + esp_tee_brownout_init(); + + /* Switch back to bootloader stack. */ + asm volatile("mv sp, %0" :: "r"(btld_sp)); + + ESP_LOGI(TAG, "Initializing. RAM available for dynamic allocation:"); + ESP_LOGI(TAG, "At %08X len %08X (%d KiB): %s", + ((void *)&_tee_heap_start), TEE_HEAP_SIZE, TEE_HEAP_SIZE / 1024, "RAM"); + + /* Setting up the permissible flash operation address range */ + assert(esp_tee_flash_setup_prot_ctx(tee_boot_part) == ESP_OK); + + /* Setting up the running non-secure app partition as per the address provided by the bootloader */ + assert(esp_tee_flash_set_running_ree_partition(ree_drom_addr) == ESP_OK); + + tee_print_app_info(); + + /* NOTE: As image rollback is mandatorily enabled for TEE OTA, + * the most optimum checkpoint to mark the current app valid and + * cancel rollback is right before the TEE ends and is about to + * pass control to the non-secure app (see below). + */ + tee_mark_app_and_valid_cancel_rollback(); + + /* Switch to the REE and launch app */ + esp_tee_switch_to_ree(ree_entry_addr); + + /* App entry function should not return here. */ + ESP_INFINITE_LOOP(); /* WDT will reset us */ +} + +// NOTE: Remove compile-time warnings for the below newlib-provided functions +struct _reent *__getreent(void) +{ + return _GLOBAL_REENT; +} + +void _fstat_r(void) {} + +void _close_r(void) {} + +void _lseek_r(void) {} + +void _read_r(void) {} + +void _write_r(void) {} diff --git a/components/esp_tee/subproject/main/core/esp_tee_intr.c b/components/esp_tee/subproject/main/core/esp_tee_intr.c new file mode 100644 index 000000000000..2b648713488c --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_tee_intr.c @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_tee.h" +#include "esp_tee_intr.h" +#include "esp_tee_rv_utils.h" +#include "soc/periph_defs.h" +#include "soc/interrupt_reg.h" +#include "hal/cpu_ll.h" +#include "esp_cpu.h" +#include "esp_log.h" + +/* For ESP32-C6 */ +#define INTR_MAX_SOURCE (77) + +#define INTR_SET_SIZE (32) +#define INTR_SET_COUNT ((((INTR_MAX_SOURCE) + ((INTR_SET_SIZE) - 1)) & ~((INTR_SET_SIZE) - 1)) / INTR_SET_SIZE) +#define INTR_DISABLED_INUM (6) + +static const char *TAG = "esp_tee_intr"; + +typedef struct tee_handler_table_entry { + intr_handler_t handler; + void *arg; +} tee_handler_table_entry; + +static tee_handler_table_entry tee_interrupt_table[INTR_MAX_SOURCE * portNUM_PROCESSORS]; + +static uint32_t protected_sources[INTR_SET_COUNT]; + +bool esp_tee_is_intr_src_protected(int source) +{ + uint32_t base = source / INTR_SET_SIZE; + uint32_t offset = source % INTR_SET_SIZE; + + return (protected_sources[base] & (1 << offset)); +} + +void esp_tee_protect_intr_src(int source) +{ + uint32_t base = source / INTR_SET_SIZE; + uint32_t offset = source % INTR_SET_SIZE; + + protected_sources[base] |= (1 << offset); +} + +/* Default handler for unhandled interrupts */ +void tee_unhandled_interrupt(void *arg) +{ + ESP_LOGE(TAG, "Unhandled interrupt %d on cpu %d!", (int)arg, esp_cpu_get_core_id()); +} + +/* Interrupt Matrix configuration API to call from non-secure world */ +void esp_tee_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num) +{ + if (esp_tee_is_intr_src_protected(model_num) || intr_num == TEE_SECURE_INUM) { + return; + } + + if (intr_num != ETS_INVALID_INUM) { + if (intr_num == INTR_DISABLED_INUM) { + rv_utils_tee_intr_disable(BIT(intr_num)); + } else { + rv_utils_tee_intr_enable(BIT(intr_num)); + } + } + + esp_rom_route_intr_matrix(cpu_no, model_num, intr_num); +} + +/** + * This function registers a handler for the specified interrupt. The "arg" + * parameter specifies the argument to be passed to the handler when it is + * invoked. The function returns the address of the previous handler. + * On error, it returns 0. + */ +static intr_handler_t tee_set_interrupt_handler(void *arg) +{ + tee_handler_table_entry *entry; + intr_handler_t old; + vector_desc_t *vd = (vector_desc_t *)arg; + int source = vd->source; + + if (source < 0 || source >= ETS_MAX_INTR_SOURCE) { + return 0; /* invalid interrupt source */ + } + + /* Convert exception number to _xt_exception_table name */ + source = source * portNUM_PROCESSORS + esp_cpu_get_core_id(); + + entry = tee_interrupt_table + source; + old = entry->handler; + + if (vd->isr) { + entry->handler = vd->isr; + entry->arg = vd->arg; + } else { + entry->handler = &tee_unhandled_interrupt; + entry->arg = (void *)source; + } + + return ((old == &tee_unhandled_interrupt) ? 0 : old); +} + +int esp_tee_intr_register(void *arg) +{ + int cpu = esp_cpu_get_core_id(); + struct vector_desc_t *vd = (struct vector_desc_t *)arg; + + tee_set_interrupt_handler(vd); + esp_rom_route_intr_matrix(cpu, vd->source, TEE_SECURE_INUM); + + return 0; +} + +int esp_tee_intr_deregister(void *arg) +{ + int cpu = esp_cpu_get_core_id(); + struct vector_desc_t *vd = (struct vector_desc_t *)arg; + + vd->isr = NULL; + vd->arg = (void *)((int)vd->source); + tee_set_interrupt_handler(vd); + + // Setting back the default value for interrupt pin. + esp_rom_route_intr_matrix(cpu, vd->source, INTR_DISABLED_INUM); + + return 0; +} + +#define FIND_MSB_SET(n) (31 - __builtin_clz((uint32_t)(n))) +// LP_APM_M0_INTR, LP_APM_M1_INTR +#define TEE_SECURE_INT_APM_MASK_0 (0x00300000) +// EFUSE_INTR, LP_RTC_TIMER_INTR +#define TEE_SECURE_INT_MASK_0 (TEE_SECURE_INT_APM_MASK_0 | 0x0000C000) +// HP_APM_M0_INTR, HP_APM_M1_INTR, HP_APM_M2_INTR, HP_APM_M3_INTR, LP_APM0_INTR +#define TEE_SECURE_INT_APM_MASK_1 (0x000000F8) +#if !CONFIG_SECURE_TEE_TEST_MODE +#define TEE_SECURE_INT_MASK_1 (TEE_SECURE_INT_APM_MASK_1) +#else +// + TG0_T0_INTR (only for test mode) +#define TEE_SECURE_INT_MASK_1 (TEE_SECURE_INT_APM_MASK_1 | 0x00080000) +#endif +// AES_INTR, SHA_INTR, RSA_INTR, ECC_INTR +#define TEE_SECURE_INT_MASK_2 (0x00001E00) + +/* called from esp_tee_vectors.S */ +void esp_tee_global_interrupt_handler(intptr_t sp, int mcause) +{ + uint32_t status, source; + + while (1) { + if ((status = TEE_SECURE_INT_MASK_0 & + REG_READ(INTMTX_CORE0_INT_STATUS_REG_0_REG))) { + source = FIND_MSB_SET(status); + /* NOTE: With ESP-TEE, since APM violations trigger a panic, it's safe to use the mscratch + * register to pass on the stack pointer to the APM violation handler */ + if (status & TEE_SECURE_INT_APM_MASK_0) { + RV_WRITE_CSR(mscratch, sp); + } + } else if ((status = TEE_SECURE_INT_MASK_1 & + REG_READ(INTMTX_CORE0_INT_STATUS_REG_1_REG))) { + source = FIND_MSB_SET(status) + 32; + if (status & TEE_SECURE_INT_APM_MASK_1) { + RV_WRITE_CSR(mscratch, sp); + } + } else if ((status = TEE_SECURE_INT_MASK_2 & + REG_READ(INTMTX_CORE0_INT_STATUS_REG_2_REG))) { + source = FIND_MSB_SET(status) + 64; + } + + if (!status) { + break; + } + + ESP_LOGV(TAG, "Found intr src: %d", source); + tee_handler_table_entry ih = tee_interrupt_table[source]; + if (ih.handler) { + (*ih.handler)(ih.arg); + } + } +} diff --git a/components/esp_tee/subproject/main/idf_component.yml b/components/esp_tee/subproject/main/idf_component.yml new file mode 100644 index 000000000000..d2a3d2d24dbb --- /dev/null +++ b/components/esp_tee/subproject/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/json_generator: + version: "^1.1.2" + rules: + - if: "target in [esp32c6]" diff --git a/components/esp_tee/subproject/main/include/esp_tee_brownout.h b/components/esp_tee/subproject/main/include/esp_tee_brownout.h new file mode 100644 index 000000000000..1453154915df --- /dev/null +++ b/components/esp_tee/subproject/main/include/esp_tee_brownout.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void esp_tee_brownout_init(void); + +void esp_tee_brownout_disable(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/main/include/esp_tee_intr.h b/components/esp_tee/subproject/main/include/esp_tee_intr.h new file mode 100644 index 000000000000..9a7371dd75a2 --- /dev/null +++ b/components/esp_tee/subproject/main/include/esp_tee_intr.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifndef __ASSEMBLER__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Function prototype for interrupt handler function */ +typedef void (*intr_handler_t)(void *arg); + +typedef struct vector_desc_t vector_desc_t; + +struct vector_desc_t { + int source: 8; + intr_handler_t isr; + void *arg; + vector_desc_t *next; +}; + +/** + * @brief Route peripheral interrupt sources to CPU's interrupt port via interrupt matrix + * + * Since the interrupt matrix controls the secure (TEE) interrupt source mapping to the + * TEE-reserved interrupt pin, this hardware module is controlled by the TEE. + * This API is provided as a Secure Service to the REE for the configuration of + * non-secure (REE) interrupts. + * + * @param cpu_no CPU core number to route the interrupt to + * @param model_num Peripheral interrupt source number + * @param intr_num CPU external interrupt number to assign + */ +void esp_tee_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num); + +/** + * @brief Check if an interrupt source is protected from REE access + * + * @param source Peripheral interrupt source number + * @return true if the interrupt source is protected, false otherwise + */ +bool esp_tee_is_intr_src_protected(int source); + +/** + * @brief Protect an interrupt source from REE access + * + * @param source Peripheral interrupt source number + */ +void esp_tee_protect_intr_src(int source); + +/** + * @brief Register an interrupt handler + * + * @param arg Pointer to the interrupt descriptor + * @return 0 on success, non-zero on failure + */ +int esp_tee_intr_register(void *arg); + +/** + * @brief Deregister an interrupt handler + * + * @param arg Pointer to the interrupt descriptor + * @return 0 on success, non-zero on failure + */ +int esp_tee_intr_deregister(void *arg); + +#ifdef __cplusplus +} +#endif + +#endif //__ASSEMBLER__ diff --git a/components/esp_tee/subproject/main/include/multi_heap.h b/components/esp_tee/subproject/main/include/multi_heap.h new file mode 100644 index 000000000000..b61a0b0bf384 --- /dev/null +++ b/components/esp_tee/subproject/main/include/multi_heap.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include +#include "esp_tee.h" +#include "tlsf.h" + +/* multi_heap is a heap implementation for handling multiple + heterogeneous heaps in a single program. + + Any contiguous block of memory can be registered as a heap. +*/ + +#ifdef __cplusplus +extern "C" { +#endif +struct multi_heap_info { + void *lock; + size_t free_bytes; + size_t minimum_free_bytes; + size_t pool_size; + void *heap_data; +}; + +typedef struct multi_heap_info heap_t; + +/** @brief Opaque handle to a registered heap */ +typedef struct multi_heap_info *multi_heap_handle_t; + +/** @brief malloc() a buffer in a given heap + * + * Semantics are the same as standard malloc(), only the returned buffer will be allocated in the TEE heap. + * + * @param size Size of desired buffer. + * + * @return Pointer to new memory, or NULL if allocation fails. + */ +void *tee_heap_malloc(size_t size); + +/** @brief calloc() a buffer in a given heap + * + * Semantics are the same as standard calloc(), only the returned buffer will be allocated in the TEE heap. + * + * @param size Size of desired buffer. + * + * @return Pointer to new memory, or NULL if allocation fails. + */ +void *tee_heap_calloc(size_t n, size_t size); + +/** + * @brief allocate a chunk of memory with specific alignment + * + * @param heap Handle to a registered heap. + * @param size size in bytes of memory chunk + * @param alignment how the memory must be aligned + * + * @return pointer to the memory allocated, NULL on failure + */ +void *tee_heap_aligned_alloc(size_t size, size_t alignment); + +/** @brief free() a buffer in a given heap. + * + * Semantics are the same as standard free(), only the argument 'p' must be NULL or have been allocated in the TEE heap. + * + * @param p NULL, or a pointer previously returned from multi_heap_malloc() or multi_heap_realloc() for the same heap. + */ +void tee_heap_free(void *p); + +/** @brief Register a new heap for use + * + * This function initialises a heap at the specified address, and returns a handle for future heap operations. + * + * There is no equivalent function for deregistering a heap - if all blocks in the heap are free, you can immediately start using the memory for other purposes. + * + * @param start Start address of the memory to use for a new heap. + * @param size Size (in bytes) of the new heap. + * + * @return Handle of a new heap ready for use, or NULL if the heap region was too small to be initialised. + */ +int tee_heap_register(void *start, size_t size); + +/** + * @brief Dump free and minimum free TEE heap information to stdout + * + */ +void tee_heap_dump_free_size(void); + +/** @brief Dump TEE heap information to stdout + * + * For debugging purposes, this function dumps information about every block in the heap to stdout. + * + */ +void tee_heap_dump_info(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in b/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in new file mode 100644 index 000000000000..95646e3867f7 --- /dev/null +++ b/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#define SRAM_IRAM_START (0x40800000) +#define SRAM_IRAM_TEE_ORG (SRAM_IRAM_START) +#define SRAM_DRAM_TEE_ORG (SRAM_IRAM_START) + +#define SRAM_IRAM_ORG (SRAM_IRAM_TEE_ORG + CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) + +#define SRAM_DRAM_TEE_SIZE (CONFIG_SECURE_TEE_DRAM_SIZE - CONFIG_SECURE_TEE_STACK_SIZE - CONFIG_SECURE_TEE_INTR_STACK_SIZE) +#define SRAM_DRAM_TEE_END (SRAM_DRAM_TEE_ORG + CONFIG_SECURE_TEE_IRAM_SIZE + SRAM_DRAM_TEE_SIZE) + +#define I_D_SRAM_TEE_SIZE (CONFIG_SECURE_TEE_IRAM_SIZE + SRAM_DRAM_TEE_SIZE) + +/* TEE interrupt stack is placed at the end of the TEE DRAM segment. + * The top of the TEE stack is before the end of interrupt stack + * and the bottom of the stack is at _heap_end. + */ +#define SRAM_STACK_TEE_ORG (SRAM_DRAM_TEE_END) +#define SRAM_INTR_STACK_TEE_ORG (SRAM_DRAM_TEE_END + CONFIG_SECURE_TEE_STACK_SIZE) + +#define FLASH_IROM_TEE_ORG (0x42000000) +#define FLASH_DROM_TEE_ORG (0x42000000) +#define I_D_FLASH_TEE_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) + +/** + * These values are the same in every app binary for the same chip target. + * + * Values that may change when the app is rebuilt, or in a new ESP-IDF version, + * should be stored via esp_app_tee_config structure + */ + +#if CONFIG_ESP_DEBUG_INCLUDE_OCD_STUB_BINS +PROVIDE ( esp_tee_app_config = SRAM_IRAM_ORG + 0x22e0 ); +#else +PROVIDE ( esp_tee_app_config = SRAM_IRAM_ORG + 0x2e0 ); +#endif + +PROVIDE ( GDMA = 0x60080000 ); + +/* Default entry point: */ +ENTRY(esp_tee_init); + +MEMORY +{ +/* IRAM Configuration */ + iram_tee_seg (RX) : org = SRAM_IRAM_TEE_ORG, len = I_D_SRAM_TEE_SIZE + +/* DRAM Configuration */ + dram_tee_seg (RW) : org = SRAM_DRAM_TEE_ORG, len = I_D_SRAM_TEE_SIZE + +/* TEE Stack Configuration */ + stack_tee_seg (RW) : org = SRAM_STACK_TEE_ORG, len = CONFIG_SECURE_TEE_STACK_SIZE + +/* TEE Interrupt Stack Configuration */ + intr_stack_tee_seg (RW) : org = SRAM_INTR_STACK_TEE_ORG, len = CONFIG_SECURE_TEE_INTR_STACK_SIZE + +/* TEE flash data section */ + flash_data_seg (R) : org = FLASH_DROM_TEE_ORG + 0x20, len = I_D_FLASH_TEE_SIZE - 0x20 + +/* TEE flash text section */ + flash_text_seg (RX): org = FLASH_IROM_TEE_ORG + 0x20, len = I_D_FLASH_TEE_SIZE - 0x20 +} + +SECTIONS +{ + /** + * This section is required to skip .iram.tee_text area because iram_tee_seg and + * dram_tee_seg reflect the same address space on different buses. + */ + .dram.tee_dummy (NOLOAD): + { + . = ORIGIN(dram_tee_seg) + CONFIG_SECURE_TEE_IRAM_SIZE; + } > dram_tee_seg + + .dram.tee.bss (NOLOAD) : + { + . = ALIGN (8); + _tee_dram_start = ABSOLUTE(.); + _tee_bss_start = ABSOLUTE(.); + *(.bss .bss.*) + *(.sbss .sbss.*) + . = ALIGN (8); + _tee_bss_end = ABSOLUTE(.); + } > dram_tee_seg + + .dram.tee.data : + { + _data_start = ABSOLUTE(.); + *(.data .data.*) + *(.sdata .sdata.*) + *(.dram1 .dram1.*) + . = ALIGN(4); + _data_end = ABSOLUTE(.); + } > dram_tee_seg + + .dram.tee.rodata : + { + _rodata_start = ABSOLUTE(.); + *libtee_flash_mgr.a:*(.rodata .srodata .rodata.* .srodata.*) + *libbootloader_support.a:bootloader_flash.*(.rodata .srodata .rodata.* .srodata.*) + *libmain.a:panic_helper_riscv.*(.rodata .srodata .rodata.* .srodata.*) + _rodata_end = ABSOLUTE(.); + _tee_dram_end = ABSOLUTE(.); + } > dram_tee_seg + + .dram.tee.heap : + { + . = ALIGN (8); + _tee_heap_start = ABSOLUTE(.); + . = ORIGIN(dram_tee_seg) + LENGTH(dram_tee_seg); + _tee_heap_end = ABSOLUTE(.); + } > dram_tee_seg + + .dram.tee.stack : + { + . = ALIGN (8); + _tee_stack_bottom = ABSOLUTE(.); + . = ORIGIN(stack_tee_seg) + LENGTH(stack_tee_seg); + . = ALIGN (8); + _tee_stack = ABSOLUTE(.); + } > stack_tee_seg + + .dram.tee.intr_stack : + { + . = ALIGN (8); + _tee_intr_stack_bottom = ABSOLUTE(.); + . = ORIGIN(intr_stack_tee_seg) + LENGTH(intr_stack_tee_seg); + . = ALIGN (8); + _tee_intr_stack = ABSOLUTE(.); + } > intr_stack_tee_seg + + .flash.rodata : + { + _tee_xip_data_start = ABSOLUTE(.); + *(.rodata_desc .rodata_desc.*) /* Should be the first. TEE App version info. DO NOT PUT ANYTHING BEFORE IT! */ + *(.rodata .rodata.*) + *(.srodata .srodata.*) + _tee_xip_data_end = ABSOLUTE(.); + } > flash_data_seg + + .flash.text_dummy (NOLOAD): + { + . = ALIGN(ALIGNOF(.flash.rodata)); + /* Create an empty gap as big as .flash.rodata section */ + . = . + SIZEOF(.flash.rodata); + /* Prepare the alignment of the section above. Few bytes (0x20) must be + * added for the mapping header. */ + . = ALIGN(CONFIG_MMU_PAGE_SIZE) + 0x20; + } > flash_text_seg + + .flash.text : + { + _tee_xip_text_start = ABSOLUTE(.); + /* Mbedtls for TEE */ + *libmbedtls.a:*(.literal .text .literal.* .text.*) + *libmbedcrypto.a:*(.literal .text .literal.* .text.*) + /* TEE attestation module */ + *libattestation.a:*(.literal .text .literal.* .text.*) + *json_generator.a:*(.literal .text .literal.* .text.*) + /* TEE test module */ + *libtest_sec_srv.a:*(.literal .text .literal.* .text.*) + _tee_xip_text_end = ABSOLUTE(.); + _tee_xip_end = ABSOLUTE(.); + } > flash_text_seg + + .iram.tee.text : + { + . = ALIGN(4); + /* Vectors go to start of IRAM */ + ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned"); + _tee_vec_start = ABSOLUTE(.); + KEEP(*(.exception_vectors_table.text)); + KEEP(*(.exception_vectors.text)); + . = ALIGN(4); + _invalid_pc_placeholder = ABSOLUTE(.); + _tee_vec_end = ABSOLUTE(.); + + _tee_iram_start = ABSOLUTE(.); + *(.literal .text .iram1 .literal.* .text.* .iram1.*) + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + _tee_iram_end = ABSOLUTE(.); + } > iram_tee_seg + + .riscv.attributes 0: { *(.riscv.attributes) } + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_pubtypes 0 : { *(.debug_pubtypes) } + /* DWARF 3 */ + .debug_ranges 0 : { *(.debug_ranges) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* GNU DWARF 2 extensions */ + .debug_gnu_pubnames 0 : { *(.debug_gnu_pubnames) } + .debug_gnu_pubtypes 0 : { *(.debug_gnu_pubtypes) } + /* DWARF 4 */ + .debug_types 0 : { *(.debug_types) } + /* DWARF 5 */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + + .comment 0 : { *(.comment) } + .note.GNU-stack 0: { *(.note.GNU-stack) } + + /** + * Discarding .rela.* sections results in the following mapping: + * .rela.text.* -> .text.* + * .rela.data.* -> .data.* + * And so forth... + */ + /DISCARD/ : { *(.rela.*) } +} + +ASSERT ((_tee_iram_end < _tee_dram_start), "Error: TEE IRAM segment overflowed into the DRAM segment!"); diff --git a/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake b/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake new file mode 100644 index 000000000000..e7569ed498f3 --- /dev/null +++ b/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# + +idf_build_get_property(target IDF_TARGET) +idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER) +idf_build_get_property(config_dir CONFIG_DIR) + +# -------------------------------- esp_tee.ld -------------------------------- + +set(ld_input "${CMAKE_CURRENT_LIST_DIR}/${target}/esp_tee.ld.in") +set(ld_output "${CMAKE_CURRENT_BINARY_DIR}/ld/esp_tee.ld") + +target_linker_script(${COMPONENT_LIB} INTERFACE "${ld_output}") + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/ld") + +# Preprocess esp_tee.ld.in linker script to include configuration, becomes esp_tee.ld +add_custom_command( + OUTPUT ${ld_output} + COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o ${ld_output} -I ${config_dir} + -I "${CMAKE_CURRENT_LIST_DIR}" ${ld_input} + MAIN_DEPENDENCY ${ld_input} + DEPENDS ${sdkconfig_header} + COMMENT "Generating esp_tee.ld linker script..." + VERBATIM) + +add_custom_target(esp_tee_ld DEPENDS ${ld_output}) +add_dependencies(${COMPONENT_LIB} esp_tee_ld) diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c new file mode 100644 index 000000000000..9c23c5392438 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "hal/aes_hal.h" +#include "soc/interrupts.h" +#include "esp_attr.h" + +#include "esp_tee_intr.h" +#include "esp_tee_aes_intr.h" + +volatile DRAM_ATTR bool intr_flag; + +static IRAM_ATTR void esp_tee_aes_complete_isr(void *arg) +{ + aes_hal_interrupt_clear(); + intr_flag = false; +} + +void esp_tee_aes_intr_alloc(void) +{ + struct vector_desc_t aes_vd = { 0, NULL, NULL, NULL}; + + aes_vd.source = ETS_AES_INTR_SOURCE; + aes_vd.isr = esp_tee_aes_complete_isr; + + esp_tee_intr_register((void *)&aes_vd); + intr_flag = false; +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c new file mode 100644 index 000000000000..95ddb92e323d --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "hal/apm_ll.h" +#include "hal/apm_hal.h" + +#include "esp_tee.h" +#include "esp_tee_apm_intr.h" + +const char *esp_tee_apm_excp_mid_to_str(uint8_t mid) +{ + char *excp_mid = NULL; + + switch (mid) { + case APM_LL_MASTER_HPCORE: + excp_mid = "HPCORE"; + break; + case APM_LL_MASTER_LPCORE: + excp_mid = "LPCORE"; + break; + case APM_LL_MASTER_REGDMA: + excp_mid = "REGDMA"; + break; + case APM_LL_MASTER_SDIOSLV: + excp_mid = "SDIOSLV"; + break; + case APM_LL_MASTER_MODEM: + excp_mid = "MODEM"; + break; + case APM_LL_MASTER_MEM_MONITOR: + excp_mid = "MEM_MONITOR"; + break; + case APM_LL_MASTER_TRACE: + excp_mid = "TRACE"; + break; + case APM_LL_MASTER_GDMA_SPI2: + excp_mid = "GDMA_SPI2"; + break; + case APM_LL_MASTER_GDMA_UHCI0: + excp_mid = "GDMA_UHCI0"; + break; + case APM_LL_MASTER_GDMA_I2S0: + excp_mid = "GDMA_I2S0"; + break; + case APM_LL_MASTER_GDMA_AES: + excp_mid = "GDMA_AES"; + break; + case APM_LL_MASTER_GDMA_SHA: + excp_mid = "GDMA_SHA"; + break; + case APM_LL_MASTER_GDMA_ADC: + excp_mid = "GDMA_ADC"; + break; + case APM_LL_MASTER_GDMA_PARLIO: + excp_mid = "GDMA_PARLIO"; + break; + default: + excp_mid = "Unknown"; + break; + } + + return excp_mid; +} + +const char *esp_tee_apm_excp_type_to_str(uint8_t type) +{ + char *excp_type = "Unknown exception"; + + if (type & 0x01) { + excp_type = "Authority exception"; + } else if (type & 0x02) { + excp_type = "Space exception"; + } + + return excp_type; +} + +const char *esp_tee_apm_excp_ctrl_to_str(apm_ll_apm_ctrl_t apm_ctrl) +{ + char *excp_ctrl = NULL; + + switch (apm_ctrl) { + case LP_APM0_CTRL: + excp_ctrl = "LP_APM0"; + break; + case HP_APM_CTRL: + excp_ctrl = "HP_APM"; + break; + case LP_APM_CTRL: + excp_ctrl = "LP_APM"; + break; + default: + excp_ctrl = "Unknown"; + break; + } + + return excp_ctrl; +} + +const char *esp_tee_apm_excp_mode_to_str(uint8_t mode) +{ + char *excp_mode = NULL; + + switch (mode) { + case APM_LL_SECURE_MODE_TEE: + case APM_LL_SECURE_MODE_REE0: + excp_mode = "REE0"; + break; + case APM_LL_SECURE_MODE_REE1: + excp_mode = "REE1"; + break; + case APM_LL_SECURE_MODE_REE2: + excp_mode = "REE2"; + break; + default: + excp_mode = "Unknown"; + break; + } + + return excp_mode; +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c new file mode 100644 index 000000000000..c5c3e645c843 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_log.h" +#include "esp_tee.h" +#include "esp_tee_intr.h" + +#include "hal/apm_hal.h" +#include "soc/soc.h" +#include "soc/spi_mem_reg.h" +#include "soc/efuse_reg.h" + +extern void tee_apm_violation_isr(void *arg); + +static const char *TAG = "esp_tee_apm_prot_cfg"; + +/* NOTE: Figuring out the eFuse protection range based on where the TEE secure storage key is stored */ +#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE +#if CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK > 9 +#error "TEE: eFuse protection region for APM out of range! (see CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK)" +#endif +#define LP_APM_EFUSE_REG_START \ + (EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20)) + +#define LP_APM_EFUSE_REG_END \ + (EFUSE_RD_KEY1_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20)) +#elif CONFIG_SECURE_TEE_SEC_STG_MODE_DEVELOPMENT +#define LP_APM_EFUSE_REG_START EFUSE_RD_KEY5_DATA0_REG +#if CONFIG_SECURE_TEE_TEST_MODE +#define LP_APM_EFUSE_REG_END EFUSE_RD_SYS_PART2_DATA0_REG +#else +#define LP_APM_EFUSE_REG_END LP_APM_EFUSE_REG_START +#endif +#endif + +/*----------------HP APM Configuration-----------------------*/ + +/* HP APM Range and Filter configuration. */ +apm_ctrl_region_config_data_t hp_apm_pms_data[] = { + /* Region0: LP memory region access. (RWX)*/ + { + .regn_num = 0, + .regn_start_addr = SOC_RTC_IRAM_LOW, + .regn_end_addr = SOC_RTC_IRAM_HIGH, + .regn_pms = 0x7, + .filter_enable = 1, + }, + /* Peripherals region access.(RW)*/ + /* Region1: Next 2 entries for MMU peripheral protection. */ + { + .regn_num = 1, + .regn_start_addr = SOC_PERIPHERAL_LOW, + .regn_end_addr = (DR_REG_INTERRUPT_MATRIX_BASE - 0x4), + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region4: Interrupt Matrix protection. */ + { + .regn_num = 2, + .regn_start_addr = DR_REG_ATOMIC_BASE, + .regn_end_addr = (DR_REG_AES_BASE - 0x4), + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region5: Peripherals region access. (RW)*/ + { + .regn_num = 3, + .regn_start_addr = DR_REG_RSA_BASE, + .regn_end_addr = (DR_REG_TEE_BASE - 0x4), + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region6: Peripherals region access. (RW)*/ + { + .regn_num = 4, + .regn_start_addr = DR_REG_MISC_BASE, + .regn_end_addr = (DR_REG_PMU_BASE - 0x04), + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region7: Peripherals region access. (RW)*/ + { + .regn_num = 5, + .regn_start_addr = DR_REG_OPT_DEBUG_BASE, + .regn_end_addr = 0x600D0000, //PWDET_CONF_REG, + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region8: IRAM region access. (RW)*/ + { + .regn_num = 6, + .regn_start_addr = SOC_NS_IRAM_START, + .regn_end_addr = SOC_IRAM_HIGH, + .regn_pms = 0x6, + .filter_enable = 1, + }, +}; + +/* NOTE: Following are the master IDs for setting the security mode and access through APM: + * +---------+-------------+ + * | Bit | Source | + * +---------+-------------+ + * | 0 | HP CPU | + * | 1 | LP CPU | + * | 2 | reserved | + * | 3 | SDIO_SLV | + * | 4 | reserved | + * | 5 | MEM_MONITOR | + * | 6 | TRACE | + * | 7~15 | reserved | + * | 16 | SPI2 | + * | 17 | Dummy-1 | + * | 18 | UHCI | + * | 19 | I2S | + * | 20 | Dummy-4 | + * | 21 | Dummy-5 | + * | 22 | AES | + * | 23 | SHA | + * | 24 | ADC | + * | 25 | PARLIO | + * | 26~31 | Dummy-10~15 | + * +---------+-------------+ + */ + +/* HP APM configuration for the Masters. */ +apm_ctrl_secure_mode_config_t hp_apm_sec_mode_data = { + .apm_ctrl = HP_APM_CTRL, + .apm_m_cnt = HP_APM_MAX_ACCESS_PATH, + .sec_mode = APM_LL_SECURE_MODE_REE0, + /* Except crypto DMA (AES and SHA) and HP CPU, all other masters will be switch to REE0 mode - refer above note */ + .master_ids = 0xFF3FFFFE, + .pms_data = hp_apm_pms_data, +}; + +/*----------------HP APM TEE Configuration-----------------------*/ + +/* HP APM Range and Filter configuration. */ +apm_ctrl_region_config_data_t hp_apm_pms_data_tee[] = { + /* Region9: TEE All access enable. (RW)*/ + { + .regn_num = 7, + .regn_start_addr = 0x0, + .regn_end_addr = ~0x0, + .regn_pms = 0x7, + .filter_enable = 1, + }, +}; + +apm_ctrl_secure_mode_config_t hp_apm_sec_mode_data_tee = { + .apm_ctrl = HP_APM_CTRL, + .apm_m_cnt = HP_APM_MAX_ACCESS_PATH, + .sec_mode = APM_LL_SECURE_MODE_TEE, + /* Crypto DMA will be switch to TEE mode - refer above note*/ + .master_ids = 0xC00001, + .pms_data = hp_apm_pms_data_tee, +}; + +/*----------------LP APM0 Configuration-----------------------*/ + +/* LP APM0 Range and Filter configuration. */ +apm_ctrl_region_config_data_t lp_apm0_pms_data[] = { + /* Region0: LP memory. (RWX) */ + { + .regn_num = 0, + .regn_start_addr = SOC_RTC_IRAM_LOW, + .regn_end_addr = SOC_RTC_IRAM_HIGH, + .regn_pms = 0x7, + .filter_enable = 1, + }, +}; + +/* LP APM0 configuration for the Masters. */ +apm_ctrl_secure_mode_config_t lp_apm0_sec_mode_data = { + .apm_ctrl = LP_APM0_CTRL, + .apm_m_cnt = LP_APM0_MAX_ACCESS_PATH, + .sec_mode = APM_LL_SECURE_MODE_REE0, + .master_ids = 0x2, //Only LP_CPU here. + .pms_data = lp_apm0_pms_data, +}; + +/*----------------LP APM Configuration-----------------------*/ + +/* LP APM Range and Filter configuration. */ +apm_ctrl_region_config_data_t lp_apm_pms_data[] = { + /* Region0: LP memory. (RWX) */ + { + .regn_num = 0, + .regn_start_addr = SOC_RTC_IRAM_LOW, + .regn_end_addr = SOC_RTC_IRAM_HIGH, + .regn_pms = 0x7, + .filter_enable = 1, + }, + /* Region1: LP Peri 1. (RW) */ + { + .regn_num = 1, + .regn_start_addr = DR_REG_PMU_BASE, + .regn_end_addr = (LP_APM_EFUSE_REG_START - 0x04), + .regn_pms = 0x6, + .filter_enable = 1, + }, + /* Region2: LP Peri 2. (RW) */ + { + .regn_num = 2, + .regn_start_addr = LP_APM_EFUSE_REG_END, + .regn_end_addr = (DR_REG_TRACE_BASE - 0x04), + .regn_pms = 0x6, + .filter_enable = 1, + }, +}; + +/* LP APM configuration for the Masters. */ +apm_ctrl_secure_mode_config_t lp_apm_sec_mode_data = { + .apm_ctrl = LP_APM_CTRL, + .apm_m_cnt = LP_APM_MAX_ACCESS_PATH, + .sec_mode = APM_LL_SECURE_MODE_REE0, + .master_ids = 0x3, //Only HP_CPU & LP_CPU here. + .pms_data = lp_apm_pms_data, +}; + +/* LP APM Range and Filter configuration. */ +apm_ctrl_region_config_data_t lp_apm_pms_data_tee[] = { + /* Region3: TEE All access enable. (RW)*/ + { + .regn_num = 3, + .regn_start_addr = 0x0, + .regn_end_addr = ~0x0, + .regn_pms = 0x7, + .filter_enable = 1, + }, +}; + +apm_ctrl_secure_mode_config_t lp_apm_sec_mode_data_tee = { + .apm_ctrl = LP_APM_CTRL, + .apm_m_cnt = LP_APM_MAX_ACCESS_PATH, + .sec_mode = APM_LL_SECURE_MODE_TEE, + /* HP CPU and LP CPU will be switch to TEE mode */ + .master_ids = 0x00003, + .pms_data = lp_apm_pms_data_tee, +}; + +/*---------------- TEE APM API-----------------------*/ + +void esp_tee_apm_int_enable(apm_ctrl_secure_mode_config_t *sec_mode_data) +{ + for (int i = 0; i < sec_mode_data->apm_m_cnt; i++) { + apm_ctrl_path_t *apm_excp_data = calloc(1, sizeof(apm_ctrl_path_t)); + assert(apm_excp_data != NULL); + + apm_excp_data->apm_ctrl = sec_mode_data->apm_ctrl; + apm_excp_data->apm_m_path = i; + + int intr_src_num = apm_hal_apm_ctrl_get_int_src_num(apm_excp_data); + + struct vector_desc_t apm_vd = {0}; + apm_vd.source = intr_src_num; + apm_vd.isr = tee_apm_violation_isr; + apm_vd.arg = (void *)apm_excp_data; + + /* Register interrupt handler with TEE. */ + esp_tee_intr_register((void *)&apm_vd); + + /* Enable APM Ctrl intewrrupt for access path(M[0:n]) */ + apm_hal_apm_ctrl_exception_clear(apm_excp_data); + apm_hal_apm_ctrl_interrupt_enable(apm_excp_data, true); + } +} + +void esp_tee_configure_apm_protection(void) +{ + /* Disable all control filter first to have full access of address rage. */ + apm_hal_apm_ctrl_filter_enable_all(false); + + /* LP APM0 configuration. */ + lp_apm0_sec_mode_data.regn_count = sizeof(lp_apm0_pms_data) / sizeof(apm_ctrl_region_config_data_t); + apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm0_sec_mode_data); + + /* LP APM0 interrupt configuration. */ + esp_tee_apm_int_enable(&lp_apm0_sec_mode_data); + ESP_LOGD(TAG, "[REE0] LP_APM0 configured"); + + /* LP APM TEE configuration. */ + lp_apm_sec_mode_data_tee.regn_count = sizeof(lp_apm_pms_data_tee) / sizeof(apm_ctrl_region_config_data_t); + apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm_sec_mode_data_tee); + ESP_LOGD(TAG, "[TEE] LP_APM configured"); + + /* LP APM configuration. */ + lp_apm_sec_mode_data.regn_count = sizeof(lp_apm_pms_data) / sizeof(apm_ctrl_region_config_data_t); + apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm_sec_mode_data); + /* LP APM interrupt configuration. */ + esp_tee_apm_int_enable(&lp_apm_sec_mode_data); + ESP_LOGD(TAG, "[REE0] LP_APM configured"); + + /* HP APM TEE configuration. */ + hp_apm_sec_mode_data_tee.regn_count = sizeof(hp_apm_pms_data_tee) / sizeof(apm_ctrl_region_config_data_t); + apm_hal_apm_ctrl_master_sec_mode_config(&hp_apm_sec_mode_data_tee); + ESP_LOGD(TAG, "[TEE] HP_APM configured"); + + /* HP APM configuration. */ + hp_apm_sec_mode_data.regn_count = sizeof(hp_apm_pms_data) / sizeof(apm_ctrl_region_config_data_t); + apm_hal_apm_ctrl_master_sec_mode_config(&hp_apm_sec_mode_data); + /* HP APM interrupt configuration. */ + esp_tee_apm_int_enable(&hp_apm_sec_mode_data); + ESP_LOGD(TAG, "[REE0] HP_APM configured"); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c new file mode 100644 index 000000000000..af6cf5e20bd5 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/ext_mem_defs.h" +#include "soc/lp_analog_peri_reg.h" +#include "soc/lp_wdt_reg.h" +#include "riscv/csr.h" +#include "esp_cpu.h" +#include "esp_fault.h" +#include "esp_tee.h" + +#define CONDITIONAL_NONE 0x0 +#define CONDITIONAL_R PMP_R +#define CONDITIONAL_RX PMP_R | PMP_X +#define CONDITIONAL_RW PMP_R | PMP_W +#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X + +#define IS_PMA_ENTRY_UNLOCKED(ENTRY) \ + ((RV_READ_CSR((CSR_PMACFG0) + (ENTRY)) & PMA_L) == 0) + +#define SWD_PROT_REG_START (LP_WDT_SWD_CONFIG_REG) +#define SWD_PROT_REG_END (LP_WDT_INT_CLR_REG) +#define BOD_PROT_REG_START (DR_REG_LP_ANALOG_PERI_BASE) +#define BOD_PROT_REG_END (DR_REG_LP_ANALOG_PERI_BASE + 0x40) + +static void esp_tee_configure_invalid_regions(void) +{ + const unsigned PMA_NONE = PMA_L | PMA_EN; + __attribute__((unused)) const unsigned PMA_RW = PMA_L | PMA_EN | PMA_R | PMA_W; + __attribute__((unused)) const unsigned PMA_RX = PMA_L | PMA_EN | PMA_R | PMA_X; + __attribute__((unused)) const unsigned PMA_RWX = PMA_L | PMA_EN | PMA_R | PMA_W | PMA_X; + + // 1. Gap at bottom of address space + PMA_RESET_AND_ENTRY_SET_TOR(0, SOC_CPU_SUBSYSTEM_LOW, PMA_NONE); + + // 2. Gap between CPU subsystem region & IROM + PMA_RESET_AND_ENTRY_SET_TOR(1, SOC_CPU_SUBSYSTEM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(2, SOC_IROM_MASK_LOW, PMA_TOR | PMA_NONE); + + // 3. Gap between ROM & RAM + PMA_RESET_AND_ENTRY_SET_TOR(3, SOC_DROM_MASK_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(4, SOC_IRAM_LOW, PMA_TOR | PMA_NONE); + + // 4. Gap between DRAM and I_Cache + PMA_RESET_AND_ENTRY_SET_TOR(5, SOC_IRAM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(6, SOC_IROM_LOW, PMA_TOR | PMA_NONE); + + // 5. Gap between D_Cache & LP_RAM + PMA_RESET_AND_ENTRY_SET_TOR(7, SOC_DROM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(8, SOC_RTC_IRAM_LOW, PMA_TOR | PMA_NONE); + + // 6. Gap between LP memory & peripheral addresses + PMA_RESET_AND_ENTRY_SET_TOR(9, SOC_RTC_IRAM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(10, SOC_PERIPHERAL_LOW, PMA_TOR | PMA_NONE); + + // 7. End of address space + PMA_RESET_AND_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(12, UINT32_MAX, PMA_TOR | PMA_NONE); + + // 8. Using PMA to configure the TEE text and data section access attribute. */ + assert(IS_PMA_ENTRY_UNLOCKED(13)); + assert(IS_PMA_ENTRY_UNLOCKED(14)); + assert(IS_PMA_ENTRY_UNLOCKED(15)); + PMA_RESET_AND_ENTRY_SET_TOR(13, SOC_S_IRAM_START, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(14, SOC_S_IRAM_END, PMA_TOR | PMA_RX); + PMA_RESET_AND_ENTRY_SET_TOR(15, SOC_S_DRAM_END, PMA_TOR | PMA_RW); +} + +void esp_tee_configure_region_protection(void) +{ + /* Notes on implementation: + * + * 1) Note: ESP32-C6 CPU doesn't support overlapping PMP regions + * + * 2) ESP32-C6 supports 16 PMA regions so we use this feature to block all the invalid address ranges + * + * 3) We use combination of NAPOT (Naturally Aligned Power Of Two) and TOR (top of range) + * entries to map all the valid address space, bottom to top. This leaves us with some extra PMP entries + * which can be used to provide more granular access + * + * 4) Entries are grouped in order with some static asserts to try and verify everything is + * correct. + */ + const unsigned NONE = PMP_L; + const unsigned R = PMP_L | PMP_R; + const unsigned RW = PMP_L | PMP_R | PMP_W; + const unsigned RX = PMP_L | PMP_R | PMP_X; + const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; + + // + // Configure all the invalid address regions using PMA + // + // We lock the PMA entries since they mark the invalid regions and is applicable to both the privilege modes + // + esp_tee_configure_invalid_regions(); + + // + // Configure all the valid address regions using PMP + // + // We are not locking the PMP entries so these permission configurations do not apply to M mode + // + + // 1.1 I/D-ROM + PMP_ENTRY_SET(0, SOC_IROM_MASK_LOW, NONE); + PMP_ENTRY_SET(1, SOC_IROM_MASK_HIGH, PMP_TOR | RX); + _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); + + /* TODO: Check whether changes are required here */ + if (esp_cpu_dbgr_is_attached()) { + // Anti-FI check that cpu is really in ocd mode + ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached()); + + // 2. IRAM and DRAM + const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH); + PMP_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RWX); + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); + } else { + // 2. IRAM and DRAM + // Splitting the REE SRAM region into IRAM and DRAM + PMP_ENTRY_SET(2, (int)SOC_NS_IRAM_START, NONE); + PMP_ENTRY_SET(3, (int)esp_tee_app_config.ns_iram_end, PMP_TOR | RX); + PMP_ENTRY_SET(4, SOC_DRAM_HIGH, PMP_TOR | RW); + } + + const uint32_t s_irom_resv_end = SOC_IROM_LOW + CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE; + const uint32_t ns_irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)esp_tee_app_config.ns_irom_end); + const uint32_t ns_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)esp_tee_app_config.ns_drom_end); + const uint32_t ns_drom_mmap_end = (uint32_t)(SOC_S_MMU_MMAP_RESV_START_VADDR); + + // 4. I_Cache / D_Cache (flash) - REE + PMP_ENTRY_CFG_RESET(5); + PMP_ENTRY_CFG_RESET(6); + PMP_ENTRY_CFG_RESET(7); + PMP_ENTRY_CFG_RESET(8); + PMP_ENTRY_SET(5, s_irom_resv_end, NONE); + PMP_ENTRY_SET(6, ns_irom_resv_end, PMP_TOR | RX); + PMP_ENTRY_SET(7, ns_drom_resv_end, PMP_TOR | R); + PMP_ENTRY_SET(8, ns_drom_mmap_end, PMP_TOR | RX); + + // 5. LP memory + /* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits + * Bootloader might have given extra permissions and those won't be cleared + */ + const uint32_t pmpaddr9 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); + + // 6. Super WDT and Brownout detector + PMP_ENTRY_SET(10, SWD_PROT_REG_START, CONDITIONAL_NONE); + PMP_ENTRY_SET(11, SWD_PROT_REG_END, PMP_TOR | CONDITIONAL_NONE); + _Static_assert(SWD_PROT_REG_START < SWD_PROT_REG_END, "Invalid peripheral region"); + + /* NOTE: Due to the limited number of PMP entries, NAPOT address matching had to be + * utilized here. To meet the requirements of NAPOT, the adjacent 20 bytes have also + * been protected along with the intended region. + */ + const uint32_t pmpaddr12 = PMPADDR_NAPOT(BOD_PROT_REG_START, BOD_PROT_REG_END); + PMP_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | CONDITIONAL_NONE); + _Static_assert(BOD_PROT_REG_START < BOD_PROT_REG_END, "Invalid peripheral region"); + + // 7. Peripheral addresses + const uint32_t pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | RW); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); + + // 8. User-mode interrupt controller registers + const uint32_t pmpaddr14 = PMPADDR_NAPOT(DR_REG_PLIC_UX_BASE, DR_REG_CLINT_M_BASE); + PMP_ENTRY_SET(14, pmpaddr14, PMP_NAPOT | RW); + _Static_assert(DR_REG_PLIC_UX_BASE < DR_REG_CLINT_M_BASE, "Invalid User mode PLIC region"); + + const uint32_t pmpaddr15 = PMPADDR_NAPOT(DR_REG_CLINT_U_BASE, DR_REG_CLINT_U_END); + PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW); + _Static_assert(DR_REG_CLINT_U_BASE < DR_REG_CLINT_U_END, "Invalid User mode CLINT region"); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c new file mode 100644 index 000000000000..9b558054cc91 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "riscv/rv_utils.h" +#include "riscv/encoding.h" + +#include "esp_cpu.h" +#include "esp_log.h" +#include "hal/apm_hal.h" + +#include "esp_tee.h" +#include "esp_tee_intr.h" + +#define _m2u_switch(arg0, arg1) \ + ({ \ + register uintptr_t ra asm("ra") = (uintptr_t)(arg0); \ + register uintptr_t a1 asm("a1") = (uintptr_t)(arg1); \ + asm volatile("ecall" : :"r"(ra), "r"(a1) : ); \ + }) + +#define SET_BIT(t, n) (t |= (1UL << (n))) +#define CLR_BIT(t, n) (t &= ~(1UL << (n))) + +static const char *TAG = "esp_tee_secure_sys_cfg"; + +extern uint32_t _vector_table; + +void esp_tee_soc_secure_sys_init(void) +{ + ESP_LOGI(TAG, "Current privilege level - %d", esp_cpu_get_curr_privilege_level()); + + /* NOTE: M/U-mode PLIC Special Configuration Register + * Bit 0: Use the external PLIC registers (legacy) from the SoC (default) + * Bit 1: Use the internal PLIC registers as per the new SoC address map + */ + REG_SET_BIT(PLIC_MXINT_CONF_REG, BIT(0)); + REG_SET_BIT(PLIC_UXINT_CONF_REG, BIT(0)); + + /* Setting the M-mode vector table */ + rv_utils_set_mtvec((uint32_t)&_vector_table); + + /* Disable global interrupts */ + RV_CLEAR_CSR(mstatus, MSTATUS_UIE); + RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + + /* Clear all interrupts */ + RV_WRITE_CSR(mie, 0x00); + RV_WRITE_CSR(uie, 0x00); + + /* All interrupts except the TEE secure interrupt are delegated to the U-mode */ + uint32_t mideleg_val = UINT32_MAX; + CLR_BIT(mideleg_val, TEE_SECURE_INUM); + RV_WRITE_CSR(mideleg, mideleg_val); + + /* TODO: IDF-8958 + * The values for the secure interrupt number and priority and + * the interrupt priority threshold (for both M and U mode) need + * to be investigated further + */ +#ifdef SOC_CPU_HAS_FLEXIBLE_INTC + /* TODO: Currently, we do not allow interrupts to be set up with a priority greater than 7, see intr_alloc.c */ + esprv_int_set_priority(TEE_SECURE_INUM, 7); + esprv_int_set_type(TEE_SECURE_INUM, ESP_CPU_INTR_TYPE_LEVEL); + esprv_int_set_threshold(1); + esprv_int_enable(BIT(TEE_SECURE_INUM)); +#endif + + ESP_LOGD(TAG, "Initial interrupt config -"); + ESP_LOGD(TAG, "mideleg: 0x%08x", RV_READ_CSR(mideleg)); + ESP_LOGD(TAG, "mie: 0x%08x | uie: 0x%08x", RV_READ_CSR(mie), RV_READ_CSR(uie)); + ESP_LOGD(TAG, "mstatus: 0x%08x | ustatus: 0x%08x", RV_READ_CSR(mstatus), RV_READ_CSR(ustatus)); + ESP_LOGD(TAG, "[PLIC] MX: 0x%08x | UX: 0x%08x", REG_READ(PLIC_MXINT_ENABLE_REG), REG_READ(PLIC_UXINT_ENABLE_REG)); + + /* PMP, PMA and APM configuration to isolate the resources between TEE and REE. */ + esp_tee_configure_region_protection(); + esp_tee_configure_apm_protection(); + + /* Protect secure interrupt sources */ + esp_tee_protect_intr_src(ETS_LP_APM_M0_INTR_SOURCE); // LP_APM_M0 + esp_tee_protect_intr_src(ETS_LP_APM_M1_INTR_SOURCE); // LP_APM_M1 + esp_tee_protect_intr_src(ETS_HP_APM_M0_INTR_SOURCE); // HP_APM_M0 + esp_tee_protect_intr_src(ETS_HP_APM_M1_INTR_SOURCE); // HP_APM_M1 + esp_tee_protect_intr_src(ETS_HP_APM_M2_INTR_SOURCE); // HP_APM_M2 + esp_tee_protect_intr_src(ETS_HP_APM_M3_INTR_SOURCE); // HP_APM_M3 + esp_tee_protect_intr_src(ETS_LP_APM0_INTR_SOURCE); // LP_APM0 + esp_tee_protect_intr_src(ETS_EFUSE_INTR_SOURCE); // eFuse + esp_tee_protect_intr_src(ETS_AES_INTR_SOURCE); // AES + esp_tee_protect_intr_src(ETS_SHA_INTR_SOURCE); // SHA +} + +IRAM_ATTR inline void esp_tee_switch_to_ree(uint32_t ree_entry_addr) +{ + /* Switch HP_CPU to REE0 mode. */ + apm_tee_hal_set_master_secure_mode(HP_APM_CTRL, APM_LL_MASTER_HPCORE, APM_LL_SECURE_MODE_REE0); + + /* 2nd argument is used as magic value to detect very first M2U switch */ + /* TBD: clean this up and use proper temporary register instead of a1 */ + /* Switch to non-secure world and launch App. */ + _m2u_switch(ree_entry_addr, ESP_TEE_M2U_SWITCH_MAGIC << 12); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h new file mode 100644 index 000000000000..ebdbebe451ac --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void esp_tee_aes_intr_alloc(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h new file mode 100644 index 000000000000..6019df64b307 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "hal/apm_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char *esp_tee_apm_excp_mid_to_str(uint8_t mid); + +const char *esp_tee_apm_excp_type_to_str(uint8_t type); + +const char *esp_tee_apm_excp_ctrl_to_str(apm_ll_apm_ctrl_t apm_ctrl); + +const char *esp_tee_apm_excp_mode_to_str(uint8_t mode); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h new file mode 100644 index 000000000000..60e588e02b70 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include + +#include "riscv/csr.h" +#include "riscv/interrupt.h" + +#include "soc/interrupt_reg.h" +#include "soc/plic_reg.h" + +#include "esp_attr.h" +#include "esp_tee.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SET_BIT(t, n) (t |= (1UL << (n))) +#define CLR_BIT(t, n) (t &= ~(1UL << (n))) + +FORCE_INLINE_ATTR void rv_utils_tee_intr_global_enable(void) +{ + /* + * Set the the U-mode previous enable global interrupts state + * + * NOTE: The TICK interrupt is setup before this service call and thus, + * it occurs in the return path of this call. + * + * Before entering the U-mode interrupt handler routine, USTATUS:UIE is + * cleared to disable U-mode interrupts temporarily. + * + * While exiting the above routine, URET is executed, setting USTATUS:UIE + * to the value of USTATUS:UPIE. However, since no interrupts were enabled + * previously, USTATUS:UPIE and thus, USTATUS:UIE is cleared. + * + * The service call completes and returns to U-mode with USTATUS:UIE disabled, + * preventing any further interrupts in U-mode. + * + */ + RV_SET_CSR(ustatus, USTATUS_UPIE); + /* Enabling the global M-mode and U-mode interrupts */ + RV_SET_CSR(ustatus, USTATUS_UIE); + RV_SET_CSR(mstatus, MSTATUS_UIE); + RV_SET_CSR(mstatus, MSTATUS_MIE); +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_enable(uint32_t intr_mask) +{ + unsigned old_xstatus; + + // Machine mode + // Disable all interrupts to make updating of the interrupt mask atomic. + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + REG_SET_BIT(PLIC_MXINT_ENABLE_REG, intr_mask); + RV_SET_CSR(mie, intr_mask); + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + + // User mode + // Disable all interrupts to make updating of the interrupt mask atomic. + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + REG_SET_BIT(PLIC_UXINT_ENABLE_REG, intr_mask); + RV_SET_CSR(uie, intr_mask); + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_disable(uint32_t intr_mask) +{ + unsigned old_xstatus; + + // Machine mode + // Disable all interrupts to make updating of the interrupt mask atomic. + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + REG_CLR_BIT(PLIC_MXINT_ENABLE_REG, intr_mask); + RV_CLEAR_CSR(mie, intr_mask); + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + + // User mode + // Disable all interrupts to make updating of the interrupt mask atomic. + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + REG_CLR_BIT(PLIC_UXINT_ENABLE_REG, intr_mask); + RV_CLEAR_CSR(uie, intr_mask); + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_set_type(int intr_num, enum intr_type type) +{ + assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM); + + if (type == INTR_TYPE_LEVEL) { + REG_CLR_BIT(PLIC_MXINT_TYPE_REG, BIT(intr_num)); + REG_CLR_BIT(PLIC_UXINT_TYPE_REG, BIT(intr_num)); + } else { + REG_SET_BIT(PLIC_MXINT_TYPE_REG, BIT(intr_num)); + REG_SET_BIT(PLIC_UXINT_TYPE_REG, BIT(intr_num)); + } +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_set_priority(int rv_int_num, int priority) +{ + assert(rv_int_num >= 0 && rv_int_num < SOC_CPU_INTR_NUM); + + REG_WRITE(PLIC_MXINT_PRI_REG(rv_int_num), priority); + REG_WRITE(PLIC_UXINT_PRI_REG(rv_int_num), priority); +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_set_threshold(int priority_threshold) +{ + REG_WRITE(PLIC_MXINT_THRESH_REG, priority_threshold); + REG_WRITE(PLIC_UXINT_THRESH_REG, priority_threshold); +} + +FORCE_INLINE_ATTR void rv_utils_tee_intr_edge_ack(int intr_num) +{ + assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM); + + REG_SET_BIT(PLIC_MXINT_CLEAR_REG, intr_num); + REG_SET_BIT(PLIC_UXINT_CLEAR_REG, intr_num); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/test_apps/.build-test-rules.yml b/components/esp_tee/test_apps/.build-test-rules.yml new file mode 100644 index 000000000000..4f0f92041df9 --- /dev/null +++ b/components/esp_tee/test_apps/.build-test-rules.yml @@ -0,0 +1,11 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/esp_tee/test_apps/tee_cli_app: + disable: + - if: IDF_TARGET not in ["esp32c6"] + reason: only supported with esp32c6 for now + +components/esp_tee/test_apps/tee_test_fw: + disable: + - if: IDF_TARGET not in ["esp32c6"] + reason: only supported with esp32c6 for now diff --git a/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt b/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt new file mode 100644 index 000000000000..09fa1b505ed7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt @@ -0,0 +1,16 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +# (Not part of the boilerplate) +# This example uses extra components for the following - +# 1. common functions such as Wi-Fi and Ethernet connection. +# 2. managing TEE OTA updates +# 3. dumping TEE attestation info +# 4. TEE Secure storage +list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_ota_ops + $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_attestation + $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_sec_storage) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tee_cli) diff --git a/components/esp_tee/test_apps/tee_cli_app/README.md b/components/esp_tee/test_apps/tee_cli_app/README.md new file mode 100644 index 000000000000..b57c21af3516 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/README.md @@ -0,0 +1,292 @@ +| Supported Targets | ESP32-C6 | +| ----------------- | -------- | + +# TEE CLI Application: Secure Services Demonstration + +## Example Usage + +### Hardware Required + +This example can be executed on any development board with a Espressif SOC chip supporting the TEE framework (see Supported Targets table above). + +### Configure the project + +- Before the project configuration and build, be sure to set the correct chip target using `idf.py set-target `. + +- Open the project configuration menu (`idf.py menuconfig`). + +- Configure the secure storage slot ID for generating/fetching the ECDSA keypair for attestation token signing at `(Top) → Security features → TEE: Secure Storage slot ID for EAT signing`. If this configuration is not set, the slot with ID **0** will be used as default. + +- Configure the Secure Storage mode for determining which eFuse block stores the encryption key at `(Top) → Security features → Trusted Execution Environment → TEE: Secure Storage Mode`. + - **Development** Mode: The encryption key is statically embedded in the TEE firmware. + - **Release** Mode: The encryption key is stored in eFuse BLK4 - BLK9, depending on the `SECURE_TEE_SEC_STG_KEY_EFUSE_BLK` Kconfig option. + - Set the eFuse block ID to store the encryption key in `Security features → Trusted Execution Environment → TEE: Secure Storage encryption key eFuse block`. + - Snippet for burning the secure storage key in eFuse is given below. + + ```shell + # Programming user key (256-bit) in eFuse + # Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5 + espefuse.py -p PORT burn_key BLOCK_KEYx user_key.bin USER + ``` + +### Build and Flash + +- Build the project and flash it to the board, then run the monitor tool to view the serial output: + +```shell +# Replace PORT with the name of the serial port to use +idf.py -p flash monitor +``` + +(To exit the serial monitor, type `Ctrl-]`.) + +Refer the _Getting Started_ guide for full steps to configure and use ESP-IDF to build projects. + +### Example Output + +Enter the `help` command get a full list of all available commands provided by the example. + +```log +I (627) app_start: Starting scheduler on CPU0 +I (632) main_task: Started on CPU0 +I (632) main_task: Calling app_main() +I (632) example: ESP-TEE: Secure services demonstration + +Type 'help' to get the list of commands. +Use UP/DOWN arrows to navigate through command history. +Press TAB when typing command name to auto-complete. +I (702) main_task: Returned from app_main() +esp32c6> help +wifi_connect [--timeout=] [] + Join WiFi AP as a station + --timeout= Connection timeout, ms + SSID of AP + PSK of AP + +tee_ota + Initiate TEE app OTA + URL for fetching the update + +user_ota + Initiate User app OTA + URL for fetching the update + +tee_att_info + Dump the TEE-generated entity attestation token + +get_msg_sha256 "" + Get the SHA256 digest for the given message + "" Message for SHA256 digest calculation + +tee_sec_stg_gen_key + Generate and store a new key of the specified type in the given TEE secure + storage slot + TEE Secure storage slot for storing the key + Key type (0: ECDSA_SECP256R1, 1: AES256) + +tee_sec_stg_sign + Sign a message using the ECDSA keypair stored in the given slot ID and verify + the signature + TEE Secure storage slot storing the ecdsa-secp256r1 keypair + SHA256 digest of the message to be signed and verified + +tee_sec_stg_encrypt + Encrypt data using AES-GCM with a key from secure storage + <slot_id> TEE Secure storage slot storing the AES key + <plaintext> Plaintext to be encrypted + +tee_sec_stg_decrypt <slot_id> <ciphertext> <tag> + Decrypt data using AES-GCM with a key from secure storage + <slot_id> TEE Secure storage slot storing the AES key + <ciphertext> Ciphertext to be decrypted + <tag> AES-GCM authentication tag + +help + Print the list of registered commands +``` + +## Secure Services + +### Attestation + +- The `tee_att_info` command provided by the attestation service generates and dumps an Entity Attestation Token (EAT) signed by the TEE. +- The token is signed using the ECDSA key (`secp256r1` curve) stored in the configured slot ID of the TEE Secure Storage. + +<details> + <summary><b>Sample output:</b> <i>tee_att_info</i></summary> + +```log +esp32c6> tee_att_info +I (8180) tee_attest: Attestation token - Length: 1455 +I (8180) tee_attest: Attestation token - Data: +'{"header":{"magic":"44fef7cc","encr_alg":"","sign_alg":"ecdsa_secp256r1_sha256","key_id":0},"eat":{"nonce":-1582119980,"client_id":262974944,"device_ver":0,"device_id":"cd9c173cb3675c7adfae243f0cd9841e4bce003237cb5321927a85a86cb4b32e","instance_id":"9616ef0ecf02cdc89a3749f8fc16b3103d5100bd42d9312fcd04593baa7bac64","psa_cert_ref":"0716053550477-10100","device_status":165,"sw_claims":{"tee":{"type":1,"ver":"v0.3.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"94536998e1dcb2a036477cb2feb01ed4fff67ba6208f30482346c62bca64b280","digest_validated":true,"sign_verified":true}},"app":{"type":2,"ver":"v0.1.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"3d4c038fcec76852b4d07acb9e94afaf5fca69fc2eb212a32032d09ce5b4f2b3","digest_validated":true,"sign_verified":true,"secure_padding":true}},"bootloader":{"type":0,"ver":"","idf_ver":"","secure_ver":-1,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"1bef421beb1a4642c6fcefb3e37fd4afad60cb4074e538f42605b012c482b946","digest_validated":true,"sign_verified":true}}}},"public_key":{"compressed":"02039c4bfab0762af1aff2fe5596b037f629cf839da8c4a9c0018afedfccf519a6"},"sign":{"r":"915e749f5a780bc21a2b21821cfeb54286dc742e9f12f2387e3de9b8b1a70bc9","s":"1e583236f2630b0fe8e291645ffa35d429f14035182e19868508d4dac0e1a441"}}' + +``` + +</details> + +### Secure Storage + +- The TEE secure storage service provides the following commands: + - `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in a specified TEE secure storage slot + - `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair stored in a specified slot and verify the signature + - `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key from the specified slot and outputs the ciphertext and tag + - `tee_sec_stg_decrypt`: Decrypt ciphertext using key from the specified slot and tag for integrity verification +- The `get_msg_sha256` command computes the SHA256 hash of a given message, which can be used as input for the `tee_sec_stg_sign` command. + +<details> + <summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + get_msg_sha256 + tee_sec_stg_sign</i></summary> + +```log +esp32c6> tee_sec_stg_gen_key 7 0 +I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key in slot 7 +esp32c6> get_msg_sha256 "hello world" +I (3984) tee_sec_stg: Message digest (SHA256) - +b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +esp32c6> tee_sec_stg_sign 7 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +I (5384) tee_sec_stg: Generated signature - +944684f6ddcf4c268ac6b65e34ccb8d95bd2849567a87867101bc1f09208f0885d935d7b3ba9d46014f28e4c7c988d68c775431fcb2cb2d4ca5c6862db771088 +I (6404) tee_sec_stg: Public key (Uncompressed) - +04a515bf1c43766cc34980dd6934b9ff54fd3d5d70fe7a694b1fea7a0bbc74434d008c7c3117ce0a5216ffdb2b807f2668cce9c973d524c038ab47b4344064dbbf +I (6444) tee_sec_stg: Signature verified successfully! +``` + +</details> + +<details> + <summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + tee_sec_stg_encrypt + tee_sec_stg_decrypt</i></summary> + +```log +esp32c6> tee_sec_stg_gen_key 8 1 +I (2784) tee_sec_stg: Generated AES256 key in slot 8 +esp32c6> tee_sec_stg_encrypt 8 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +I (3084) tee_sec_stg: Ciphertext - +58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 +I (3594) tee_sec_stg: Tag - +caeedb43e08dc3b4e35a58b2412908cc +esp32c6> tee_sec_stg_decrypt 8 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc +I (4314) tee_sec_stg: Decrypted plaintext - +b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +``` + +</details> + +### Over-the-Air updates + +- The TEE/REE OTA service demonstrates the following workflow: + 1. Connect to a WiFi network (`wifi_connect` command) + 2. Initiate the TEE/REE OTA update, fetching the _new_ application image from the given URL (`tee_ota` and `user_ota` commands) + +<details> + <summary><b>Sample output:</b> <i>wifi_connect</i></summary> + +```log +esp32c6> wifi_connect myssid mypassword +I (498) connect: Connecting to 'myssid' +I (498) pp: pp rom version: 5b8dcfa +I (508) net80211: net80211 rom version: 5b8dcfa +I (518) wifi_init: rx ba win: 6 +I (518) wifi_init: tcpip mbox: 32 +I (518) wifi_init: udp mbox: 6 +I (518) wifi_init: tcp mbox: 6 +I (528) wifi_init: tcp tx win: 5760 +I (528) wifi_init: tcp rx win: 5760 +I (538) wifi_init: tcp mss: 1440 +I (538) wifi_init: WiFi IRAM OP enabled +I (538) wifi_init: WiFi RX IRAM OP enabled +I (548) phy_init: phy_version 290,81efd96,May 8 2024,10:42:13 +W (598) wifi:(bf)761:0x600a7cac:0x01b4b4b0 +W (598) wifi:(agc)0x600a7128:0xd2173800, min.avgNF:0xce->0xd2(dB), RCalCount:0x173, min.RRssi:0x800(-128.00) +W (608) wifi:(TB)WDEV_PWR_TB_MCS0:19 +W (608) wifi:(TB)WDEV_PWR_TB_MCS1:19 +W (608) wifi:(TB)WDEV_PWR_TB_MCS2:19 +W (608) wifi:(TB)WDEV_PWR_TB_MCS3:19 +W (618) wifi:(TB)WDEV_PWR_TB_MCS4:19 +W (618) wifi:(TB)WDEV_PWR_TB_MCS5:19 +W (618) wifi:(TB)WDEV_PWR_TB_MCS6:18 +W (628) wifi:(TB)WDEV_PWR_TB_MCS7:18 +W (628) wifi:(TB)WDEV_PWR_TB_MCS8:17 +W (628) wifi:(TB)WDEV_PWR_TB_MCS9:15 +W (648) wifi:(TB)WDEV_PWR_TB_MCS10:15 +W (648) wifi:(TB)WDEV_PWR_TB_MCS11:15 +W (1328) wifi:<ba-add>idx:0, ifx:0, tid:0, TAHI:0x1008fe0, TALO:0xc7e45510, (ssn:0, win:64, cur_ssn:0), CONF:0xc0000005 +I (6358) esp_netif_handlers: sta ip: 192.168.1.30, mask: 255.255.255.0, gw: 192.168.1.1 +I (6358) event_handler: got ip:192.168.1.30 +I (6368) connect: Connected +``` + +</details> + +<details> + <summary><b>Sample output:</b> <i>tee_ota</i></summary> + +```log +esp32c6> tee_ota https://192.168.1.1:4443/esp_tee/esp_tee.bin +I (5884) ota_with_tee: Starting TEE OTA... +esp32c6> I (1066394) esp-x509-crt-bundle: Certificate validated +I (7424) ota_with_tee: esp_tee_ota_begin succeeded +I (7904) ota_with_tee: Connection closed +I (7904) ota_with_tee: esp_tee_ota_write succeeded +I (7904) ota_with_tee: Total binary data written: 118784 +I (8064) ota_with_tee: esp_tee_ota_end succeeded +I (8064) ota_with_tee: Prepare to restart system! +``` + +</details> + +<details> + <summary><b>Sample output:</b> <i>user_ota</i></summary> + +```log +esp32c6> user_ota https://192.168.1.1:4443/tee_cli.bin +I (2388) ota_with_tee: Starting User OTA task... +I (2388) ota_with_tee: Attempting to download update from https://192.168.1.1:4443/tee_cli.bin +I (2438) esp-x509-crt-bundle: Certificate validated +esp32c6> I (62888) esp_https_ota: Starting OTA... +I (2888) esp_https_ota: Writing to partition subtype 17 at offset 0x1f0000 +I (37338) esp_image: segment 0: paddr=001f0020 vaddr=420e0020 size=2ecc0h (191680) map +I (37368) esp_image: segment 1: paddr=0021ece8 vaddr=40811000 size=01330h ( 4912) +I (37378) esp_image: segment 2: paddr=00220020 vaddr=42020020 size=b88b0h (755888) map +I (37518) esp_image: segment 3: paddr=002d88d8 vaddr=40812330 size=14488h ( 83080) +I (37538) esp_image: segment 4: paddr=002ecd68 vaddr=408267c0 size=032c0h ( 12992) +I (37538) esp_image: segment 5: paddr=002f0030 vaddr=00000000 size=0ffa0h ( 65440) +I (37568) esp_image: Verifying image signature... +I (37568) secure_boot_v2: Take trusted digest key(s) from eFuse block(s) +I (37568) secure_boot_v2: #0 app key digest == #0 trusted key digest +I (37578) secure_boot_v2: Verifying with RSA-PSS... +I (37638) secure_boot_v2_rsa: Signature verified successfully! +I (37648) esp_image: segment 0: paddr=001f0020 vaddr=420e0020 size=2ecc0h (191680) map +I (37678) esp_image: segment 1: paddr=0021ece8 vaddr=40811000 size=01330h ( 4912) +I (37678) esp_image: segment 2: paddr=00220020 vaddr=42020020 size=b88b0h (755888) map +I (37828) esp_image: segment 3: paddr=002d88d8 vaddr=40812330 size=14488h ( 83080) +I (37848) esp_image: segment 4: paddr=002ecd68 vaddr=408267c0 size=032c0h ( 12992) +I (37848) esp_image: segment 5: paddr=002f0030 vaddr=00000000 size=0ffa0h ( 65440) +I (37868) esp_image: Verifying image signature... +I (37878) secure_boot_v2: Take trusted digest key(s) from eFuse block(s) +I (37878) secure_boot_v2: #0 app key digest == #0 trusted key digest +I (37888) secure_boot_v2: Verifying with RSA-PSS... +I (37948) secure_boot_v2_rsa: Signature verified successfully! +I (37998) ota_with_tee: OTA Succeed, Rebooting... +``` + +</details> + +#### Local HTTPS server for hosting OTA images + +- The script `https_server.py` is a helper script for the OTA service. Executing it with the `python https_server.py` command starts a local HTTPS server with the test certificates from the `test_certs` directory and serves files from the `build` directory that holds the generated binaries for the TEE and REE. Following is the script help - + +``` +python https_server.py --help +usage: https_server.py [-h] [--certfile CERTFILE] [--keyfile KEYFILE] [--port PORT] [--path PATH] + +Start a local HTTPS server. + +options: + -h, --help show this help message and exit + --certfile CERTFILE Path to the SSL certificate file (default: test_certs/server_cert.pem) + --keyfile KEYFILE Path to the SSL key file (default: test_certs/server_key.pem) + --port PORT Port number to bind the server to (default: 4443) + --path PATH Path to the directory to serve files from (default: build directory) + +E.g., python https_server.py --certfile test_certs/server_cert.pem --keyfile test_certs/server_key.pem --port 8070 --path build +``` diff --git a/components/esp_tee/test_apps/tee_cli_app/https_server.py b/components/esp_tee/test_apps/tee_cli_app/https_server.py new file mode 100644 index 000000000000..352e9357344c --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/https_server.py @@ -0,0 +1,86 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import argparse +import http.server +import os +import socket +import ssl +import sys + +# python https_server.py --help +# usage: https_server.py [-h] [--certfile CERTFILE] [--keyfile KEYFILE] [--port PORT] [--path PATH] +# +# Start a local HTTPS server. +# +# options: +# -h, --help show this help message and exit +# --certfile CERTFILE Path to the SSL certificate file (default: test_certs/server_cert.pem) +# --keyfile KEYFILE Path to the SSL key file (default: test_certs/server_key.pem) +# --port PORT Port number to bind the server to (default: 4443) +# --path PATH Path to the directory to serve files from (default: current directory) +# +# E.g., python https_server.py --certfile test_certs/server_cert.pem --keyfile test_certs/server_key.pem --port 8070 --path build + + +# Get the local machine's network IP address +def get_local_addr() -> str: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + local_addr = '' + try: + s.connect(('8.8.8.8', 80)) # Connect to a remote server to get the network IP + local_addr = s.getsockname()[0] + finally: + s.close() + return local_addr + + +def main() -> None: + # Parse command-line arguments + parser = argparse.ArgumentParser(description='Start a local HTTPS server') + parser.add_argument('--certfile', default='test_certs/server_cert.pem', help='Path to the SSL certificate file (default: test_certs/server_cert.pem)') + parser.add_argument('--keyfile', default='test_certs/server_key.pem', help='Path to the SSL key file (default: test_certs/server_key.pem)') + parser.add_argument('--port', type=int, default=4443, help='Port number to bind the server to (default: 4443)') + parser.add_argument('--path', default='build', help='Path to the directory to serve files from (default: build directory)') + args = parser.parse_args() + + # Resolve the absolute paths for the cert and key files before changing the working directory + certfile_path = os.path.abspath(args.certfile) + keyfile_path = os.path.abspath(args.keyfile) + + # Change to the specified directory + os.chdir(args.path) + + # Get the network IP address + local_ip = get_local_addr() + + # Define the server address and port + server_address = (local_ip, args.port) + + # Create a simple request handler + Handler = http.server.SimpleHTTPRequestHandler + + # Create the HTTP server + httpd = http.server.HTTPServer(server_address, Handler) + + # Create an SSL context + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain(certfile=certfile_path, keyfile=keyfile_path) + + # Wrap the server's socket with the SSL context + httpd.socket = context.wrap_socket(httpd.socket, server_side=True) + + # Print the server address for clients to use + print(f'Server is running at: https://{local_ip}:{args.port}') + print('Press [Ctrl+C] to shut down the server') + + # Start the server + try: + httpd.serve_forever() + except KeyboardInterrupt: + print('\nShutting down the server...') + httpd.shutdown() + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt new file mode 100644 index 000000000000..825c28ebe769 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "tee_srv_att.c" + "tee_srv_ota.c" + "tee_srv_sec_str.c" + "tee_cmd_wifi.c" + "app_main.c" + INCLUDE_DIRS ".") diff --git a/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild b/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild new file mode 100644 index 000000000000..f748858eea4a --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + + config EXAMPLE_SKIP_COMMON_NAME_CHECK + bool "Skip server certificate CN fieldcheck" + default n + help + This allows you to skip the validation of OTA server certificate CN field. + + config EXAMPLE_OTA_RECV_TIMEOUT + int "OTA Receive Timeout" + default 5000 + help + Maximum time for reception + +endmenu diff --git a/components/esp_tee/test_apps/tee_cli_app/main/app_main.c b/components/esp_tee/test_apps/tee_cli_app/main/app_main.c new file mode 100644 index 000000000000..9f619a8ee730 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/app_main.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> +#include <inttypes.h> +#include <errno.h> + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_console.h" + +#include "nvs.h" +#include "nvs_flash.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "example_tee_srv.h" + +#define PROMPT_STR CONFIG_IDF_TARGET + +static const char *TAG = "example"; + +static void setup_console(void) +{ + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + /* Prompt to be printed before each line. + * This can be customized, made dynamic, etc. + */ + repl_config.prompt = PROMPT_STR ">"; + repl_config.max_cmdline_length = 128; + + /* Register help command */ + ESP_ERROR_CHECK(esp_console_register_help_command()); + + /* Register custom commands */ + register_cmd_wifi(); + register_srv_tee_ota(); + register_srv_user_ota(); + register_srv_attestation(); + register_cmd_msg_sha256(); + register_srv_sec_stg_gen_key(); + register_srv_sec_stg_sign(); + register_srv_sec_stg_encrypt(); + register_srv_sec_stg_decrypt(); + +#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM) + esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl)); +#elif defined(CONFIG_ESP_CONSOLE_USB_CDC) + esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl)); +#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) + esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl)); +#else +#error Unsupported console type +#endif + + ESP_ERROR_CHECK(esp_console_start_repl(repl)); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "ESP-TEE: Secure services demonstration"); + + // Initialize NVS + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + + setup_console(); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h b/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h new file mode 100644 index 000000000000..87a58e9e65ca --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void register_cmd_wifi(void); + +void register_srv_attestation(void); + +void register_srv_tee_ota(void); + +void register_srv_user_ota(void); + +void register_cmd_msg_sha256(void); + +void register_srv_sec_stg_gen_key(void); + +void register_srv_sec_stg_sign(void); + +void register_srv_sec_stg_encrypt(void); + +void register_srv_sec_stg_decrypt(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c new file mode 100644 index 000000000000..e09b2168bb49 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c @@ -0,0 +1,134 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <stdio.h> +#include <string.h> + +#include "esp_log.h" +#include "esp_event.h" + +#include "esp_wifi.h" +#include "esp_netif.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#include "esp_console.h" +#include "argtable3/argtable3.h" + +#include "example_tee_srv.h" + +#define JOIN_TIMEOUT_MS (15000) + +static EventGroupHandle_t wifi_event_group; +const int CONNECTED_BIT = BIT0; + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(__func__, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + } +} + +static void initialise_wifi(void) +{ + esp_log_level_set("wifi", ESP_LOG_WARN); + static bool initialized = false; + if (initialized) { + return; + } + ESP_ERROR_CHECK(esp_netif_init()); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + assert(ap_netif); + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + assert(sta_netif); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); + ESP_ERROR_CHECK(esp_wifi_start()); + initialized = true; +} + +static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) +{ + initialise_wifi(); + wifi_config_t wifi_config = { 0 }; + strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + if (pass) { + strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + } + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + esp_wifi_connect(); + + int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS); + return (bits & CONNECTED_BIT) != 0; +} + +/** Arguments used by 'join' function */ +static struct { + struct arg_int *timeout; + struct arg_str *ssid; + struct arg_str *password; + struct arg_end *end; +} join_args; + +static int connect(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &join_args); + if (nerrors != 0) { + arg_print_errors(stderr, join_args.end, argv[0]); + return ESP_ERR_INVALID_ARG; + } + ESP_LOGI(__func__, "Connecting to '%s'", + join_args.ssid->sval[0]); + + /* set default value*/ + if (join_args.timeout->count == 0) { + join_args.timeout->ival[0] = JOIN_TIMEOUT_MS; + } + + bool connected = wifi_join(join_args.ssid->sval[0], + join_args.password->sval[0], + join_args.timeout->ival[0]); + if (!connected) { + ESP_LOGW(__func__, "Connection timed out"); + return ESP_ERR_TIMEOUT; + } + ESP_LOGI(__func__, "Connected"); + return 0; +} + +void register_cmd_wifi(void) +{ + join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms"); + join_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP"); + join_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP"); + join_args.end = arg_end(2); + + const esp_console_cmd_t join_cmd = { + .command = "wifi_connect", + .help = "Join WiFi AP as a station", + .hint = NULL, + .func = &connect, + .argtable = &join_args + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&join_cmd)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c new file mode 100644 index 000000000000..985897d7e561 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "esp_event.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_console.h" +#include "argtable3/argtable3.h" + +#include "esp_tee_attestation.h" +#include "example_tee_srv.h" + +static const char *TAG = "tee_attest"; + +#define ESP_ATT_TK_BUF_SIZE (1792) +#define ESP_ATT_TK_PSA_CERT_REF ("0716053550477-10100") + +static uint8_t token_buf[ESP_ATT_TK_BUF_SIZE] = {0}; + +static int tee_dump_att_token(int argc, char **argv) +{ + if (argc != 1) { + ESP_LOGE(TAG, "Incorrect number of arguments given!"); + return ESP_ERR_INVALID_ARG; + } + + uint32_t token_len = 0; + esp_err_t err = esp_tee_att_generate_token(0xA1B2C3D4, 0x0FACADE0, (const char *)ESP_ATT_TK_PSA_CERT_REF, + token_buf, sizeof(token_buf), &token_len); + if (err != ESP_OK) { + return err; + } + + ESP_LOGI(TAG, "Attestation token - Length: %lu", token_len); + ESP_LOGI(TAG, "Attestation token - Data:\n'%.*s'", (int)token_len, token_buf); + + return ESP_OK; +} + +void register_srv_attestation(void) +{ + const esp_console_cmd_t tee_att_cmd = { + .command = "tee_att_info", + .help = "Dump the TEE-generated entity attestation token", + .hint = NULL, + .func = &tee_dump_att_token, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_att_cmd)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c new file mode 100644 index 000000000000..42aacd3a37e7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c @@ -0,0 +1,300 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> +#include <inttypes.h> +#include <errno.h> + +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_app_format.h" +#include "esp_image_format.h" +#include "esp_flash_partitions.h" +#include "esp_partition.h" + +#include "esp_crt_bundle.h" +#include "esp_http_client.h" +#include "esp_https_ota.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_console.h" +#include "argtable3/argtable3.h" + +#include "esp_tee_ota_ops.h" +#include "example_tee_srv.h" + +#define BUF_SIZE 256 + +static const char *TAG = "ota_with_tee"; + +/* Semaphore governing the TEE and User app OTA processes; only one should be active at a time */ +static SemaphoreHandle_t s_ota_mgmt; + +static void http_cleanup(esp_http_client_handle_t client) +{ + esp_http_client_close(client); + esp_http_client_cleanup(client); +} + +static void task_fatal_error(void) +{ + ESP_LOGE(TAG, "Exiting task due to fatal error..."); + (void)vTaskDelete(NULL); +} + +static esp_err_t setup_task_conn(esp_http_client_config_t *config, const char *url) +{ + if (config == NULL || url == NULL) { + ESP_LOGE(TAG, "Invalid arguments!"); + return ESP_ERR_INVALID_ARG; + } + + /* Wait for the semaphore to be free for the taking */ + if (xSemaphoreTake(s_ota_mgmt, pdMS_TO_TICKS(1000)) != pdTRUE) { + ESP_LOGE(TAG, "Other OTA already in progress!"); + return ESP_FAIL; + } + + config->url = url; + config->timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT; + config->keep_alive_enable = true; + config->crt_bundle_attach = esp_crt_bundle_attach; +#ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK + config->skip_cert_common_name_check = true; +#endif + + return ESP_OK; +} + +static void tee_ota_task(void *pvParameter) +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + esp_http_client_config_t config = {}; + if (setup_task_conn(&config, (const char *)pvParameter) != ESP_OK) { + ESP_LOGE(TAG, "Failed to setup OTA task"); + task_fatal_error(); + } + + esp_http_client_handle_t client = esp_http_client_init(&config); + if (client == NULL) { + ESP_LOGE(TAG, "Failed to initialise HTTP connection"); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + + esp_err_t err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + esp_http_client_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + esp_http_client_fetch_headers(client); + + uint32_t curr_write_offset = 0; + bool image_header_was_checked = false; /* deal with all receive packet */ + char ota_write_data[BUF_SIZE + 1] = {0}; /* an ota data write buffer ready to write to the flash */ + + while (1) { + int data_read = esp_http_client_read(client, ota_write_data, BUF_SIZE); + if (data_read < 0) { + ESP_LOGE(TAG, "Error: SSL data read error"); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } else if (data_read > 0) { + if (image_header_was_checked == false) { + /* TODO: TEE image header is missing the `esp_app_desc_t` configuration structure */ + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)) { + esp_image_header_t img_hdr; + memcpy(&img_hdr, ota_write_data, sizeof(esp_image_header_t)); + if (img_hdr.chip_id != CONFIG_IDF_FIRMWARE_CHIP_ID) { + ESP_LOGE(TAG, "Mismatch chip id, expected %d, found %d", CONFIG_IDF_FIRMWARE_CHIP_ID, img_hdr.chip_id); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + image_header_was_checked = true; + + err = esp_tee_ota_begin(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_tee_ota_begin failed (%s)", esp_err_to_name(err)); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + ESP_LOGI(TAG, "esp_tee_ota_begin succeeded"); + + } else { + ESP_LOGE(TAG, "received package is not fit len"); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + } + err = esp_tee_ota_write(curr_write_offset, (const void *)ota_write_data, data_read); + if (err != ESP_OK) { + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + curr_write_offset += data_read; + memset(ota_write_data, 0x00, sizeof(ota_write_data)); + ESP_LOGD(TAG, "Written image length: %lu", curr_write_offset); + } else if (data_read == 0) { + /* + * As esp_http_client_read never returns negative error code, we rely on + * `errno` to check for underlying transport connectivity closure if any + */ + if (errno == ECONNRESET || errno == ENOTCONN) { + ESP_LOGE(TAG, "Connection closed, errno = %d", errno); + break; + } + if (esp_http_client_is_complete_data_received(client) == true) { + ESP_LOGI(TAG, "Connection closed"); + break; + } + } + } + + ESP_LOGI(TAG, "esp_tee_ota_write succeeded"); + ESP_LOGI(TAG, "Total binary data written: %lu", curr_write_offset); + + if (esp_http_client_is_complete_data_received(client) != true) { + ESP_LOGE(TAG, "Error in receiving complete file"); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + + err = esp_tee_ota_end(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); + } + ESP_LOGI(TAG, "esp_tee_ota_end succeeded"); + + /* Ending connection, freeing the semaphore */ + http_cleanup(client); + xSemaphoreGive(s_ota_mgmt); + + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return; +} + +static void user_ota_task(void *pvParameter) +{ + ESP_LOGI(TAG, "Starting User OTA task..."); + + esp_http_client_config_t config = {}; + if (setup_task_conn(&config, (const char *)pvParameter) != 0) { + ESP_LOGE(TAG, "Failed to setup OTA task"); + task_fatal_error(); + } + + esp_https_ota_config_t ota_config = { + .http_config = &config, + }; + ESP_LOGI(TAG, "Attempting to download update from %s", config.url); + esp_err_t ret = esp_https_ota(&ota_config); + if (ret == ESP_OK) { + ESP_LOGI(TAG, "OTA Succeed, Rebooting..."); + esp_restart(); + } else { + ESP_LOGE(TAG, "Firmware upgrade failed"); + } + + xSemaphoreGive(s_ota_mgmt); + task_fatal_error(); +} + +static void init_ota_sem(void) +{ + static bool first_call = true; + if (first_call) { + s_ota_mgmt = xSemaphoreCreateBinary(); + xSemaphoreGive(s_ota_mgmt); + first_call = false; + } +} + +static int create_ota_task(const char *url, const char *task_name, void (*ota_task)(void *)) +{ + init_ota_sem(); + if (xTaskCreate(ota_task, task_name, configMINIMAL_STACK_SIZE * 3, (void *)url, 5, NULL) != pdPASS) { + ESP_LOGE(TAG, "Task creation failed for %s", task_name); + return ESP_FAIL; + } + + return ESP_OK; +} + +static struct { + struct arg_str *url; + struct arg_end *end; +} tee_ota_args; + +static struct { + struct arg_str *url; + struct arg_end *end; +} user_ota_args; + +static int tee_app_ota_task(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &tee_ota_args); + if (nerrors != 0) { + arg_print_errors(stderr, tee_ota_args.end, argv[0]); + return ESP_ERR_INVALID_ARG; + } + return create_ota_task(tee_ota_args.url->sval[0], "tee_ota_task", &tee_ota_task); +} + +static int user_app_ota_task(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &user_ota_args); + if (nerrors != 0) { + arg_print_errors(stderr, user_ota_args.end, argv[0]); + return ESP_ERR_INVALID_ARG; + } + return create_ota_task(user_ota_args.url->sval[0], "user_ota_task", &user_ota_task); +} + +void register_srv_tee_ota(void) +{ + tee_ota_args.url = arg_str1(NULL, NULL, "<url>", "URL for fetching the update"); + tee_ota_args.end = arg_end(2); + + const esp_console_cmd_t tee_ota_cmd = { + .command = "tee_ota", + .help = "Initiate TEE app OTA", + .hint = NULL, + .func = &tee_app_ota_task, + .argtable = &tee_ota_args, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_ota_cmd)); +} + +void register_srv_user_ota(void) +{ + user_ota_args.url = arg_str1(NULL, NULL, "<url>", "URL for fetching the update"); + user_ota_args.end = arg_end(2); + + const esp_console_cmd_t user_ota_cmd = { + .command = "user_ota", + .help = "Initiate User app OTA", + .hint = NULL, + .func = &user_app_ota_task, + .argtable = &user_ota_args, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&user_ota_cmd)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c new file mode 100644 index 000000000000..fad89de36f38 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c @@ -0,0 +1,547 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "esp_event.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_console.h" +#include "argtable3/argtable3.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "esp_tee_sec_storage.h" +#include "example_tee_srv.h" + +#define SHA256_DIGEST_SZ (32) +#define ECDSA_SECP256R1_KEY_LEN (32) + +#define AES256_GCM_TAG_LEN (16) +#define MAX_AES_PLAINTEXT_LEN (256) + +static const char *TAG = "tee_sec_stg"; + +static esp_err_t hexstr_to_hexbuf(const char *hexstr, size_t hexstr_len, void *hexbuf, size_t hexbuf_sz) +{ + if (hexstr == NULL || hexbuf == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (hexstr_len == 0 || hexbuf_sz < (hexstr_len / 2)) { + return ESP_ERR_INVALID_SIZE; + } + + const uint8_t *const_bytes = (const uint8_t *)hexstr; + uint8_t *bytes = (uint8_t *)hexbuf; + + for (size_t i = 0; i < hexstr_len; i += 2) { + uint8_t upper_nibble = (const_bytes[i] >= 'a') ? (const_bytes[i] - 'a' + 10) : (const_bytes[i] - '0'); + uint8_t lower_nibble = (const_bytes[i + 1] >= 'a') ? (const_bytes[i + 1] - 'a' + 10) : (const_bytes[i + 1] - '0'); + + bytes[i / 2] = (upper_nibble << 4) | (lower_nibble); + } + + return ESP_OK; +} + +static esp_err_t hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len) +{ + if (hexbuf == NULL || hexstr == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (hexbuf_sz == 0 || hexstr_len < (hexbuf_sz * 2 + 1)) { + return ESP_ERR_INVALID_SIZE; + } + + const uint8_t *bytes = (const uint8_t *)hexbuf; + + for (size_t i = 0; i < hexbuf_sz; i++) { + for (int shift = 0; shift < 2; shift++) { + uint8_t nibble = (bytes[i] >> (shift ? 0 : 4)) & 0x0F; + if (nibble < 10) { + hexstr[i * 2 + shift] = '0' + nibble; + } else { + hexstr[i * 2 + shift] = 'a' + nibble - 10; + } + } + } + hexstr[hexbuf_sz * 2] = '\0'; + + return ESP_OK; +} + +static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign) +{ + if (pubkey == NULL || digest == NULL || sign == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (len == 0) { + return ESP_ERR_INVALID_SIZE; + } + + esp_err_t err = ESP_FAIL; + + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + mbedtls_ecdsa_context ecdsa_context; + mbedtls_ecdsa_init(&ecdsa_context); + + int ret = mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1); + if (ret != 0) { + goto exit; + } + + size_t plen = mbedtls_mpi_size(&ecdsa_context.MBEDTLS_PRIVATE(grp).P); + + ret = mbedtls_mpi_read_binary(&r, sign->sign_r, plen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_read_binary(&s, sign->sign_s, plen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pubkey->pub_x, plen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pubkey->pub_y, plen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), digest, len, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s); + if (ret != 0) { + goto exit; + } + + err = ESP_OK; + +exit: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + mbedtls_ecdsa_free(&ecdsa_context); + + return err; +} + +static struct { + struct arg_str *msg; + struct arg_end *end; +} cmd_get_msg_sha256_args; + +static int get_msg_sha256(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &cmd_get_msg_sha256_args); + if (nerrors != 0) { + arg_print_errors(stderr, cmd_get_msg_sha256_args.end, argv[0]); + return ESP_ERR_INVALID_ARG; + } + + const char *msg = (const char *)cmd_get_msg_sha256_args.msg->sval[0]; + + uint8_t msg_digest[SHA256_DIGEST_SZ]; + int ret = mbedtls_sha256((const unsigned char *)msg, strlen(msg), msg_digest, false); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to calculate message hash!"); + return ESP_FAIL; + } + + size_t digest_hexstr_len = SHA256_DIGEST_SZ * 2 + 1; + char *digest_hexstr = calloc(digest_hexstr_len, sizeof(char)); + if (digest_hexstr == NULL) { + return ESP_ERR_NO_MEM; + } + + hexbuf_to_hexstr(msg_digest, sizeof(msg_digest), digest_hexstr, digest_hexstr_len); + ESP_LOGI(TAG, "Message digest (SHA256) -\n%s", digest_hexstr); + free(digest_hexstr); + + return ESP_OK; +} + +void register_cmd_msg_sha256(void) +{ + cmd_get_msg_sha256_args.msg = arg_str1(NULL, NULL, "\"<msg>\"", "Message for SHA256 digest calculation"); + cmd_get_msg_sha256_args.end = arg_end(2); + + const esp_console_cmd_t cmd_get_msg_sha256 = { + .command = "get_msg_sha256", + .help = "Get the SHA256 digest for the given message", + .hint = NULL, + .func = &get_msg_sha256, + .argtable = &cmd_get_msg_sha256_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_get_msg_sha256)); +} + +static struct { + struct arg_int *slot_id; + struct arg_int *key_type; + struct arg_end *end; +} tee_sec_stg_gen_key_args; + +static int tee_sec_stg_gen_key(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_gen_key_args); + if (nerrors != 0) { + arg_print_errors(stderr, tee_sec_stg_gen_key_args.end, argv[0]); + return 1; + } + + esp_err_t err = ESP_FAIL; + uint16_t slot_id = (uint16_t)tee_sec_stg_gen_key_args.slot_id->ival[0]; + esp_tee_sec_storage_type_t key_type = (esp_tee_sec_storage_type_t)tee_sec_stg_gen_key_args.key_type->ival[0]; + + err = esp_tee_sec_storage_init(); + if (err != ESP_OK) { + goto exit; + } + + err = esp_tee_sec_storage_clear_slot(slot_id); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to clear slot %d!", slot_id); + goto exit; + } + + err = esp_tee_sec_storage_gen_key(slot_id, key_type); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate key!"); + goto exit; + } + + ESP_LOGI(TAG, "Generated %s key in slot %d", + (key_type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) ? "ECDSA_SECP256R1" : "AES256", + slot_id); + +exit: + return err; +} + +void register_srv_sec_stg_gen_key(void) +{ + tee_sec_stg_gen_key_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot for storing the key"); + tee_sec_stg_gen_key_args.key_type = arg_int1(NULL, NULL, "<key_type>", "Key type (0: ECDSA_SECP256R1, 1: AES256)"); + tee_sec_stg_gen_key_args.end = arg_end(2); + + const esp_console_cmd_t tee_sec_stg = { + .command = "tee_sec_stg_gen_key", + .help = "Generate and store a new key of the specified type in the given TEE secure storage slot", + .hint = NULL, + .func = &tee_sec_stg_gen_key, + .argtable = &tee_sec_stg_gen_key_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg)); +} + +static struct { + struct arg_int *slot_id; + struct arg_str *msg_sha256; + struct arg_end *end; +} tee_sec_stg_sign_args; + +static int tee_sec_stg_sign(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &tee_sec_stg_sign_args); + if (nerrors != 0) { + arg_print_errors(stderr, tee_sec_stg_sign_args.end, argv[0]); + return ESP_ERR_INVALID_ARG; + } + + const char *msg_sha256 = (const char *)tee_sec_stg_sign_args.msg_sha256->sval[0]; + size_t msg_sha256_len = strnlen(msg_sha256, SHA256_DIGEST_SZ * 2 + 1); + if (msg_sha256_len != SHA256_DIGEST_SZ * 2) { + ESP_LOGE(TAG, "Invalid input digest!"); + return ESP_ERR_INVALID_ARG; + } + + uint8_t digest[SHA256_DIGEST_SZ] = {}; + hexstr_to_hexbuf(msg_sha256, msg_sha256_len, digest, sizeof(digest)); + + esp_err_t err = ESP_FAIL; + uint16_t slot_id = (uint16_t)tee_sec_stg_sign_args.slot_id->ival[0]; + + err = esp_tee_sec_storage_init(); + if (err != ESP_OK) { + goto exit; + } + + esp_tee_sec_storage_sign_t sign = {}; + err = esp_tee_sec_storage_get_signature(slot_id, digest, sizeof(digest), &sign); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate signature!"); + goto exit; + } + + size_t sign_hexstr_len = (ECDSA_SECP256R1_KEY_LEN * 2) * 2 + 1; + char *sign_hexstr = calloc(sign_hexstr_len, sizeof(char)); + if (sign_hexstr == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + hexbuf_to_hexstr(&sign, sizeof(sign), sign_hexstr, sign_hexstr_len); + ESP_LOGI(TAG, "Generated signature -\n%s", sign_hexstr); + free(sign_hexstr); + + esp_tee_sec_storage_pubkey_t pubkey = {}; + err = esp_tee_sec_storage_get_pubkey(slot_id, &pubkey); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to fetch public-key!"); + goto exit; + } + + size_t pubkey_hexstr_len = (ECDSA_SECP256R1_KEY_LEN * 2) * 2 + 1; + char *pubkey_hexstr = calloc(pubkey_hexstr_len, sizeof(char)); + if (pubkey_hexstr == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + hexbuf_to_hexstr(&pubkey, sizeof(pubkey), pubkey_hexstr, pubkey_hexstr_len); + ESP_LOGI(TAG, "Public key (Uncompressed) -\n04%s", pubkey_hexstr); + free(pubkey_hexstr); + + err = verify_ecdsa_secp256r1_sign(digest, sizeof(digest), &pubkey, &sign); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify signature!"); + goto exit; + } + + ESP_LOGI(TAG, "Signature verified successfully!"); + +exit: + return err; +} + +void register_srv_sec_stg_sign(void) +{ + tee_sec_stg_sign_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the ecdsa-secp256r1 keypair"); + tee_sec_stg_sign_args.msg_sha256 = arg_str1(NULL, NULL, "<msg_sha256>", "SHA256 digest of the message to be signed and verified"); + tee_sec_stg_sign_args.end = arg_end(2); + + const esp_console_cmd_t tee_sec_stg = { + .command = "tee_sec_stg_sign", + .help = "Sign a message using the ECDSA keypair stored in the given slot ID and verify the signature", + .hint = NULL, + .func = &tee_sec_stg_sign, + .argtable = &tee_sec_stg_sign_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg)); +} + +static struct { + struct arg_int *slot_id; + struct arg_str *plaintext; + struct arg_end *end; +} tee_sec_stg_encrypt_args; + +static int tee_sec_stg_encrypt(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_encrypt_args); + if (nerrors != 0) { + arg_print_errors(stderr, tee_sec_stg_encrypt_args.end, argv[0]); + return 1; + } + + esp_err_t err = ESP_FAIL; + uint8_t tag[AES256_GCM_TAG_LEN]; + uint16_t slot_id = (uint16_t)tee_sec_stg_encrypt_args.slot_id->ival[0]; + + const char *plaintext = tee_sec_stg_encrypt_args.plaintext->sval[0]; + size_t plaintext_len = strnlen(plaintext, MAX_AES_PLAINTEXT_LEN); + if (plaintext_len == MAX_AES_PLAINTEXT_LEN && plaintext[MAX_AES_PLAINTEXT_LEN] != '\0') { + ESP_LOGE(TAG, "Plaintext too long (max - %d bytes)", MAX_AES_PLAINTEXT_LEN); + err = ESP_ERR_INVALID_ARG; + goto exit; + } + + if (plaintext_len % 2 != 0) { + ESP_LOGE(TAG, "Invalid plaintext - should be a hex string"); + err = ESP_ERR_INVALID_ARG; + goto exit; + } + + size_t plaintext_buf_len = plaintext_len / 2; + uint8_t *plaintext_buf = calloc(plaintext_buf_len, sizeof(uint8_t)); + if (plaintext_buf == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + hexstr_to_hexbuf(plaintext, plaintext_len, plaintext_buf, plaintext_buf_len); + + uint8_t *ciphertext_buf = calloc(plaintext_buf_len, sizeof(uint8_t)); + if (ciphertext_buf == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + err = esp_tee_sec_storage_init(); + if (err != ESP_OK) { + goto exit; + } + + err = esp_tee_sec_storage_encrypt(slot_id, (uint8_t *)plaintext_buf, plaintext_buf_len, + NULL, 0, tag, sizeof(tag), ciphertext_buf); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt data: %s", esp_err_to_name(err)); + goto exit; + } + + char *ciphertext = calloc(plaintext_len + 1, sizeof(char)); + if (ciphertext == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + hexbuf_to_hexstr(ciphertext_buf, plaintext_buf_len, ciphertext, plaintext_len + 1); + + char tag_hexstr[AES256_GCM_TAG_LEN * 2 + 1]; + hexbuf_to_hexstr(tag, sizeof(tag), tag_hexstr, sizeof(tag_hexstr)); + + ESP_LOGI(TAG, "Ciphertext -\n%s", ciphertext); + ESP_LOGI(TAG, "Tag -\n%s", tag_hexstr); + + free(plaintext_buf); + free(ciphertext_buf); + free(ciphertext); + +exit: + return err; +} + +void register_srv_sec_stg_encrypt(void) +{ + tee_sec_stg_encrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key"); + tee_sec_stg_encrypt_args.plaintext = arg_str1(NULL, NULL, "<plaintext>", "Plaintext to be encrypted"); + tee_sec_stg_encrypt_args.end = arg_end(2); + + const esp_console_cmd_t tee_sec_stg = { + .command = "tee_sec_stg_encrypt", + .help = "Encrypt data using AES-GCM with a key from secure storage", + .hint = NULL, + .func = &tee_sec_stg_encrypt, + .argtable = &tee_sec_stg_encrypt_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg)); +} + +static struct { + struct arg_int *slot_id; + struct arg_str *ciphertext; + struct arg_str *tag; + struct arg_end *end; +} tee_sec_stg_decrypt_args; + +static int tee_sec_stg_decrypt(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_decrypt_args); + if (nerrors != 0) { + arg_print_errors(stderr, tee_sec_stg_decrypt_args.end, argv[0]); + return 1; + } + + esp_err_t err = ESP_FAIL; + uint16_t slot_id = (uint16_t)tee_sec_stg_decrypt_args.slot_id->ival[0]; + + const char *tag_hexstr = tee_sec_stg_decrypt_args.tag->sval[0]; + uint8_t tag[AES256_GCM_TAG_LEN]; + hexstr_to_hexbuf(tag_hexstr, strlen(tag_hexstr), tag, sizeof(tag)); + + const char *ciphertext = tee_sec_stg_decrypt_args.ciphertext->sval[0]; + size_t ciphertext_len = strnlen(ciphertext, MAX_AES_PLAINTEXT_LEN); + if (ciphertext_len == MAX_AES_PLAINTEXT_LEN && ciphertext[MAX_AES_PLAINTEXT_LEN] != '\0') { + ESP_LOGE(TAG, "Ciphertext too long (max - %d bytes)", MAX_AES_PLAINTEXT_LEN); + err = ESP_ERR_INVALID_ARG; + goto exit; + } + + if (ciphertext_len % 2 != 0) { + ESP_LOGE(TAG, "Invalid plaintext - should be a hex string"); + err = ESP_ERR_INVALID_ARG; + goto exit; + } + + size_t ciphertext_buf_len = ciphertext_len / 2; + uint8_t *ciphertext_buf = calloc(ciphertext_buf_len, sizeof(uint8_t)); + if (ciphertext_buf == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + hexstr_to_hexbuf(ciphertext, ciphertext_len, ciphertext_buf, ciphertext_buf_len); + + uint8_t *plaintext_buf = calloc(ciphertext_buf_len, sizeof(uint8_t)); + if (plaintext_buf == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + + err = esp_tee_sec_storage_init(); + if (err != ESP_OK) { + goto exit; + } + + err = esp_tee_sec_storage_decrypt(slot_id, ciphertext_buf, ciphertext_buf_len, + NULL, 0, tag, sizeof(tag), plaintext_buf); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to decrypt data: %s", esp_err_to_name(err)); + goto exit; + } + + char *plaintext = calloc(ciphertext_len + 1, sizeof(char)); + if (plaintext == NULL) { + err = ESP_ERR_NO_MEM; + goto exit; + } + hexbuf_to_hexstr(plaintext_buf, ciphertext_buf_len, plaintext, ciphertext_len + 1); + + ESP_LOGI(TAG, "Decrypted plaintext -\n%s", plaintext); + + free(ciphertext_buf); + free(plaintext_buf); + free(plaintext); + +exit: + return err; +} + +void register_srv_sec_stg_decrypt(void) +{ + tee_sec_stg_decrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key"); + tee_sec_stg_decrypt_args.ciphertext = arg_str1(NULL, NULL, "<ciphertext>", "Ciphertext to be decrypted"); + tee_sec_stg_decrypt_args.tag = arg_str1(NULL, NULL, "<tag>", "AES-GCM authentication tag"); + tee_sec_stg_decrypt_args.end = arg_end(3); + + const esp_console_cmd_t tee_sec_stg = { + .command = "tee_sec_stg_decrypt", + .help = "Decrypt data using AES-GCM with a key from secure storage", + .hint = NULL, + .func = &tee_sec_stg_decrypt, + .argtable = &tee_sec_stg_decrypt_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py new file mode 100644 index 000000000000..ef948dbc7b58 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py @@ -0,0 +1,242 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import hashlib +import http.server +import json +import logging +import multiprocessing +import os +import socket +import ssl +import time +from typing import Any +from typing import Callable + +import pexpect +import pytest +from common_test_methods import get_env_config_variable +from common_test_methods import get_host_ip4_by_dest_ip +from cryptography.hazmat.primitives.asymmetric import utils +from ecdsa.curves import NIST256p +from ecdsa.keys import VerifyingKey +from ecdsa.util import sigdecode_der +from pytest_embedded import Dut +from RangeHTTPServer import RangeRequestHandler + +TEST_MSG = 'hello world' + +server_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_cert.pem') +key_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_key.pem') + + +########################### +# ESP-TEE: Secure Storage # +########################### + + +@pytest.mark.esp32c6 +@pytest.mark.generic +def test_tee_cli_secure_storage(dut: Dut) -> None: + # Dumping the REE binary size + binary_file = os.path.join(dut.app.binary_path, 'tee_cli.bin') + bin_size = os.path.getsize(binary_file) + logging.info('tee_cli_bin_size : {}KB'.format(bin_size // 1024)) + + # Starting the test + dut.expect('ESP-TEE: Secure services demonstration', timeout=30) + time.sleep(1) + + # Get the SHA256 digest of the test message + dut.write(f'get_msg_sha256 "{TEST_MSG}"') + test_msg_hash = dut.expect(r'Message digest \(SHA256\) -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode() + time.sleep(1) + + # Test out the TEE secure storage workflow - Message signing and verification + iterations = 3 + sec_stg_slots = {0: 0, 1: 14, 2: 7} + for i in range(iterations): + dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 0') + dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30) + + dut.write(f'tee_sec_stg_sign {sec_stg_slots.get(i)} {test_msg_hash}') + test_msg_sign = dut.expect(r'Generated signature -\s*([0-9a-fA-F]{128})', timeout=30)[1].decode() + test_msg_pubkey = dut.expect(r'Public key \(Uncompressed\) -\s*([0-9a-fA-F]{130})', timeout=30)[1].decode() + dut.expect('Signature verified successfully', timeout=30) + + vk = VerifyingKey.from_string(bytes.fromhex(test_msg_pubkey), curve=NIST256p, hashfunc=hashlib.sha256) + assert vk.verify_digest(bytes.fromhex(test_msg_sign), bytes.fromhex(test_msg_hash)) + time.sleep(1) + + # Test out the TEE secure storage workflow - Encryption and decryption + sec_stg_slots = {0: 1, 1: 14, 2: 9} + for i in range(iterations): + dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 1') + dut.expect(r'Generated AES256 key in slot (\d+)', timeout=30) + + dut.write(f'tee_sec_stg_encrypt {sec_stg_slots.get(i)} {test_msg_hash}') + test_msg_cipher = dut.expect(r'Ciphertext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode() + test_msg_tag = dut.expect(r'Tag -\s*([0-9a-fA-F]{32})', timeout=30)[1].decode() + + dut.write(f'tee_sec_stg_decrypt {sec_stg_slots.get(i)} {test_msg_cipher} {test_msg_tag}') + test_msg_decipher = dut.expect(r'Decrypted plaintext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode() + + assert (test_msg_decipher == test_msg_hash) + time.sleep(1) + +######################## +# ESP-TEE: Attestation # +######################## + + +def verify_att_token_signature(att_tk: str) -> Any: + # Parsing the token + tk_info = json.loads(att_tk) + + # Fetching the data to be verified + tk_hdr_val = json.dumps(tk_info['header'], separators=(',', ':')).encode('latin-1') + tk_eat_val = json.dumps(tk_info['eat'], separators=(',', ':')).encode('latin-1') + tk_pubkey_val = json.dumps(tk_info['public_key'], separators=(',', ':')).encode('latin-1') + + # Pre-hashing the data + ctx = hashlib.new('sha256') + ctx.update(tk_hdr_val) + ctx.update(tk_eat_val) + ctx.update(tk_pubkey_val) + digest = ctx.digest() + + # Fetching the public key + tk_pubkey_c = bytes.fromhex(tk_info['public_key']['compressed']) + + # Fetching the appended signature + tk_sign_r = bytes.fromhex(tk_info['sign']['r']) + tk_sign_s = bytes.fromhex(tk_info['sign']['s']) + + # Construct the signature using the R and S components + signature = utils.encode_dss_signature(int.from_bytes(tk_sign_r, 'big'), int.from_bytes(tk_sign_s, 'big')) + + # Uncompress the public key and verify the signature + vk = VerifyingKey.from_string(tk_pubkey_c, NIST256p, hashfunc=hashlib.sha256) + return vk.verify_digest(signature, digest, sigdecode=sigdecode_der) + + +@pytest.mark.esp32c6 +@pytest.mark.generic +def test_tee_cli_attestation(dut: Dut) -> None: + # Dumping the REE binary size + binary_file = os.path.join(dut.app.binary_path, 'tee_cli.bin') + bin_size = os.path.getsize(binary_file) + logging.info('tee_cli_bin_size : {}KB'.format(bin_size // 1024)) + + # Starting the test + dut.expect('ESP-TEE: Secure services demonstration', timeout=30) + time.sleep(1) + + att_key_slot = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_SLOT_ID') + dut.write(f'tee_sec_stg_gen_key {att_key_slot} 0') + dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30) + + # Get the Entity Attestation token from TEE and verify its signature + dut.write('tee_att_info') + dut.expect(r'Attestation token - Length: (\d+)', timeout=30) + att_tk = dut.expect(r"'(.*?)'", timeout=30)[1].decode() + assert verify_att_token_signature(att_tk) + +####################################### +# ESP-TEE: Over-the-Air (OTA) updates # +####################################### + + +def https_request_handler() -> Callable[...,http.server.BaseHTTPRequestHandler]: + """ + Returns a request handler class that handles broken pipe exception + """ + class RequestHandler(RangeRequestHandler): + def finish(self) -> None: + try: + if not self.wfile.closed: + self.wfile.flush() + self.wfile.close() + except socket.error: + pass + self.rfile.close() + + def handle(self) -> None: + try: + RangeRequestHandler.handle(self) + except socket.error: + pass + + return RequestHandler + + +def start_https_server(ota_image_dir: str, server_ip: str, server_port: int) -> None: + os.chdir(ota_image_dir) + requestHandler = https_request_handler() + httpd = http.server.HTTPServer((server_ip, server_port), requestHandler) + + httpd.socket = ssl.wrap_socket(httpd.socket, + keyfile=key_file, + certfile=server_file, server_side=True) + httpd.serve_forever() + + +@pytest.mark.esp32c6 +@pytest.mark.wifi_high_traffic +def test_tee_cli_secure_ota_wifi(dut: Dut) -> None: + """ + This is a positive test case, which downloads complete binary file multiple number of times. + Number of iterations can be specified in variable iterations. + steps: + 1. join AP + 2. Fetch TEE/REE OTA image over HTTPS + 3. Reboot with the new TEE OTA image + """ + # Number of iterations to validate OTA + iterations = 4 + server_port = 8001 + tee_bin = 'esp_tee/esp_tee.bin' + user_bin = 'tee_cli.bin' + + # Start server + thread1 = multiprocessing.Process(target=start_https_server, args=(dut.app.binary_path, '0.0.0.0', server_port)) + thread1.daemon = True + thread1.start() + time.sleep(1) + + try: + # start test + for i in range(iterations): + # Boot up sequence checks + dut.expect('Loaded TEE app from partition at offset', timeout=30) + dut.expect('Loaded app from partition at offset', timeout=30) + + # Starting the test + dut.expect('ESP-TEE: Secure services demonstration', timeout=30) + time.sleep(1) + + # Connecting to Wi-Fi + env_name = 'wifi_high_traffic' + ap_ssid = get_env_config_variable(env_name, 'ap_ssid') + ap_password = get_env_config_variable(env_name, 'ap_password') + dut.write(f'wifi_connect {ap_ssid} {ap_password}') + + # Fetch the DUT IP address + try: + ip_address = dut.expect(r'got ip:(\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode() + print('Connected to AP/Ethernet with IP: {}'.format(ip_address)) + except pexpect.exceptions.TIMEOUT: + raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP') + + host_ip = get_host_ip4_by_dest_ip(ip_address) + + # User OTA for last iteration + if i == (iterations - 1): + dut.write(f'user_ota https://{host_ip}:{str(server_port)}/{user_bin}') + dut.expect('OTA Succeed, Rebooting', timeout=150) + else: + dut.write(f'tee_ota https://{host_ip}:{str(server_port)}/{tee_bin}') + dut.expect('esp_tee_ota_end succeeded', timeout=150) + dut.expect('Prepare to restart system!', timeout=150) + + finally: + thread1.terminate() diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe new file mode 100644 index 000000000000..7ae136948890 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe @@ -0,0 +1,15 @@ +# Security features - build-only configuration +CONFIG_PARTITION_TABLE_OFFSET=0xf000 + +# Secure Boot +CONFIG_SECURE_BOOT=y +CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y +CONFIG_SECURE_BOOT_SIGNING_KEY="test_keys/secure_boot_signing_key.pem" + +# Flash Encryption +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y + +# TEE Secure Storage: Release mode +CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y +CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK=9 diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults new file mode 100644 index 000000000000..f6cf613bd128 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults @@ -0,0 +1,20 @@ +# Enabling TEE +CONFIG_SECURE_ENABLE_TEE=y +CONFIG_SECURE_TEE_IRAM_SIZE=0x9000 + +# Custom partition table +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA_TEE=y + +# TEE: Flash operations' limitation +CONFIG_FREERTOS_UNICORE=y + +# TEE: OTA-related +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y +CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=45000 + +# Custom certificates for testing +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y +CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="test_certs/server_cert.pem" diff --git a/components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem new file mode 100644 index 000000000000..b29ba7ab1f54 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWDCCAkACCQCbF4+gVh/MLjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJJ +TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD +VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j +b20wHhcNMjEwNzEyMTIzNjI3WhcNNDEwNzA3MTIzNjI3WjBuMQswCQYDVQQGEwJJ +TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD +VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhxF/y7bygndxPwiWL +SwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQuc32W +ukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2mKRbQ +S5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO2fEz +YaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnvL6Oz +3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdOAoap +rFTRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAItw24y565k3C/zENZlxyzto44ud +IYPQXN8Fa2pBlLe1zlSIyuaA/rWQ+i1daS8nPotkCbWZyf5N8DYaTE4B0OfvoUPk +B5uGDmbuk6akvlB5BGiYLfQjWHRsK9/4xjtIqN1H58yf3QNROuKsPAeywWS3Fn32 +3//OpbWaClQePx6udRYMqAitKR+QxL7/BKZQsX+UyShuq8hjphvXvk0BW8ONzuw9 +RcoORxM0FzySYjeQvm4LhzC/P3ZBhEq0xs55aL2a76SJhq5hJy7T/Xz6NFByvlrN +lFJJey33KFrAf5vnV9qcyWFIo7PYy2VsaaEjFeefr7q3sTFSMlJeadexW2Y= +-----END CERTIFICATE----- diff --git a/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem new file mode 100644 index 000000000000..20a4bdb624f0 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhxF/y7bygndxP +wiWLSwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQu +c32WukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2m +KRbQS5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO +2fEzYaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnv +L6Oz3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdO +AoaprFTRAgMBAAECggEAE0HCxV/N1Q1h+1OeDDGL5+74yjKSFKyb/vTVcaPCrmaH +fPvp0ddOvMZJ4FDMAsiQS6/n4gQ7EKKEnYmwTqj4eUYW8yxGUn3f0YbPHbZT+Mkj +z5woi3nMKi/MxCGDQZX4Ow3xUQlITUqibsfWcFHis8c4mTqdh4qj7xJzehD2PVYF +gNHZsvVj6MltjBDAVwV1IlGoHjuElm6vuzkfX7phxcA1B4ZqdYY17yCXUnvui46z +Xn2kUTOOUCEgfgvGa9E+l4OtdXi5IxjaSraU+dlg2KsE4TpCuN2MEVkeR5Ms3Y7Q +jgJl8vlNFJDQpbFukLcYwG7rO5N5dQ6WWfVia/5XgQKBgQD74at/bXAPrh9NxPmz +i1oqCHMDoM9sz8xIMZLF9YVu3Jf8ux4xVpRSnNy5RU1gl7ZXbpdgeIQ4v04zy5aw +8T4tu9K3XnR3UXOy25AK0q+cnnxZg3kFQm+PhtOCKEFjPHrgo2MUfnj+EDddod7N +JQr9q5rEFbqHupFPpWlqCa3QmQKBgQDldWUGokNaEpmgHDMnHxiibXV5LQhzf8Rq +gJIQXb7R9EsTSXEvsDyqTBb7PHp2Ko7rZ5YQfyf8OogGGjGElnPoU/a+Jij1gVFv +kZ064uXAAISBkwHdcuobqc5EbG3ceyH46F+FBFhqM8KcbxJxx08objmh58+83InN +P9Qr25Xw+QKBgEGXMHuMWgQbSZeM1aFFhoMvlBO7yogBTKb4Ecpu9wI5e3Kan3Al +pZYltuyf+VhP6XG3IMBEYdoNJyYhu+nzyEdMg8CwXg+8LC7FMis/Ve+o7aS5scgG +1to/N9DK/swCsdTRdzmc/ZDbVC+TuVsebFBGYZTyO5KgqLpezqaIQrTxAoGALFCU +10glO9MVyl9H3clap5v+MQ3qcOv/EhaMnw6L2N6WVT481tnxjW4ujgzrFcE4YuxZ +hgwYu9TOCmeqopGwBvGYWLbj+C4mfSahOAs0FfXDoYazuIIGBpuv03UhbpB1Si4O +rJDfRnuCnVWyOTkl54gKJ2OusinhjztBjcrV1XkCgYEA3qNi4uBsPdyz9BZGb/3G +rOMSw0CaT4pEMTLZqURmDP/0hxvTk1polP7O/FYwxVuJnBb6mzDa0xpLFPTpIAnJ +YXB8xpXU69QVh+EBbemdJWOd+zp5UCfXvb2shAeG3Tn/Dz4cBBMEUutbzP+or0nG +vSXnRLaxQhooWm+IuX9SuBQ= +-----END PRIVATE KEY----- diff --git a/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem b/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem new file mode 100644 index 000000000000..405f3a01e796 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEAvB+DrHYuY3yM7fiBPpxE7ZZqde/ULn5eG7vlHF+/2sblb2LC +o/sdWRcImSOzbbVG8wYPCqtCG3FeQzhsdAKapd8ha7+YQcIvqPgJMfOSIfsYdFua +qNhAzAj/2zBxajzHfdtFPVjdWhW9tIztv7uTlkXoIsy2ty0De6uwOCVBTbvpJ9JU +g03IIV5h20J3qAf6ODRA/bi2P8r1muxgs+hhjP4V90h6149Kwv82B3A0eV5a9hG/ +4Ut4uX9jNYn/KU+0zEGif0EQMjcKOc3F0ruuiaHdAJLJ6yK4MhM8Rgmfdy7okdfF +Q7SrliFW/1Crys4iCxShRffCYNGgKgwMpaK6qrqwVf56Geu+4uk8/TOhlOg7Tyvo +5tpafvdcJo4y+mX6Bb/5ErskCaRWCsb0733rbWaBkrEDNZ/dJmkvByu7JWqGGHmN +rbiiop60+pz7N8jcBwPtAlUGH3aKEMvX3RTbjrug4cFTQvsIM3kJaklhmv0UDFhg +zeGu6Pm9w+f/JlV5AgMBAAECggGAAV72OReGlwSdWLUEgoEZCRidH5oaOoyCzkKJ +FOZA7uAFymrQO4NLX3U8UGfJZijgCRb0nRtKZaQAQw6/Afyfq+QuyV47Rp9np+kt +Oy2PO96Px9x/1m/2nB+kqn0PFofpQqpXNgjW9B62CmhmHMBjmI2Oqrin/2wjwY73 +jDEy6W7pI0Unhuo8mLtBwBtOS+2WJ6EZ21kXp7dbutlMixyybH8D7sPODiwZKC3V +jJBT9/gPQz1pEvc91fw7rA3Py9LgENDbSlPlu0qx9vKvcr3QaGVS5Mghi+g4vpZH +Txyz96dSHoQOJ+1NFTZQn+P3TEpXH9YabBodyAJzeWFswYJLVgJw4S48BwsKDNje +Kx8z0dUe/8mK1LLO+c8GArAe36gAGr9PO/o3WZC7ZNdDuTnl0l6Ccg4+JzUwaL2y +s5eNt7dGSyl40xnG0J2+eVmVdECVdLHRsGVqlmp4wqtUSirBwUx9F+M1iIELPvmp +O6AexJLeX8uSBVYgBkRMVMqq6hzRAoHBAO7Aft5ctKbTwr0eff4pQb3O1DB1WUsM +U5L13cob9fpVSNOiydK2armJeKZI2TSryY+dkY0TF1aIvQ+lD0leWVON8TgYwTGU +He0hwhb23+ZQC6VKCzGTIwqCw/1hY6cJXaTAtZl7JCx3Nfj1s2MQTZND+aX65Uba +FQ+skuszLYzTS/5q0ooi7VLLUGtAqnkjFuskLlawgj3PY51HEZaivftsdCapLmoc +eWefu7X2Qok/VjvdQakaPe+jWoHaGiwL3wKBwQDJtq8AGDY+ZuTWYvROGz4oYH53 +BC213d6dEMZeVX+zsMta1/+9LwZ7k3QbZOF1GF4tMT21mlT/AtbCA6WYz0pzYagp +8zm5Ppm7LuwcY+q4z3xc1b4PvHTfcbCLFohDPd1fKsw3uNsac8st+voaWmbSPRv9 +frA8vWklC2GHQFH0QNEGB3LKto65A3p8dkGcG6KMLqedHSB8vlGUD2jZnC5E67Hj +q1J0bsWsfP9madoWcEMIeG7YX15TNvp5DMP4SacCgcEAz46NPKZ/bmC99Udi5Ofc +/TOniBEcdX/bz/DqxV+VcfFehI4KwqJPGckHGBhQq9eEPNQQywID0fjB0639Ih1h +rp/BSrFKkt1fPt2QLAKnF0QTO7ipDooJdoPD+q0FwxsDS800kp9ZDUb9pteDAeYU +aqg3ijIZzISetqeiedJeEQVIFX+sVOaURtv1p6fqC7GTpQwpNLfyIeX/haVZBwmn ++8GHIG0HqrviV1GoEEJsCYEEaLqq8GWd3oy2jBidlBklAoHAOSca1JMHo4yx4BCv +mgXcCcK1cnFrpjEUJzqeihH9meNI6xdybZ8KXi88YZqmyu+5l2JxUqhE1vBt+xDf +dbmeJa0Q4bn5rAQRuNilq7Gfyvd3DDK/pGPIVZs342DiAPNhatqMfGlTI16VvhRz +ks30fjM0YBHqS3t7dDSSUKknz2YiE2w+2TL6o29Z4DP9iQbHeqKpeTnMf6vJH4Ny +ON4oufxyLcjYFwdf2OydzN3HHj52r8q31XTIHBnixDOavxnXAoHBAIMS2ipFhYpk +aPlcnygqV4tlcmsy376OMqOv2Ggv/ZhNHnKZkmywEtw463o19ruIjZ2F2ik/FhRH +V94/PeLRRWn54mI6z6N7eZ1m96sgfPH0shH5a4yLFtCPpmVlBlUv0wj6FXlfqDaL +DtTzbY3i2dDskcx1ollMAoz/RJErhbxNGCQE1mtjld6lIb4gNd3DmZhkdGQS38rG +fvBa0fgPJBzVu1kfVcJemFJxOhpS/+MxbEfFw7AgIFIKGh0mviUBWA== +-----END RSA PRIVATE KEY----- diff --git a/components/esp_tee/test_apps/tee_cli_app/version.txt b/components/esp_tee/test_apps/tee_cli_app/version.txt new file mode 100644 index 000000000000..3eefcb9dd5b3 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/version.txt @@ -0,0 +1 @@ +1.0.0 diff --git a/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt new file mode 100644 index 000000000000..ac89d5cec6d4 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt @@ -0,0 +1,18 @@ +#This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +# This example uses extra components for the following - +# 1. Test framework related. +# 2. Managing TEE OTA updates +# 3. TEE Secure Storage +# 4. TEE Entity Attestation +list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components + $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_ota_ops + $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_sec_storage + $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_attestation) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/components/test_sec_srv/test_tee_project.cmake) + +project(esp_tee_test) diff --git a/components/esp_tee/test_apps/tee_test_fw/README.md b/components/esp_tee/test_apps/tee_test_fw/README.md new file mode 100644 index 000000000000..8259fa367e09 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/README.md @@ -0,0 +1,28 @@ +| Supported Targets | ESP32-C6 | +| ----------------- | -------- | + +## ESP-TEE: Test Suite + +- ESP-TEE utilizes the `pytest` framework in ESP-IDF for executing the dedicated unit tests on the target. The test application includes cases spanning the following modules - + - Secure service call interface + - Interrupts and exception handling + - Privilege violation + - Cryptographic operations + - TEE OTA updates + - Secure storage + - Attestation + +- For executing the test locally, ESP-IDF needs to be installed with the additional Python requirements. + +```bash +cd $IDF_PATH +bash install.sh --enable-ci --enable-pytest +. ./export.sh +``` + +- For example, to execute the TEE test suite for ESP32-C6 with all the available `sdkconfig` files, run the following steps. The required test applications will be built and flashed automatically onto the DUT by the `pytest` framework. + +```bash +python $IDF_PATH/tools/ci/ci_build_apps.py . --target esp32c6 -v --pytest-apps +pytest --target esp32c6 +``` diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt new file mode 100644 index 000000000000..a93e6bc7ef82 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.16) + +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) + +set(srcs) +set(priv_requires) + +if(esp_tee_build) + list(APPEND srcs "src/test_dummy_srv.c" + "src/test_interrupt.c" + "src/test_panic.c" + "src/test_sec_srv.c") + list(APPEND priv_requires main) +else() + list(APPEND srcs "src/test_dummy_srv_wrapper.c") + list(APPEND priv_requires esp_tee) +endif() + + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS include + PRIV_REQUIRES ${priv_requires}) diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h new file mode 100644 index 000000000000..0830bfb8bee7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#define TEE_TEST_INT_COUNT 3 + +uint32_t __attribute__((__noinline__)) esp_tee_service_add(uint32_t a, uint32_t b); + +uint32_t __attribute__((__noinline__)) esp_tee_service_sub(uint32_t a, uint32_t b); + +uint32_t __attribute__((__noinline__)) esp_tee_service_mul(uint32_t a, uint32_t b); + +uint32_t __attribute__((__noinline__)) esp_tee_service_div(uint32_t a, uint32_t b); + +int esp_tee_secure_int_test(void); + +int esp_tee_non_secure_int_test(volatile uint32_t* volatile ns_int_count); + +int esp_tee_test_int_count(uint32_t *secure_int_count); + +int esp_tee_test_store_prohibited(uint32_t type); + +int esp_tee_test_illegal_instr(void); + +int esp_tee_test_instr_fetch_prohibited(uint32_t type); + +void dummy_secure_service(void); + +uint32_t add_in_loop(uint32_t a, uint32_t b, uint32_t iter); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c new file mode 100644 index 000000000000..24faeb5e416b --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "secure_service_num.h" +#include "esp_tee.h" +#include "esp_err.h" +#include "esp_rom_sys.h" + +void _ss_dummy_secure_service(void) +{ + esp_rom_printf("Dummy secure service\n"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c new file mode 100644 index 000000000000..ae40a9419385 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <stdarg.h> +#include "secure_service_num.h" +#include "esp_tee.h" +#include "esp_err.h" + +void dummy_secure_service(void) +{ + esp_tee_service_call(1, SS_DUMMY_SECURE_SERVICE); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c new file mode 100644 index 000000000000..c5877e009ace --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c @@ -0,0 +1,174 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/timer_group_reg.h" +#include "esp_tee.h" +#include "esp_log.h" +#include "esp_tee_test.h" + +#include "riscv/csr.h" +#include "soc/interrupt_matrix_reg.h" + +#include "esp_tee_intr.h" +#include "hal/timer_ll.h" +#include "hal/clk_gate_ll.h" +#include "soc/timer_group_reg.h" + +#define TIMER_DIVIDER 80 // Hardware timer clock divider +#define TIMER_RESOLUTION_HZ 1000000 // 1MHz resolution +#define TIMER_ALARM_PERIOD_S 0.10 // sample test interval for the first timer + +/* TEE uses Group0 Timer0 */ +#define TEE_SECURE_TIMER 0 + +static const char *TAG = "esp_tee_intr_test"; + +static timg_dev_t *timg_hw = (&TIMERG0); + +uint32_t *psecure_int_count = NULL; + +static void IRAM_ATTR timer_group0_isr(void *arg) +{ + ESP_LOGI(TAG, "Timer ISR Handler from World %d!", esp_cpu_get_curr_privilege_level()); + + /* For interrupt test. */ + *psecure_int_count = *psecure_int_count + 1; + + /* Clear interrupt */ + timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER)); + + /* Re-enable the alarm. */ + timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, true); +} + +static void tee_timer_enable(void) +{ + struct vector_desc_t timer_vd = { 0, NULL, NULL, NULL }; + + // init timer_vc + timer_vd.source = ETS_TG0_T0_LEVEL_INTR_SOURCE; + timer_vd.isr = timer_group0_isr; + + ESP_LOGI(TAG, "Enabling test timer from secure world"); + + /* Enable TG0 peripheral module */ + periph_ll_enable_clk_clear_rst(PERIPH_TIMG0_MODULE); + + /* Stop counter, alarm, auto-reload at first place */ + timer_ll_enable_clock(timg_hw, TEE_SECURE_TIMER, true); + timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, false); + timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, false); + timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, false); + + // Set clock source + timer_ll_set_clock_source(timg_hw, TEE_SECURE_TIMER, GPTIMER_CLK_SRC_DEFAULT); + timer_ll_set_clock_prescale(timg_hw, TEE_SECURE_TIMER, TIMER_DIVIDER); + + // Initialize counter value to zero + timer_ll_set_reload_value(timg_hw, TEE_SECURE_TIMER, 0); + timer_ll_trigger_soft_reload(timg_hw, TEE_SECURE_TIMER); + + // set counting direction + timer_ll_set_count_direction(timg_hw, TEE_SECURE_TIMER, GPTIMER_COUNT_UP); + + // disable interrupt + timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), false); + // clear pending interrupt event + timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER)); + + esp_tee_intr_register((void *)&timer_vd); + timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), true); + + timer_ll_set_reload_value(timg_hw, TEE_SECURE_TIMER, 0); + + // enable timer interrupt + timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), true); + + // set timer alarm + uint64_t alarm_value = (TIMER_ALARM_PERIOD_S * TIMER_RESOLUTION_HZ); + timer_ll_set_alarm_value(timg_hw, TEE_SECURE_TIMER, alarm_value); + + timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, true); + timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, true); + timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, true); +} + +static void tee_timer_disable(void) +{ + ESP_LOGI(TAG, "Disabling test timer from secure world"); + + /* Init timer interrupt vector descriptor */ + struct vector_desc_t timer_vd = { 0, NULL, NULL, NULL }; + timer_vd.source = ETS_TG0_T0_LEVEL_INTR_SOURCE; + timer_vd.isr = timer_group0_isr; + + esp_tee_intr_deregister((void *)&timer_vd); + + /* Disable timer */ + timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, false); + timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, false); + timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, false); + + /* Disable and clear interrupt */ + timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), false); + timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER)); + + /* Disable TG0 peripheral module */ + // periph_ll_disable_clk_set_rst(PERIPH_TIMG0_MODULE); +} + +void _ss_esp_tee_test_timer_init(bool enable) +{ + if (enable) { + tee_timer_enable(); + } else { + tee_timer_disable(); + } +} + +/** + * Secure interrupt in secure world test. + */ +int _ss_esp_tee_secure_int_test(void) +{ + ESP_LOGD(TAG, "In WORLD %d", esp_cpu_get_curr_privilege_level()); + volatile uint32_t secure_int_count = 0; + psecure_int_count = (uint32_t *)&secure_int_count; + + _ss_esp_tee_test_timer_init(true); + while (secure_int_count < TEE_TEST_INT_COUNT); + _ss_esp_tee_test_timer_init(false); + + ESP_LOGD(TAG, "Exiting WORLD %d", esp_cpu_get_curr_privilege_level()); + return secure_int_count; +} + +/** + * Non-Secure interrupt in secure world test. + */ +int _ss_esp_tee_non_secure_int_test(volatile uint32_t *volatile ns_int_count) +{ + ESP_LOGD(TAG, "In WORLD %d", esp_cpu_get_curr_privilege_level()); + + uint32_t count = 0; + count = *ns_int_count; + + while ((*ns_int_count < TEE_TEST_INT_COUNT)) { + if (*ns_int_count > count) { + count = *ns_int_count; + ESP_LOGI(TAG, "Interrupt count %d", count); + } + } + + ESP_LOGD(TAG, "Exiting WORLD %d", esp_cpu_get_curr_privilege_level()); + return 0; +} + +int _ss_esp_tee_test_int_count(uint32_t *secure_int_count) +{ + psecure_int_count = secure_int_count; + return (*psecure_int_count); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c new file mode 100644 index 000000000000..e159106cb9a4 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> +#include "esp_tee_test.h" +#include "esp_log.h" + +#define RND_VAL (0xA5A5A5A5) + +extern int _tee_vec_start; +extern int _tee_vec_end; +extern int _tee_iram_start; +extern int _tee_iram_end; +extern int _tee_dram_start; + +typedef void (*func_ptr)(void); + +__attribute__((unused)) static const char *TAG = "esp_tee_test_panic"; + +#pragma GCC push_options +#pragma GCC optimize ("Og") + +void _ss_esp_tee_test_resv_reg1_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(0x1)); + *test_addr = RND_VAL; +} + +void _ss_esp_tee_test_resv_reg1_exec_violation(void) +{ + volatile func_ptr fptr = (void(*)(void))0x1; + fptr(); +} + +void _ss_esp_tee_test_iram_reg1_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(&_tee_vec_start) - 0x100); + *test_addr = RND_VAL; +} + +void _ss_esp_tee_test_iram_reg2_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(&_tee_iram_start) - 0x04); + *test_addr = RND_VAL; +} + +#pragma GCC pop_options + +static void foo_d(void) +{ + for (int i = 0; i < 16; i++) { + __asm__ __volatile__("NOP"); + } +} + +void _ss_esp_tee_test_dram_reg1_exec_violation(void) +{ + uint8_t s_dram_buf[256]; + memcpy(&s_dram_buf, &foo_d, sizeof(s_dram_buf)); + volatile func_ptr fptr = (void(*)(void))&s_dram_buf; + fptr(); +} + +void _ss_esp_tee_test_dram_reg2_exec_violation(void) +{ + uint8_t *instr = calloc(256, sizeof(uint8_t)); + assert(instr); + memcpy(instr, &foo_d, 256); + volatile func_ptr fptr = (void(*)(void))instr; + fptr(); +} + +void _ss_esp_tee_test_illegal_instruction(void) +{ +#if CONFIG_IDF_TARGET_ARCH_XTENSA + __asm__ __volatile__("ill"); +#elif CONFIG_IDF_TARGET_ARCH_RISCV + __asm__ __volatile__("unimp"); +#endif +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c new file mode 100644 index 000000000000..7d84a4e7a768 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_log.h" +#include "esp_tee.h" +#include "esp_tee_test.h" + +static const char *TAG = "test_sec_srv"; + +/* Sample Trusted App */ + +uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_add(uint32_t a, uint32_t b) +{ + ESP_LOGD(TAG, "SS: %s", __func__); + return (a + b); +} + +uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_sub(uint32_t a, uint32_t b) +{ + ESP_LOGD(TAG, "SS: %s", __func__); + return (a - b); +} + +uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_mul(uint32_t a, uint32_t b) +{ + ESP_LOGD(TAG, "SS: %s", __func__); + return (a * b); +} + +uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_div(uint32_t a, uint32_t b) +{ + ESP_LOGD(TAG, "SS: %s", __func__); + return (a / b); +} + +uint32_t _ss_add_in_loop(uint32_t a, uint32_t b, uint32_t iter) +{ + ESP_LOGD(TAG, "SS: %s", __func__); + for (int i = 0; i < iter; i++) { + a += b; + esp_rom_delay_us(1000000); + ESP_LOGD(TAG, "[mode: %d] val: %d", esp_cpu_get_curr_privilege_level(), a); + } + return a; +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl new file mode 100644 index 000000000000..08f27b7b4ffc --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl @@ -0,0 +1,18 @@ +# SS no. API type Function Args +101 custom esp_tee_service_add 6 +102 custom esp_tee_service_sub 6 +103 custom esp_tee_service_mul 6 +104 custom esp_tee_service_div 6 +105 custom esp_tee_test_timer_init 6 +106 custom esp_tee_secure_int_test 6 +107 custom esp_tee_non_secure_int_test 6 +108 custom esp_tee_test_int_count 6 +109 custom esp_tee_test_resv_reg1_write_violation 0 +110 custom esp_tee_test_resv_reg1_exec_violation 0 +111 custom esp_tee_test_iram_reg1_write_violation 0 +112 custom esp_tee_test_iram_reg2_write_violation 0 +113 custom esp_tee_test_dram_reg1_exec_violation 0 +114 custom esp_tee_test_dram_reg2_exec_violation 0 +115 custom esp_tee_test_illegal_instruction 0 +201 custom dummy_secure_service 6 +202 custom add_in_loop 6 diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake new file mode 100644 index 000000000000..8be3ba76b349 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake @@ -0,0 +1,15 @@ +# tee_project.cmake file must be manually included in the project's top level CMakeLists.txt before project() +# This ensures that the variables are set before TEE starts building + +get_filename_component(directory "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE DIRECTORY) +get_filename_component(name ${CMAKE_CURRENT_LIST_DIR} NAME) + +# Append secure service table consisting of secure services +idf_build_set_property(CUSTOM_SECURE_SERVICE_TBL ${CMAKE_CURRENT_LIST_DIR}/test.tbl APPEND) + +# Append the directory of this component which is used by esp_tee component as +# EXTRA_COMPONENT_DIRS +idf_build_set_property(CUSTOM_SECURE_SERVICE_COMPONENT_DIR ${directory} APPEND) + +# Append the name of the component so that esp_tee can include it in its COMPONENTS list +idf_build_set_property(CUSTOM_SECURE_SERVICE_COMPONENT ${name} APPEND) diff --git a/components/esp_tee/test_apps/tee_test_fw/conftest.py b/components/esp_tee/test_apps/tee_test_fw/conftest.py new file mode 100644 index 000000000000..67055572eebd --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/conftest.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +# pylint: disable=W0621 # redefined-outer-name +import os +import tempfile +from typing import Any + +import espsecure +import esptool +import pytest +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from pytest_embedded_idf.serial import IdfSerial +from pytest_embedded_serial_esp.serial import EspSerial + +esp_tee_empty_bin = { + 'esp32c6': [ + 0xE9, 0x04, 0x02, 0x10, 0x00, 0x00, 0x80, 0x40, 0xEE, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + # DROM segment + 0x20, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x00, + # esp_app_desc structure + 0x32, 0x54, 0xCD, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x65, 0x73, 0x70, 0x5F, 0x74, 0x65, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4E, 0x6F, 0x76, 0x20, 0x31, 0x31, 0x20, 0x32, + 0x30, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2D, 0x63, 0x66, 0x8B, 0x75, 0xFA, 0x59, 0x05, + 0x53, 0x34, 0x91, 0x71, 0x51, 0x33, 0x91, 0xDD, 0xF8, 0xB1, 0xFE, 0x83, + 0x06, 0xEB, 0x03, 0x80, 0x45, 0xC9, 0x18, 0x20, 0x83, 0x7E, 0x2E, 0x43, + *([0x00] * 0x58), + # Padding + *([0x00] * 0x100), + # IRAM segment + 0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00, + *([0x00] * 0x20), + # PADDING segment + 0x00, 0x00, 0x00, 0x00, 0xC8, 0x7D, 0x00, 0x00, + *([0x00] * 0x7DC8), + # IROM segment + 0x20, 0x80, 0x00, 0x42, 0x00, 0x01, 0x00, 0x00, + *([0x00] * 0x100), + # Padding + *([0x00] * 0x0F), + # CRC8 checksum + 0x56, + # Image SHA256 + 0xF4, 0xA4, 0xCF, 0x06, 0xAE, 0x94, 0x75, 0x47, 0xBC, 0x88, 0xA2, 0xCA, + 0x52, 0x97, 0x7A, 0x5C, 0x55, 0x43, 0xD9, 0xF5, 0xD3, 0x45, 0xD1, 0x34, + 0xFC, 0x74, 0xB2, 0xB9, 0x34, 0x72, 0xC3, 0x00 + ] +} + + +# This is a custom IdfSerial class to support custom functionality +# which is required only for this test +class TEESerial(IdfSerial): + def __init__(self, *args, **kwargs) -> None: # type: ignore + super().__init__(*args, **kwargs) + + def _get_flash_size(self) -> Any: + return self.app.sdkconfig.get('ESPTOOLPY_FLASHSIZE', '') + + @EspSerial.use_esptool() + def bootloader_force_flash_if_req(self) -> None: + # Forcefully flash the bootloader only if security features are enabled + if any(( + self.app.sdkconfig.get('SECURE_BOOT', True), + self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', True), + )): + offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0)) + bootloader_path = os.path.join(self.app.binary_path, 'bootloader', 'bootloader.bin') + encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else '' + flash_size = self._get_flash_size() + + esptool.main( + f'--no-stub write_flash {offs} {bootloader_path} --force {encrypt} --flash_size {flash_size}'.split(), + esp=self.esp + ) + + @EspSerial.use_esptool() + def custom_erase_partition(self, partition: str) -> None: + if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE'): + with tempfile.NamedTemporaryFile(delete=True) as temp_file: + offs = self.app.partition_table[partition]['offset'] + size = self.app.partition_table[partition]['size'] + flash_size = self._get_flash_size() + + binstr = b'\xff' * int(size) + temp_file.write(binstr) + temp_file.flush() + + esptool.main( + f'--no-stub write_flash {offs} {temp_file.name} --flash_size {flash_size}'.split(), + esp=self.esp + ) + else: + self.erase_partition(partition) + + @EspSerial.use_esptool() + def copy_test_tee_img(self, partition: str, is_rollback: bool = False) -> None: + offs = self.app.partition_table[partition]['offset'] + no_stub = '--no-stub' if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE') else '' + encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else '' + flash_size = self._get_flash_size() + + flash_file = os.path.join(self.app.binary_path, 'esp_tee', 'esp_tee.bin') + + if is_rollback: + datafile = 'esp_tee_empty.bin' + datafile_signed = 'esp_tee_empty_signed.bin' + flash_file = datafile + + with open(datafile, 'wb') as data_file: + bin_data = esp_tee_empty_bin.get(self.app.sdkconfig.get('IDF_TARGET'), None) + if bin_data is not None: + data_file.write(bytes(bin_data)) + data_file.flush() + + if self.app.sdkconfig.get('SECURE_BOOT'): + keyfile = self.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY') + # Signing the image with espsecure + espsecure.main( + f'sign_data --version 2 --append_signatures --keyfile {keyfile} --output {datafile_signed} {datafile}'.split() + ) + flash_file = datafile_signed + + esptool.main( + f'{no_stub} write_flash {offs} {flash_file} {encrypt} --flash_size {flash_size}'.split(), + esp=self.esp + ) + + if is_rollback: + if os.path.exists(datafile): + os.remove(datafile) + if os.path.exists(datafile_signed): + os.remove(datafile_signed) + + @EspSerial.use_esptool() + def custom_flash(self) -> None: + self.bootloader_force_flash_if_req() + self.flash() + + @EspSerial.use_esptool() + def custom_flash_w_test_tee_img_gen(self) -> None: + self.bootloader_force_flash_if_req() + self.flash() + self.copy_test_tee_img('ota_1', False) + + @EspSerial.use_esptool() + def custom_flash_w_test_tee_img_rb(self) -> None: + self.bootloader_force_flash_if_req() + self.flash() + self.copy_test_tee_img('ota_1', True) + + @EspSerial.use_esptool() + def custom_flash_with_empty_sec_stg(self) -> None: + self.bootloader_force_flash_if_req() + self.flash() + self.custom_erase_partition('secure_storage') + + +@pytest.fixture(scope='module') +def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch: + mp = MonkeyPatch() + request.addfinalizer(mp.undo) + return mp + + +@pytest.fixture(scope='module', autouse=True) +def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None: + monkeypatch_module.setattr('pytest_embedded_idf.IdfSerial', TEESerial) diff --git a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt new file mode 100644 index 000000000000..9cb889dcdb72 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt @@ -0,0 +1,37 @@ +idf_build_get_property(idf_path IDF_PATH) + +set(priv_requires bootloader_support driver esp_tee esp_timer mbedtls spi_flash) +# Test FW related +list(APPEND priv_requires cmock json test_utils unity) +# TEE related +list(APPEND priv_requires tee_sec_storage tee_attestation tee_ota_ops test_sec_srv) + +set(srcs "app_main.c") + +list(APPEND srcs "test_esp_tee_ctx_switch.c" + "test_esp_tee_interrupt.c" + "test_esp_tee_panic.c" + "test_esp_tee_sec_stg.c" + "test_esp_tee_ota.c" + "test_esp_tee_att.c") + +set(mbedtls_test_srcs_dir "${idf_path}/components/mbedtls/test_apps/main") + +# AES +list(APPEND srcs "${mbedtls_test_srcs_dir}/test_aes.c" + "${mbedtls_test_srcs_dir}/test_aes_gcm.c" + "${mbedtls_test_srcs_dir}/test_aes_perf.c") +# SHA +list(APPEND srcs "${mbedtls_test_srcs_dir}/test_mbedtls_sha.c" + "${mbedtls_test_srcs_dir}/test_sha.c" + "${mbedtls_test_srcs_dir}/test_sha_perf.c") + +# Mixed +list(APPEND srcs "${mbedtls_test_srcs_dir}/test_aes_sha_parallel.c") +# Utility +list(APPEND srcs "${mbedtls_test_srcs_dir}/test_apb_dport_access.c" + "${mbedtls_test_srcs_dir}/test_mbedtls_utils.c") + +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES ${priv_requires} + WHOLE_ARCHIVE) diff --git a/components/esp_tee/test_apps/tee_test_fw/main/app_main.c b/components/esp_tee/test_apps/tee_test_fw/main/app_main.c new file mode 100644 index 000000000000..14c57a8afe21 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/app_main.c @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "memory_checks.h" + +/* setUp runs before every test */ +void setUp(void) +{ + test_utils_record_free_mem(); + test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(CONFIG_UNITY_WARN_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_LWIP); +} + +/* tearDown runs after every test */ +void tearDown(void) +{ + /* some FreeRTOS stuff is cleaned up by idle task */ + vTaskDelay(5); + + /* clean up some of the newlib's lazy allocations */ + esp_reent_cleanup(); + + /* check if unit test has caused heap corruption in any heap */ + TEST_ASSERT_MESSAGE(heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap"); + + test_utils_finish_and_evaluate_leaks(test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL), + test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL)); + +} + +static void test_task(void *pvParameters) +{ + vTaskDelay(10); /* Delay a bit to let the main task be deleted */ + unity_run_menu(); +} + +void app_main(void) +{ + xTaskCreatePinnedToCore(test_task, "testTask", CONFIG_UNITY_FREERTOS_STACK_SIZE, NULL, CONFIG_UNITY_FREERTOS_PRIORITY, NULL, CONFIG_UNITY_FREERTOS_CPU); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml b/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml new file mode 100644 index 000000000000..2ae836a9359b --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + ccomp_timer: "^1.0.0" diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c new file mode 100644 index 000000000000..6dd98f10248b --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c @@ -0,0 +1,301 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "esp_log.h" +#include "esp_heap_caps.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "esp_tee.h" +#include "esp_tee_attestation.h" +#include "secure_service_num.h" + +#include "esp_tee_sec_storage.h" + +#include "cJSON.h" +#include "unity.h" + +/* Note: negative value here so that assert message prints a grep-able + error hex value (mbedTLS uses -N for error codes) */ +#define TEST_ASSERT_MBEDTLS_OK(X) TEST_ASSERT_EQUAL_HEX32(0, -(X)) + +#define SHA256_DIGEST_SZ (32) +#define ECDSA_SECP256R1_KEY_LEN (32) + +#define ESP_ATT_TK_BUF_SIZE (1792) +#define ESP_ATT_TK_PSA_CERT_REF ("0716053550477-10100") + +#define ESP_ATT_TK_NONCE (0xABCD1234) +#define ESP_ATT_TK_CLIENT_ID (0x0FACADE0) + +static const char *TAG = "test_esp_tee_att"; + +extern int verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign); + +static uint8_t hexchar_to_byte(char hex) +{ + if (hex >= '0' && hex <= '9') { + return hex - '0'; + } else if (hex >= 'a' && hex <= 'f') { + return hex - 'a' + 10; + } else if (hex >= 'A' && hex <= 'F') { + return hex - 'A' + 10; + } else { + // Handle invalid hex characters + return UINT8_MAX; + } +} + +static void hexstr_to_bytes(const char *hex_str, uint8_t **hex_buf, size_t *buf_sz) +{ + size_t hex_len = strlen(hex_str); + + // Check if the hex string has an even number of characters + TEST_ASSERT_EQUAL(0, hex_len % 2); + + // Calculate the size of the byte buffer + *buf_sz = hex_len / 2; + + // Allocate memory for the byte buffer + *hex_buf = calloc(*buf_sz, sizeof(uint8_t)); + TEST_ASSERT_NOT_NULL(hex_buf); + + // Convert each pair of hex characters to a byte + for (size_t i = 0; i < *buf_sz; ++i) { + (*hex_buf)[i] = (hexchar_to_byte(hex_str[2 * i]) << 4) | hexchar_to_byte(hex_str[2 * i + 1]); + } +} + +static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_pubkey_t *pubkey) +{ + int ret = -1; + mbedtls_mpi r, x, n; + + size_t plen = mbedtls_mpi_size(&grp->P); + + if (ilen != plen + 1) { + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + } + + if (input[0] != 0x02 && input[0] != 0x03) { + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + } + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&x); + mbedtls_mpi_init(&n); + + // x <= input + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, input + 1, plen)); + + // r = x^2 + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &x, &x)); + + // r = x^2 + a + if (grp->A.MBEDTLS_PRIVATE(p) == NULL) { + // Special case where a is -3 + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&r, &r, 3)); + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->A)); + } + + // r = x^3 + ax + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &r, &x)); + + // r = x^3 + ax + b + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->B)); + + // Calculate square root of r over finite field P: + // r = sqrt(x^3 + ax + b) = (x^3 + ax + b) ^ ((P + 1) / 4) (mod P) + + // n = P + 1 + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &grp->P, 1)); + + // n = (P + 1) / 4 + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2)); + + // r ^ ((P + 1) / 4) (mod p) + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r, &r, &n, &grp->P, NULL)); + + // Select solution that has the correct "sign" (equals odd/even solution in finite group) + if ((input[0] == 0x03) != mbedtls_mpi_get_bit(&r, 0)) { + // r = p - r + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&r, &grp->P, &r)); + } + + // y => output + memcpy(pubkey->pub_x, input + 1, ilen - 1); + ret = mbedtls_mpi_write_binary(&r, pubkey->pub_y, plen); + +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&x); + mbedtls_mpi_free(&n); + + return (ret); +} + +static void prehash_token_data(const char *token_json, uint8_t *digest, size_t len) +{ + TEST_ASSERT_NOT_NULL(token_json); + TEST_ASSERT_NOT_NULL(digest); + TEST_ASSERT_NOT_EQUAL(0, len); + + // Parse JSON string + cJSON *root = cJSON_Parse(token_json); + TEST_ASSERT_NOT_NULL(root); + + // Fetching the data to be verified + cJSON *header = cJSON_GetObjectItemCaseSensitive(root, "header"); + TEST_ASSERT_NOT_NULL(header); + + cJSON *eat = cJSON_GetObjectItemCaseSensitive(root, "eat"); + TEST_ASSERT_NOT_NULL(eat); + + cJSON *public_key = cJSON_GetObjectItemCaseSensitive(root, "public_key"); + TEST_ASSERT_NOT_NULL(public_key); + + char *header_str = cJSON_PrintUnformatted(header); + char *eat_str = cJSON_PrintUnformatted(eat); + char *public_key_str = cJSON_PrintUnformatted(public_key); + + mbedtls_sha256_context sha256_ctx; + + mbedtls_sha256_init(&sha256_ctx); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_starts(&sha256_ctx, false)); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)header_str, strlen(header_str))); + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)eat_str, strlen(eat_str))); + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)public_key_str, strlen(public_key_str))); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_finish(&sha256_ctx, digest)); + + mbedtls_sha256_free(&sha256_ctx); + + free(public_key_str); + free(eat_str); + free(header_str); + + cJSON_Delete(root); +} + +static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *pubkey_ctx) +{ + TEST_ASSERT_NOT_NULL(token_json); + TEST_ASSERT_NOT_NULL(pubkey_ctx); + + // Parse JSON string + cJSON *root = cJSON_Parse(token_json); + TEST_ASSERT_NOT_NULL(root); + + cJSON *public_key = cJSON_GetObjectItemCaseSensitive(root, "public_key"); + TEST_ASSERT_NOT_NULL(token_json); + + cJSON *compressed = cJSON_GetObjectItemCaseSensitive(public_key, "compressed"); + TEST_ASSERT_NOT_NULL(compressed); + + uint8_t *pubkey_buf = NULL; + size_t pubkey_buf_sz = 0; + hexstr_to_bytes(compressed->valuestring, &pubkey_buf, &pubkey_buf_sz); + + mbedtls_ecp_keypair keypair; + mbedtls_ecp_keypair_init(&keypair); + mbedtls_ecp_point_init(&keypair.MBEDTLS_PRIVATE(Q)); + mbedtls_ecp_group_init(&keypair.MBEDTLS_PRIVATE(grp)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecp_group_load(&keypair.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1)); + + TEST_ASSERT_EQUAL(0, decompress_ecdsa_pubkey(&keypair.MBEDTLS_PRIVATE(grp), pubkey_buf, pubkey_buf_sz, pubkey_ctx)); + mbedtls_ecp_keypair_free(&keypair); + + free(pubkey_buf); + cJSON_Delete(root); +} + +static void fetch_signature(const char *token_json, esp_tee_sec_storage_sign_t *sign_ctx) +{ + TEST_ASSERT_NOT_NULL(token_json); + TEST_ASSERT_NOT_NULL(sign_ctx); + + // Parse JSON string + cJSON *root = cJSON_Parse(token_json); + TEST_ASSERT_NOT_NULL(root); + + cJSON *sign = cJSON_GetObjectItemCaseSensitive(root, "sign"); + TEST_ASSERT_NOT_NULL(sign); + + cJSON *sign_r = cJSON_GetObjectItemCaseSensitive(sign, "r"); + TEST_ASSERT_NOT_NULL(sign_r); + + cJSON *sign_s = cJSON_GetObjectItemCaseSensitive(sign, "s"); + TEST_ASSERT_NOT_NULL(sign_s); + + uint8_t *sign_r_buf = NULL; + size_t sign_r_buf_sz = 0; + hexstr_to_bytes(sign_r->valuestring, &sign_r_buf, &sign_r_buf_sz); + memcpy(sign_ctx->sign_r, sign_r_buf, sign_r_buf_sz); + free(sign_r_buf); + + uint8_t *sign_s_buf = NULL; + size_t sign_s_buf_sz = 0; + hexstr_to_bytes(sign_s->valuestring, &sign_s_buf, &sign_s_buf_sz); + memcpy(sign_ctx->sign_s, sign_s_buf, sign_s_buf_sz); + free(sign_s_buf); + + cJSON_Delete(root); +} + +TEST_CASE("Test TEE Attestation - Generate and verify the EAT", "[attestation]") +{ + uint8_t *token_buf = heap_caps_calloc(ESP_ATT_TK_BUF_SIZE, sizeof(uint8_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(token_buf); + + ESP_LOGI(TAG, "Generating EAT for all active firmwares (Bootloader, TEE and non-secure app)..."); + // Generating the attestation token + uint32_t token_len = 0; + TEST_ESP_OK(esp_tee_att_generate_token(0xA1B2C3D4, 0x0FACADE0, (const char *)ESP_ATT_TK_PSA_CERT_REF, + token_buf, ESP_ATT_TK_BUF_SIZE, &token_len)); + ESP_LOGI(TAG, "EAT generated - length: %"PRIu32"", token_len); + + // Pre-hashing the data + uint8_t digest[SHA256_DIGEST_SZ] = {}; + prehash_token_data((const char *)token_buf, digest, sizeof(digest)); + + // Fetching and decompressing the public key + esp_tee_sec_storage_pubkey_t pubkey_ctx = {}; + fetch_pubkey((const char *)token_buf, &pubkey_ctx); + + // Fetching the signature components + esp_tee_sec_storage_sign_t sign_ctx = {}; + fetch_signature((const char *)token_buf, &sign_ctx); + + ESP_LOGI(TAG, "Verifying the generated EAT..."); + // Verifying the generated token + TEST_ASSERT_EQUAL(0, verify_ecdsa_secp256r1_sign(digest, sizeof(digest), &pubkey_ctx, &sign_ctx)); + free(token_buf); +} + +TEST_CASE("Test TEE Attestation - Invalid token buffer", "[attestation]") +{ + esp_err_t err; + uint32_t token_len = 0; + + uint8_t *token_buf = heap_caps_calloc(4, sizeof(uint8_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(token_buf); + + err = esp_tee_att_generate_token(ESP_ATT_TK_NONCE, ESP_ATT_TK_CLIENT_ID, (const char *)ESP_ATT_TK_PSA_CERT_REF, + token_buf, 0, &token_len); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, err); + + err = esp_tee_att_generate_token(ESP_ATT_TK_NONCE, ESP_ATT_TK_CLIENT_ID, (const char *)ESP_ATT_TK_PSA_CERT_REF, + NULL, 0, &token_len); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, err); + + free(token_buf); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c new file mode 100644 index 000000000000..298d3ade3e91 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_tee.h" +#include "secure_service_num.h" +#include "esp_tee_test.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +static void test_op(int sec_srv_num, uint32_t a, uint32_t b) +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + uint32_t lval = 0; + switch (sec_srv_num) { + case SS_ESP_TEE_SERVICE_ADD: + lval = (a + b); + break; + case SS_ESP_TEE_SERVICE_SUB: + lval = (a - b); + break; + case SS_ESP_TEE_SERVICE_MUL: + lval = (a * b); + break; + case SS_ESP_TEE_SERVICE_DIV: + lval = (a / b); + break; + default: + lval = UINT32_MAX; + break; + } + + uint32_t val = esp_tee_service_call(3, sec_srv_num, a, b); + TEST_ASSERT_EQUAL_UINT32(lval, val); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Sample app world switch failed"); +} + +TEST_CASE("Test single calls to sample app (basic services)", "[basic]") +{ + const uint32_t a = 200, b = 100; + test_op(SS_ESP_TEE_SERVICE_ADD, a, b); + test_op(SS_ESP_TEE_SERVICE_SUB, a, b); + test_op(SS_ESP_TEE_SERVICE_MUL, a, b); + test_op(SS_ESP_TEE_SERVICE_DIV, a, b); +} + +TEST_CASE("Test multiple calls to sample app (basic services)", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + for (int i = 0; i < 1024; i++) { + uint32_t val = esp_tee_service_call(3, SS_ESP_TEE_SERVICE_ADD, i, i + 1); + TEST_ASSERT_EQUAL_UINT32((2 * i + 1), val); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Sample app world switch failed"); + } +} + +TEST_CASE("Custom secure service call", "[basic]") +{ + dummy_secure_service(); +} + +void test_task(void *pvParameters) +{ + for (int i = 0; i < 8; i++) { + esp_rom_printf("[mode: %d] test_task - %d\n", esp_cpu_get_curr_privilege_level(), i); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + vTaskDelete(NULL); +} + +TEST_CASE("Task switching during secure service calls", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + xTaskCreate(test_task, "test_task", 4096, NULL, CONFIG_UNITY_FREERTOS_PRIORITY + 3, NULL); + + const uint32_t a = 100, b = 200, iter = 8; + TEST_ASSERT_EQUAL_UINT32(a + b * iter, esp_tee_service_call(4, SS_ADD_IN_LOOP, a, b, iter)); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c new file mode 100644 index 000000000000..9b172c79e29b --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "driver/gptimer.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#include "esp_tee.h" +#include "esp_tee_test.h" +#include "secure_service_num.h" + +#include "unity.h" + +static const char *TAG = "test_esp_tee_intr"; + +/* ---------------------------------------------------- Utility functions ---------------------------------------------------- */ + +typedef struct { + uint64_t event_count; +} test_queue_element_t; + +static QueueHandle_t s_timer_queue; + +static gptimer_handle_t gptimer = NULL; + +static volatile uint32_t ns_int_count; + +static bool IRAM_ATTR test_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + ESP_EARLY_LOGI(TAG, "Timer ISR Handler from World %d!", esp_cpu_get_curr_privilege_level()); + + BaseType_t high_task_awoken = pdFALSE; + QueueHandle_t queue = (QueueHandle_t)user_data; + // Retrieve count value and send to queue + test_queue_element_t ele = { + .event_count = edata->count_value + }; + ns_int_count += 1; + + xQueueSendFromISR(queue, &ele, &high_task_awoken); + // return whether we need to yield at the end of ISR + return (high_task_awoken == pdTRUE); +} + +static void IRAM_ATTR timer_evt_task(void *arg) +{ + int record = 3; + while (1) { + test_queue_element_t ele; + if (xQueueReceive(s_timer_queue, &ele, pdMS_TO_TICKS(2000))) { + ESP_LOGI(TAG, "Timer reloaded, count=%llu", ele.event_count); + record--; + } else { + ESP_LOGW(TAG, "Missed one count event"); + } + if (!record) { + break; + } + } +} + +static void test_timer_init(bool for_ns_world) +{ + s_timer_queue = xQueueCreate(10, sizeof(test_queue_element_t)); + if (!s_timer_queue) { + ESP_LOGE(TAG, "Creating queue failed"); + return; + } + + ns_int_count = 0; + + /* Select and initialize basic parameters of the timer */ + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1000000, // 1MHz, 1 tick=1us + }; + ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); + + gptimer_event_callbacks_t cbs = { + .on_alarm = test_timer_on_alarm_cb, + }; + ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, s_timer_queue)); + + ESP_ERROR_CHECK(gptimer_enable(gptimer)); + + gptimer_alarm_config_t alarm_config2 = { + .reload_count = 0, + .alarm_count = 250000, // Alarm target = 250ms @ resolution 1MHz + .flags.auto_reload_on_alarm = true, + }; + ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config2)); + ESP_ERROR_CHECK(gptimer_start(gptimer)); + + if (for_ns_world) { + timer_evt_task(NULL); + } +} + +static void test_timer_deinit(void) +{ + ESP_ERROR_CHECK(gptimer_stop(gptimer)); + ESP_ERROR_CHECK(gptimer_disable(gptimer)); + ESP_ERROR_CHECK(gptimer_del_timer(gptimer)); + + if (s_timer_queue != NULL) { + vQueueDelete(s_timer_queue); + s_timer_queue = NULL; + } +} + +/* ---------------------------------------------------- Test cases ---------------------------------------------------- */ + +TEST_CASE("Test Secure interrupt in Non-Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + volatile uint32_t lsecure_int_count = 0; + + /* Pass the variable to secure world to record the interrupt count. */ + esp_tee_service_call(2, SS_ESP_TEE_TEST_INT_COUNT, &lsecure_int_count); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + esp_tee_service_call(2, SS_ESP_TEE_TEST_TIMER_INIT, true); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + /* Secure timer initialized. + * As secure timer interrupt will fire; CPU will switch to secure world. + * Secure world ISR handler will be called, Secure ISR log can be observed on console. + * After handling the secure interrupt, CPU will return to non-secure world + * and resume this loop and wait for the next secure timer interrupt. + * CPU will wait for TEE_TEST_INT_COUNT number of secure interrupts. + */ + while (lsecure_int_count < TEE_TEST_INT_COUNT); + + /* After waiting for TEE_TEST_INT_COUNT secure interrupt, + * disable the secure timer and assert the test status. + */ + esp_tee_service_call(2, SS_ESP_TEE_TEST_TIMER_INIT, false); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + /* Assert the number of secure interrupt occurred again. */ + TEST_ASSERT_EQUAL_UINT32(TEE_TEST_INT_COUNT, lsecure_int_count); +} + +TEST_CASE("Test Secure interrupt in Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + uint32_t cnt = esp_tee_service_call(1, SS_ESP_TEE_SECURE_INT_TEST); + TEST_ASSERT_EQUAL_UINT32(TEE_TEST_INT_COUNT, cnt); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); +} + +static volatile uint32_t *get_ns_int_count(void) +{ + return &ns_int_count; +} + +TEST_CASE("Test Non-secure interrupt in Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not W1"); + + /* Non-secure world timer initialization. */ + ESP_LOGI(TAG, "Enabling test timer from non-secure world"); + test_timer_init(false); + + volatile uint32_t *volatile lns_int_count; + lns_int_count = get_ns_int_count(); + + /* After non-secure timer initialization, + * CPU will switch to secure world by using a service call to test API. + * CPU will wait in finite loop in secure world. + * And as non-secure timer interrupt fires, CPU will switch to non-secure world. + * Non-secure world ISR handler will be called, non-secure ISR log can be obsereved on console. + * After handling the interrupt in non-secure world, CPU will switch back to secure world + * and wait for the next timer interrupt. + * In secure world CPU will wait for TEE_TEST_INT_COUNT non-secure interrupts. + */ + uint32_t val = esp_tee_service_call(2, SS_ESP_TEE_NON_SECURE_INT_TEST, lns_int_count); + TEST_ASSERT_EQUAL_UINT32(0, val); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + ESP_LOGI(TAG, "Disabling test timer from non-secure world"); + test_timer_deinit(); +} + +TEST_CASE("Test Non-secure interrupt in Non-Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + ESP_LOGI(TAG, "Enabling test timer from non-secure world"); + test_timer_init(true); + + vTaskDelay(pdMS_TO_TICKS(1000)); + + ESP_LOGI(TAG, "Disabling test timer from non-secure world"); + test_timer_deinit(); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c new file mode 100644 index 000000000000..6096e49ac3ee --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "unity.h" +#include "esp_log.h" + +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_image_format.h" +#include "esp_tee_ota_ops.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +#define TEE_IMG_SRC_PART_SUBTYPE (ESP_PARTITION_SUBTYPE_APP_OTA_1) +#define OTA_BUF_SIZE (512) + +#define FLASH_SECTOR_SIZE (4096) +#define ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1)) +#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) + +static const char *TAG = "test_esp_tee_ota"; + +TEST_CASE("Test TEE OTA - API state check", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + ESP_LOGI(TAG, "TEE OTA - write without begin"); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_ota_write(0x00, (const void *)buf, sizeof(buf))); + + ESP_LOGI(TAG, "TEE OTA - end without begin and write"); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_ota_end()); + + ESP_LOGI(TAG, "TEE OTA - end without write"); + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +TEST_CASE("Test TEE OTA - Write out of bounds", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + const esp_partition_t *tee_next = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_TEE_1, NULL); + TEST_ASSERT_NOT_NULL(tee_next); + + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_ERR(ESP_FAIL, esp_tee_ota_write(tee_next->size, (const void *)buf, sizeof(buf))); + TEST_ESP_ERR(ESP_FAIL, esp_tee_ota_write(tee_next->size - sizeof(buf) + 1, (const void *)buf, sizeof(buf))); +} + +TEST_CASE("Test TEE OTA - Invalid image", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_OK(esp_tee_ota_write(0x00, (const void *)buf, sizeof(buf))); + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +static uint32_t copy_tee_update(void) +{ + /* NOTE: In a real-world scenario, we would get the non-secure app's next update partition */ + const esp_partition_t *tee_update = esp_partition_find_first(ESP_PARTITION_TYPE_APP, TEE_IMG_SRC_PART_SUBTYPE, NULL); + TEST_ASSERT_NOT_NULL(tee_update); + + const esp_partition_pos_t tee_next_pos = { + .offset = tee_update->address, + .size = tee_update->size, + }; + esp_image_metadata_t tee_next_metadata = {}; + + TEST_ESP_OK(esp_image_get_metadata(&tee_next_pos, &tee_next_metadata)); + + uint32_t tee_next_img_len = tee_next_metadata.image_len; + +#if CONFIG_SECURE_BOOT_V2_ENABLED + tee_next_img_len = ALIGN_UP(tee_next_img_len, FLASH_SECTOR_SIZE) + FLASH_SECTOR_SIZE; +#endif + + uint32_t curr_write_offset = 0; + bool image_header_was_checked = false; /* deal with all receive packet */ + + /* an ota data write buffer ready to write to the flash */ + uint8_t ota_write_data[OTA_BUF_SIZE]; + memset(ota_write_data, 0xFF, OTA_BUF_SIZE); + + while (curr_write_offset < tee_next_img_len) { + uint32_t bytes_to_copy = (curr_write_offset + OTA_BUF_SIZE <= tee_next_img_len) + ? OTA_BUF_SIZE + : (tee_next_img_len - curr_write_offset); + + TEST_ESP_OK(esp_partition_read(tee_update, curr_write_offset, ota_write_data, bytes_to_copy)); + + if (image_header_was_checked == false) { + esp_image_header_t img_hdr; + memcpy(&img_hdr, ota_write_data, sizeof(esp_image_header_t)); + TEST_ASSERT_EQUAL(img_hdr.chip_id, CONFIG_IDF_FIRMWARE_CHIP_ID); + image_header_was_checked = true; + + TEST_ESP_OK(esp_tee_ota_begin()); + } + + TEST_ESP_OK(esp_tee_ota_write(curr_write_offset, (const void *)ota_write_data, bytes_to_copy)); + + curr_write_offset += bytes_to_copy; + memset(ota_write_data, 0xFF, OTA_BUF_SIZE); + } + + ESP_LOGI(TAG, "Done copying the update!"); + return curr_write_offset; +} + +TEST_CASE("Test TEE OTA - Corrupted image", "[ota_neg_2]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t curr_write_offset = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", curr_write_offset); + + /* Corrupting the image */ + ESP_LOGI(TAG, "Corrupting the image at some offset..."); + uint32_t corrupt[8] = {[0 ... 7] = 0x0BADC0DE}; + curr_write_offset -= (2 * FLASH_SECTOR_SIZE + sizeof(corrupt)); + TEST_ESP_OK(esp_tee_ota_write(curr_write_offset, (const void *)corrupt, sizeof(corrupt))); + + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +TEST_CASE("Test TEE OTA - Reboot without ending OTA", "[ota_neg_2]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} + +TEST_CASE("Test TEE OTA - Valid image", "[ota_valid_img]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + TEST_ESP_OK(esp_tee_ota_end()); + ESP_LOGI(TAG, "TEE OTA update successful!"); + + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} + +TEST_CASE("Test TEE OTA - Rollback", "[ota_rollback]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + TEST_ESP_OK(esp_tee_ota_end()); + + ESP_LOGI(TAG, "TEE OTA update successful!"); + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c new file mode 100644 index 000000000000..a9eb544d227e --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/aes_reg.h" +#include "soc/efuse_reg.h" +#include "soc/lp_analog_peri_reg.h" +#include "soc/lp_wdt_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "unity.h" +#include "esp_tee.h" +#include "secure_service_num.h" + +#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) + +extern uint32_t _iram_start; +extern uint32_t _instruction_reserved_start; + +/* ---------------------------------------------------- Basic Exceptions for TEE tests ---------------------------------------------------- */ + +#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE +#define TEST_APM_EFUSE_PROT_REG \ + (EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20)) +#else +#define TEST_APM_EFUSE_PROT_REG EFUSE_RD_KEY5_DATA0_REG +#endif + +TEST_CASE("Test APM violation interrupt: AES", "[apm_violation]") +{ + uint32_t val = UINT32_MAX; + val = REG_READ(AES_DATE_REG); + TEST_ASSERT_EQUAL(0, val); + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} + +TEST_CASE("Test APM violation interrupt: eFuse", "[apm_violation]") +{ + uint32_t val = UINT32_MAX; + val = REG_READ(TEST_APM_EFUSE_PROT_REG); + TEST_ASSERT_EQUAL(0, val); + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} + +/* TEE IRAM: Reserved/Vector-table boundary */ +TEST_CASE("Test TEE-TEE violation: IRAM (W1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_IRAM_REG1_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal memory space: Write */ +TEST_CASE("Test TEE-TEE violation: Reserved (W1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_RESV_REG1_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal memory space: Execution */ +TEST_CASE("Test TEE-TEE violation: Reserved (X1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_RESV_REG1_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM: Vector table region */ +TEST_CASE("Test TEE-TEE violation: IRAM (W2)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_IRAM_REG2_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM: Stack region */ +TEST_CASE("Test TEE-TEE violation: DRAM (X1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_DRAM_REG1_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM: Heap region */ +TEST_CASE("Test TEE-TEE violation: DRAM (X2)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_DRAM_REG2_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal Instruction */ +TEST_CASE("Test TEE-TEE violation: Illegal Instruction", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_ILLEGAL_INSTRUCTION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM -REE IRAM Boundary */ +TEST_CASE("Test REE-TEE isolation: DRAM (R1)", "[exception]") +{ + uint32_t* val = (uint32_t *)(&_iram_start - 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM -REE IRAM Boundary */ +TEST_CASE("Test REE-TEE isolation: DRAM (W1)", "[exception]") +{ + *(uint32_t *)(&_iram_start - 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM region */ +TEST_CASE("Test REE-TEE isolation: IRAM (R1)", "[exception]") +{ + uint32_t *val = (uint32_t *)(&_iram_start - (CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) + 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM region */ +TEST_CASE("Test REE-TEE isolation: IRAM (W1)", "[exception]") +{ + *(uint32_t *)(&_iram_start - (CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) + 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IROM region */ +TEST_CASE("Test REE-TEE isolation: IROM (R1)", "[exception]") +{ + uint32_t *val = (uint32_t *)(SOC_IROM_LOW + 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IROM region */ +TEST_CASE("Test REE-TEE isolation: IROM (W1)", "[exception]") +{ + *(uint32_t *)(SOC_IROM_LOW + 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DROM - REE IROM boundary */ +TEST_CASE("Test REE-TEE isolation: DROM (R1)", "[exception]") +{ + const uint32_t test_addr = ALIGN_DOWN_TO_MMU_PAGE_SIZE((uint32_t)&_instruction_reserved_start); + uint32_t *val = (uint32_t *)(test_addr - 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DROM - REE IROM boundary */ +TEST_CASE("Test REE-TEE isolation: DROM (W1)", "[exception]") +{ + const uint32_t test_addr = ALIGN_DOWN_TO_MMU_PAGE_SIZE((uint32_t)&_instruction_reserved_start); + *(uint32_t *)(test_addr - 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* SWDT/BOD Reset register */ +TEST_CASE("Test REE-TEE isolation: SWDT/BOD (W)", "[exception]") +{ + REG_WRITE(LP_ANALOG_PERI_LP_ANA_FIB_ENABLE_REG, 0); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c new file mode 100644 index 000000000000..fa5e381b6eed --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c @@ -0,0 +1,274 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "esp_partition.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "esp_tee.h" +#include "esp_tee_sec_storage.h" +#include "secure_service_num.h" + +#include "esp_random.h" + +#include "unity.h" + +/* Note: negative value here so that assert message prints a grep-able + error hex value (mbedTLS uses -N for error codes) */ +#define TEST_ASSERT_MBEDTLS_OK(X) TEST_ASSERT_EQUAL_HEX32(0, -(X)) + +#define SHA256_DIGEST_SZ (32) +#define ECDSA_SECP256R1_KEY_LEN (32) + +static const char *TAG = "test_esp_tee_sec_storage"; + +int verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign) +{ + TEST_ASSERT_NOT_NULL(pubkey); + TEST_ASSERT_NOT_NULL(digest); + TEST_ASSERT_NOT_NULL(sign); + TEST_ASSERT_NOT_EQUAL(0, len); + + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + mbedtls_ecdsa_context ecdsa_context; + mbedtls_ecdsa_init(&ecdsa_context); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1)); + size_t plen = mbedtls_mpi_size(&ecdsa_context.MBEDTLS_PRIVATE(grp).P); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&r, sign->sign_r, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&s, sign->sign_s, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pubkey->pub_x, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pubkey->pub_y, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), digest, len, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s)); + + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + mbedtls_ecdsa_free(&ecdsa_context); + + return 0; +} + +TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-slot IDs", "[secure_storage]") +{ + const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size + unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(message); + + esp_fill_random(message, buf_sz); + + uint8_t msg_digest[SHA256_DIGEST_SZ]; + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false)); + free(message); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) { + ESP_LOGI(TAG, "Slot ID: %u", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, msg_digest, sizeof(msg_digest), &sign)); + + esp_tee_sec_storage_pubkey_t pubkey = {}; + TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, &pubkey)); + + ESP_LOGI(TAG, "Verifying generated signature..."); + TEST_ESP_OK(verify_ecdsa_secp256r1_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + } +} + +TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-slot IDs", "[secure_storage]") +{ + const uint8_t SZ = 100; + uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(plaintext); + esp_fill_random(plaintext, SZ); + + uint8_t *ciphertext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(ciphertext); + + uint8_t *decryptedtext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(decryptedtext); + + uint8_t tag[12]; + uint8_t aad[16]; + memset(aad, 0xA5, sizeof(aad)); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) { + ESP_LOGI(TAG, "Slot ID: %u", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + TEST_ESP_OK(esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + TEST_ESP_OK(esp_tee_sec_storage_decrypt(slot_id, ciphertext, SZ, aad, sizeof(aad), + tag, sizeof(tag), decryptedtext)); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(plaintext, decryptedtext, SZ); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + } + + free(plaintext); + free(ciphertext); + free(decryptedtext); +} + +TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[secure_storage]") +{ + // Setup for ECDSA + const uint8_t SZ = 100; + unsigned char *message = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(message); + esp_fill_random(message, SZ); + uint8_t msg_digest[SHA256_DIGEST_SZ]; + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, SZ, msg_digest, false)); + free(message); + + // Setup for AES + uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(plaintext); + esp_fill_random(plaintext, SZ); + uint8_t *ciphertext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(ciphertext); + uint8_t tag[12]; + uint8_t aad[16]; + memset(aad, 0xA5, sizeof(aad)); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + // Test ECDSA key with AES operation + const uint16_t slot_id = 7; + ESP_LOGI(TAG, "Slot ID: %u - Trying AES operation with ECDSA key...", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + + // Test AES key with ECDSA operation + ESP_LOGI(TAG, "Slot ID: %u - Trying ECDSA operation with AES key...", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_get_signature(slot_id, msg_digest, sizeof(msg_digest), &sign)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + + // Test with non-existent data + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + + free(plaintext); + free(ciphertext); +} + +TEST_CASE("Test TEE Secure Storage - Invalid key-slot IDs", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + uint16_t slot_id = MAX_SEC_STG_SLOT_ID + 1; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + slot_id = MIN_SEC_STG_SLOT_ID - 1; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); +} + +TEST_CASE("Test TEE Secure Storage - Exhaust all key-slots", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + esp_err_t err = ESP_FAIL; + uint16_t slot_id = MIN_SEC_STG_SLOT_ID; + + while (1) { + esp_tee_sec_storage_clear_slot(slot_id); + err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256); + if (err != ESP_OK) { + break; + } + TEST_ASSERT_FALSE(esp_tee_sec_storage_is_slot_empty(slot_id)); + slot_id++; + } + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err); + + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MAX_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MIN_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256)); + + uint16_t mid_slot = (MIN_SEC_STG_SLOT_ID + MAX_SEC_STG_SLOT_ID) / 2; + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(mid_slot, ESP_SEC_STG_KEY_AES256)); +} + +TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + const uint16_t slot_id = 7; + uint8_t data[31], tag[12]; + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, NULL, sizeof(data), &sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_get_signature(slot_id, data, 0, &sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, data, sizeof(data), NULL)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_pubkey(slot_id, NULL)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); +} + +#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6) +TEST_CASE("Test TEE Secure Storage - Corruption from non-secure world", "[secure_storage_neg]") +{ + const esp_partition_t *tee_sec_stg = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL); + TEST_ASSERT_NOT_NULL(tee_sec_stg); + + uint8_t buf_w[128]; + memset(buf_w, 0xA5, sizeof(buf_w)); + TEST_ESP_OK(esp_partition_write(tee_sec_stg, 0x00, buf_w, sizeof(buf_w))); + + uint8_t buf_r[128]; + memset(buf_r, 0x00, sizeof(buf_r)); + TEST_ESP_OK(esp_partition_read(tee_sec_stg, 0x00, buf_r, sizeof(buf_r))); + ESP_LOG_BUFFER_HEXDUMP(TAG, buf_r, sizeof(buf_r), ESP_LOG_INFO); + + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} +#endif diff --git a/components/esp_tee/test_apps/tee_test_fw/partitions.csv b/components/esp_tee/test_apps/tee_test_fw/partitions.csv new file mode 100644 index 000000000000..e5f7bb21fdd2 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/partitions.csv @@ -0,0 +1,6 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +tee, app, tee_0, , 192K, +secure_storage, data, tee_sec_stg, , 64K, +factory, app, factory, , 512K, +nvs, data, nvs, , 24K, diff --git a/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv b/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv new file mode 100644 index 000000000000..7856d6bb3432 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +tee_0, app, tee_0, , 192K, +tee_1, app, tee_1, , 192K, +tee_otadata, data, tee_ota, , 8K, +secure_storage, data, tee_sec_stg, , 56K, +ota_0, app, ota_0, , 512K, +ota_1, app, ota_1, , 512K, +otadata, data, ota, , 8K, +nvs, data, nvs, , 24K, diff --git a/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py b/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py new file mode 100644 index 000000000000..05ba445334bb --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py @@ -0,0 +1,301 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +from enum import Enum +from typing import Any +from typing import Dict + +import pytest +from pytest_embedded_idf import IdfDut + +CONFIGS_DEFAULT = [ + pytest.param('default', marks=[pytest.mark.esp32c6]) +] + +CONFIGS_OTA = [ + pytest.param('ota', marks=[pytest.mark.esp32c6]) +] + +TEE_VIOLATION_TEST_EXC_RSN: Dict[str, Any] = { + 'esp32c6': { + ('Reserved', 'W1'): 'Store access fault', + ('Reserved', 'X1'): 'Instruction access fault', + ('IRAM', 'W1'): 'Store access fault', + ('IRAM', 'W2'): 'Store access fault', + ('DRAM', 'X1'): 'Instruction access fault', + ('DRAM', 'X2'): 'Instruction access fault' + } +} + +REE_ISOLATION_TEST_EXC_RSN: Dict[str, Any] = { + 'esp32c6': { + ('DRAM', 'R1'): 'Load access fault', + ('DRAM', 'W1'): 'Store access fault', + ('IRAM', 'R1'): 'Load access fault', + ('IRAM', 'W1'): 'Store access fault', + ('IROM', 'R1'): 'Load access fault', + ('IROM', 'W1'): 'Store access fault', + ('DROM', 'R1'): 'Load access fault', + ('DROM', 'W1'): 'Store access fault', + ('SWDT/BOD', 'W'): 'Store access fault', + } +} + +TEE_APM_VIOLATION_EXC_CHK = ['AES', 'eFuse'] + +# ---------------- TEE default tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='basic') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_crypto_aes(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='aes') + dut.run_all_single_board_cases(group='aes-gcm') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_crypto_sha(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='mbedtls') + dut.run_all_single_board_cases(group='hw_crypto') + + +# NOTE: Stress testing the AES performance case for interrupt-related edge-cases +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_aes_perf(dut: IdfDut) -> None: + # start test + for i in range(24): + if not i: + dut.expect_exact('Press ENTER to see the list of tests') + else: + dut.expect_exact("Enter next test, or 'enter' to see menu") + dut.write('"mbedtls AES performance"') + dut.expect_unity_test_output(timeout=60) + +# ---------------- TEE Exceptions generation Tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_apm_violation(dut: IdfDut) -> None: + for check in TEE_APM_VIOLATION_EXC_CHK: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test APM violation interrupt: {check}"') + exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if exc != 'Authority exception': + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_illegal_instruction(dut: IdfDut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test TEE-TEE violation: Illegal Instruction"') + exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if exc != 'Illegal instruction': + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_violation_checks(dut: IdfDut) -> None: + checks_list = TEE_VIOLATION_TEST_EXC_RSN[dut.target] + for test in checks_list: + memory, access_type = test + expected_exc = checks_list[test] + if expected_exc is None: + continue + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test TEE-TEE violation: {memory} ({access_type})"') + actual_exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if actual_exc != expected_exc: + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_isolation_checks(dut: IdfDut) -> None: + checks_list = REE_ISOLATION_TEST_EXC_RSN[dut.target] + for test in checks_list: + memory, access_type = test + expected_exc = checks_list[test] + if expected_exc is None: + continue + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test REE-TEE isolation: {memory} ({access_type})"') + actual_exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if actual_exc != expected_exc: + raise RuntimeError('Incorrect exception received!') + dut.expect('Exception origin: U-mode') + +# ---------------- TEE Local OTA tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +def test_esp_tee_ota_negative(dut: IdfDut) -> None: + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_neg_1]') + dut.expect_unity_test_output(timeout=120) + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_corrupted_img(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"Test TEE OTA - Corrupted image"') + dut.expect_unity_test_output(timeout=120) + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +class TeeOtaStage(Enum): + PRE = 1 + BEGIN = 2 + REBOOT = 3 + + +def tee_ota_stage_checks(dut: IdfDut, stage: TeeOtaStage, offset: str) -> None: + if stage == TeeOtaStage.PRE: + dut.expect(f'Loaded TEE app from partition at offset {offset}', timeout=30) + dut.expect('Current image already has been marked VALID', timeout=30) + elif stage == TeeOtaStage.BEGIN: + dut.expect('Starting TEE OTA...', timeout=30) + dut.expect('Running partition - Subtype: 0x30', timeout=30) + dut.expect_exact(f'Next partition - Subtype: 0x31 (Offset: {offset})', timeout=30) + elif stage == TeeOtaStage.REBOOT: + dut.expect(f'Loaded TEE app from partition at offset {offset}', timeout=30) + dut.expect_exact('Press ENTER to see the list of tests') + else: + raise ValueError('Undefined stage!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"Test TEE OTA - Reboot without ending OTA"') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + + # after reboot + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x10000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_valid_img(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_valid_img]') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + dut.expect('TEE OTA update successful!', timeout=30) + + # after reboot 1 + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x40000') + + # resetting device to check for image validity + dut.serial.hard_reset() + + # after reboot 2 + dut.expect('TEE otadata - Current image state: VALID', timeout=30) + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x40000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_rollback(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_rb() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_rollback]') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + dut.expect('TEE OTA update successful!', timeout=30) + + # after reboot 1 + dut.expect('TEE otadata - Current image state: NEW', timeout=10) + dut.expect('Loaded TEE app from partition at offset 0x40000', timeout=10) + rst_rsn = dut.expect(r'rst:(0x[0-9A-Fa-f]+) \(([^)]+)\)', timeout=30).group(2).decode() + # NOTE: LP_WDT_SYS is for ESP32-C6 case as bootloader fails to load the dummy TEE app + if rst_rsn != 'LP_WDT_SYS': + raise RuntimeError('Incorrect reset reason observed after TEE image failure!') + + # after rollback + dut.expect('TEE otadata - Current image state: PENDING_VERIFY', timeout=30) + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x10000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + +# ---------------- TEE Secure Storage tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_secure_storage(dut: IdfDut) -> None: + # Flash image and erase the secure_storage partition + dut.serial.custom_flash_with_empty_sec_stg() + + dut.run_all_single_board_cases(group='secure_storage') + +# ---------------- TEE Attestation tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_attestation(dut: IdfDut) -> None: + # Flash image and erase the secure_storage partition + dut.serial.custom_flash_with_empty_sec_stg() + + # start test + dut.run_all_single_board_cases(group='attestation') diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota new file mode 100644 index 000000000000..f6984d573cb8 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota @@ -0,0 +1,11 @@ +# Custom partition table +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_tee_ota.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_tee_ota.csv" + +# Increasing Bootloader log verbosity +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y + +# secure storage key slot for attestation +CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID=14 diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults new file mode 100644 index 000000000000..9f36b65b8096 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults @@ -0,0 +1,17 @@ +# Test-app related +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_INIT=n + +# Enabling TEE +CONFIG_SECURE_ENABLE_TEE=y +CONFIG_SECURE_TEE_DEBUG_MODE=y +CONFIG_SECURE_TEE_LOG_LEVEL_DEBUG=y +CONFIG_SECURE_TEE_TEST_MODE=y + +# Custom partition table +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +# TEE IRAM size +CONFIG_SECURE_TEE_IRAM_SIZE=0x9000