Merge branch 'feature/esp_tee' into 'master'

feat(security): Support for ESP-TEE

Closes IDF-6902, IDF-8268, IDF-9389, IDF-10479, IDF-10480, IDF-10482, IDF-10755, and IDF-11438

See merge request espressif/esp-idf!32050
This commit is contained in:
Mahavir Jain
2024-12-02 21:18:06 +08:00
184 changed files with 14193 additions and 146 deletions

View File

@@ -111,6 +111,7 @@
/components/esp_rom/ @esp-idf-codeowners/system @esp-idf-codeowners/bluetooth @esp-idf-codeowners/wifi /components/esp_rom/ @esp-idf-codeowners/system @esp-idf-codeowners/bluetooth @esp-idf-codeowners/wifi
/components/esp_security/ @esp-idf-codeowners/security /components/esp_security/ @esp-idf-codeowners/security
/components/esp_system/ @esp-idf-codeowners/system /components/esp_system/ @esp-idf-codeowners/system
/components/esp_tee/ @esp-idf-codeowners/security
/components/esp_timer/ @esp-idf-codeowners/system /components/esp_timer/ @esp-idf-codeowners/system
/components/esp-tls/ @esp-idf-codeowners/app-utilities /components/esp-tls/ @esp-idf-codeowners/app-utilities
/components/esp_vfs_*/ @esp-idf-codeowners/storage /components/esp_vfs_*/ @esp-idf-codeowners/storage

View File

@@ -13,29 +13,7 @@ endif()
# Add the following build specifications here, since these seem to be dependent # Add the following build specifications here, since these seem to be dependent
# on config values on the root Kconfig. # on config values on the root Kconfig.
if(NOT BOOTLOADER_BUILD) if(BOOTLOADER_BUILD)
if(CONFIG_COMPILER_OPTIMIZATION_SIZE)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
list(APPEND compile_options "-Oz")
else()
list(APPEND compile_options "-Os")
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
list(APPEND compile_options "-freorder-blocks")
endif()
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_COMPILER_OPTIMIZATION_NONE)
list(APPEND compile_options "-O0")
elseif(CONFIG_COMPILER_OPTIMIZATION_PERF)
list(APPEND compile_options "-O2")
endif()
else() # BOOTLOADER_BUILD
if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE) if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE)
if(CMAKE_C_COMPILER_ID MATCHES "Clang") if(CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -57,6 +35,37 @@ else() # BOOTLOADER_BUILD
list(APPEND compile_options "-O2") list(APPEND compile_options "-O2")
endif() endif()
elseif(ESP_TEE_BUILD)
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()
list(APPEND compile_options "-Os")
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
list(APPEND compile_options "-freorder-blocks")
endif()
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_COMPILER_OPTIMIZATION_NONE)
list(APPEND compile_options "-O0")
elseif(CONFIG_COMPILER_OPTIMIZATION_PERF)
list(APPEND compile_options "-O2")
endif()
endif() endif()
if(CONFIG_COMPILER_CXX_EXCEPTIONS) if(CONFIG_COMPILER_CXX_EXCEPTIONS)

View File

@@ -1,4 +1,5 @@
idf_build_get_property(target IDF_TARGET) idf_build_get_property(target IDF_TARGET)
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(${target} STREQUAL "linux") if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator 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) 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 # 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() return()
endif() endif()

View File

@@ -1,7 +1,9 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH}) set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH})
# Do not generate flash file when building bootloader # 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() return()
endif() endif()

View File

@@ -32,7 +32,8 @@ set(COMPONENTS
main main
efuse efuse
esp_system esp_system
newlib) newlib
esp_tee)
# EXTRA_COMPONENT_DIRS can be populated with directories containing one or several components. # 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. # Make sure this variable contains `bootloader_components` directory of the project being compiled.

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -53,6 +53,11 @@ void __attribute__((noreturn)) call_start_cpu0(void)
bootloader_reset(); 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 // 3. Load the app image for booting
bootloader_utility_load_boot_image(&bs, boot_index); bootloader_utility_load_boot_image(&bs, boot_index);
} }

View File

@@ -71,6 +71,7 @@ SECTIONS
*libbootloader_support.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) *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_efuse.*(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_utility.*(.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_sha.*(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*)

View File

@@ -1,9 +1,38 @@
idf_build_get_property(target IDF_TARGET) idf_build_get_property(target IDF_TARGET)
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(${target} STREQUAL "linux") if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator return() # This component is not supported by the POSIX/Linux simulator
endif() 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 set(srcs
"src/bootloader_common.c" "src/bootloader_common.c"
"src/bootloader_common_loader.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_soc.c"
"src/${IDF_TARGET}/bootloader_${IDF_TARGET}.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) list(APPEND priv_requires hal)
if(CONFIG_ESP_ROM_REV0_HAS_NO_ECDSA_INTERFACE) if(CONFIG_ESP_ROM_REV0_HAS_NO_ECDSA_INTERFACE)
list(APPEND srcs list(APPEND srcs

View File

@@ -13,11 +13,12 @@
#include "hal/efuse_ll.h" #include "hal/efuse_ll.h"
#include "hal/efuse_hal.h" #include "hal/efuse_hal.h"
#ifndef BOOTLOADER_BUILD #if !NON_OS_BUILD
#include "spi_flash_mmap.h" #include "spi_flash_mmap.h"
#endif #endif
#include "hal/spi_flash_ll.h" #include "hal/spi_flash_ll.h"
#include "rom/spi_flash.h" #include "rom/spi_flash.h"
#include "esp_private/cache_utils.h"
#if !CONFIG_IDF_TARGET_ESP32 #if !CONFIG_IDF_TARGET_ESP32
#include "hal/spimem_flash_ll.h" #include "hal/spimem_flash_ll.h"
#endif #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_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. #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... /* Normal app version maps to spi_flash_mmap.h operations...
*/ */
static const char *TAG = "bootloader_mmap"; 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); return esp_flash_erase_region(NULL, start_addr, size);
} }
#else //BOOTLOADER_BUILD #else // NON_OS_BUILD
/* Bootloader version, uses ROM functions only */ /* Bootloader version, uses ROM functions only */
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h" #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 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/opi_flash.h" #include "esp32p4/rom/opi_flash.h"
#endif #endif
#if ESP_TEE_BUILD
#include "esp_flash_partitions.h"
#include "esp32c6/rom/spi_flash.h"
#endif
static const char *TAG = "bootloader_flash"; 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 #if CONFIG_IDF_TARGET_ESP32
/* Use first 50 blocks in MMU for bootloader_mmap, /* Use first 50 blocks in MMU for bootloader_mmap,
50th block for bootloader_flash_read 50th block for bootloader_flash_read
*/ */
#define MMU_BLOCK0_VADDR SOC_DROM_LOW #define MMU_BLOCK0_VADDR SOC_DROM_LOW
#define MMAP_MMU_SIZE (0x320000) #define MMU_TOTAL_SIZE (0x320000)
#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE) #define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE)
#define FLASH_READ_VADDR MMU_BLOCK50_VADDR #define FLASH_READ_VADDR MMU_BLOCK50_VADDR
#else // !CONFIG_IDF_TARGET_ESP32 #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)`. * 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 * 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 #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 #endif
#define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE - SPI_FLASH_MMU_PAGE_SIZE) #define MMU_END_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE)
#define FLASH_READ_VADDR MMU_BLOCK63_VADDR #define MMU_BLOCKL_VADDR (MMU_END_VADDR - 1 * CONFIG_MMU_PAGE_SIZE)
#define FLASH_READ_VADDR MMU_BLOCKL_VADDR
#endif #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 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; 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() // Current bootloader mapping (ab)used for bootloader_read()
static uint32_t current_read_mapping = UINT32_MAX; 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) 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; 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. //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 size_after_paddr_aligned = (src_paddr - src_paddr_aligned) + size;
uint32_t actual_mapped_len = 0;
/** /**
* @note 1 * @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 * 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. * Now simply check if it's valid vaddr, didn't check if it's readable, writable or executable.
* TODO: IDF-4710 * 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"); ESP_EARLY_LOGE(TAG, "vaddr not valid");
return NULL; return NULL;
} }
@@ -204,15 +306,25 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
Cache_Read_Disable(0); Cache_Read_Disable(0);
Cache_Flush(0); Cache_Flush(0);
#else #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); 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 #endif
//---------------Do mapping------------------------ //---------------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 #if CONFIG_IDF_TARGET_ESP32
uint32_t count = GET_REQUIRED_MMU_PAGES(size, src_paddr); 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); 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)MMU_BLOCK0_VADDR, count * SPI_FLASH_MMU_PAGE_SIZE); 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) { if (e != 0) {
ESP_EARLY_LOGE(TAG, "cache_flash_mmu_set failed: %d", e); ESP_EARLY_LOGE(TAG, "cache_flash_mmu_set failed: %d", e);
Cache_Read_Enable(0); 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()`. * 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 * See above comments (note 1) about IDF-4710
*/ */
uint32_t actual_mapped_len = 0; mmu_hal_map_region(0, MMU_TARGET_FLASH0, FLASH_MMAP_VADDR, src_paddr_aligned, size_after_paddr_aligned, &actual_mapped_len);
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)FLASH_MMAP_VADDR, 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);
#endif #endif
/** /**
@@ -238,14 +349,19 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
Cache_Read_Enable(0); Cache_Read_Enable(0);
#else #else
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE #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 #endif
#if !ESP_TEE_BUILD
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); 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 #endif
mapped = true; 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) void bootloader_munmap(const void *mapping)
@@ -257,11 +373,18 @@ void bootloader_munmap(const void *mapping)
Cache_Flush(0); Cache_Flush(0);
mmu_init(0); mmu_init(0);
#else #else
#if !ESP_TEE_BUILD
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
mmu_hal_unmap_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 #endif
mapped = false; 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_Read_Disable(0);
Cache_Flush(0); Cache_Flush(0);
#else #else
#if !ESP_TEE_BUILD
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#elif CONFIG_ESP32C6_REV_MIN_0
rom_read_api_workaround();
#endif
#endif #endif
esp_rom_spiflash_result_t r = esp_rom_spiflash_read(src_addr, dest, size); 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 #if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0); Cache_Read_Enable(0);
#else #else
#if !ESP_TEE_BUILD
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif
#endif #endif
return spi_to_esp_err(r); 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_Read_Disable(0);
Cache_Flush(0); Cache_Flush(0);
#else #else
#if !ESP_TEE_BUILD
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); 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 #endif
//---------------Do mapping------------------------ //---------------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); Cache_Read_Enable(0);
#else #else
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE #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 #endif
#if !ESP_TEE_BUILD
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); 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 #endif
} }
map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at)); map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at));
dest_words[word] = *map_ptr; dest_words[word] = *map_ptr;
current_read_mapping = UINT32_MAX;
} }
return ESP_OK; 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 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; size_t alignment = write_encrypted ? 32 : 4;
if ((dest_addr % alignment) != 0) { if ((dest_addr % alignment) != 0) {
ESP_EARLY_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment); 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; return ESP_FAIL;
} }
err = bootloader_flash_unlock(); esp_err_t err = bootloader_flash_unlock();
if (err != ESP_OK) { if (err != ESP_OK) {
return err; return err;
} }
esp_rom_spiflash_result_t rc = ESP_ROM_SPIFLASH_RESULT_OK;
if (write_encrypted && !ENCRYPTION_IS_VIRTUAL) { 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 { } 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) 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; ++sector;
} }
} }
#if ESP_TEE_BUILD
spi_flash_check_and_flush_cache(start_addr, size);
#endif
return spi_to_esp_err(rc); 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
#endif // BOOTLOADER_BUILD #endif // NON_OS_BUILD
FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip) 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_SUPPORT CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT
#define XMC_VENDOR_ID_1 0x20 #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__) #define BOOTLOADER_FLASH_LOG(level, ...) ESP_EARLY_LOG##level(TAG, ##__VA_ARGS__)
#else #else
static DRAM_ATTR char bootloader_flash_tag[] = "bootloader_flash"; static DRAM_ATTR char bootloader_flash_tag[] = "bootloader_flash";

View File

@@ -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

View File

@@ -21,6 +21,8 @@ extern "C" {
#define PART_SUBTYPE_OTA_FLAG 0x10 #define PART_SUBTYPE_OTA_FLAG 0x10
#define PART_SUBTYPE_OTA_MASK 0x0f #define PART_SUBTYPE_OTA_MASK 0x0f
#define PART_SUBTYPE_TEST 0x20 #define PART_SUBTYPE_TEST 0x20
#define PART_SUBTYPE_TEE_0 0x30
#define PART_SUBTYPE_TEE_1 0x31
#define PART_TYPE_DATA 0x01 #define PART_TYPE_DATA 0x01
#define PART_SUBTYPE_DATA_OTA 0x00 #define PART_SUBTYPE_DATA_OTA 0x00
@@ -38,6 +40,9 @@ extern "C" {
#define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00 #define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00
#define PART_SUBTYPE_PARTITION_TABLE_OTA 0x01 #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_TYPE_END 0xff
#define PART_SUBTYPE_END 0xff #define PART_SUBTYPE_END 0xff

View File

@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -21,11 +21,16 @@ extern "C"
#define SPI_ERROR_LOG "spi flash error" #define SPI_ERROR_LOG "spi flash error"
#define MAX_OTA_SLOTS 16 #define MAX_OTA_SLOTS 16
#define MAX_TEE_OTA_SLOTS 2
typedef struct { typedef struct {
esp_partition_pos_t ota_info; esp_partition_pos_t ota_info;
esp_partition_pos_t factory; esp_partition_pos_t factory;
esp_partition_pos_t test; 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]; esp_partition_pos_t ota[MAX_OTA_SLOTS];
uint32_t app_count; uint32_t app_count;
uint32_t selected_subtype; uint32_t selected_subtype;

View File

@@ -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); 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. * @brief Load the selected partition and start application.
* *

View File

@@ -52,6 +52,10 @@
#include "esp_efuse.h" #include "esp_efuse.h"
#include "esp_fault.h" #include "esp_fault.h"
#if CONFIG_SECURE_ENABLE_TEE
#include "bootloader_utility_tee.h"
#endif
static const char *TAG = "boot"; static const char *TAG = "boot";
/* Reduce literal size for some generic string literals */ /* 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, uint32_t irom_size,
const esp_image_metadata_t *data); 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) 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; 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; bs->test = partition->pos;
partition_usage = "test app"; partition_usage = "test app";
break; 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: default:
/* OTA binary */ /* OTA binary */
if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { 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); esp_efuse_init_virtual_mode_in_flash(partition->pos.offset, partition->pos.size);
#endif #endif
break; 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: default:
partition_usage = "Unknown data"; partition_usage = "Unknown data";
break; break;
@@ -518,6 +553,29 @@ void bootloader_utility_load_boot_image_from_deep_sleep(void)
} }
#endif #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 #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) 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 #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( static void set_cache_and_start_app(
uint32_t drom_addr, uint32_t drom_addr,
uint32_t drom_load_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); cache_ll_l1_enable_bus(1, bus_mask);
#endif #endif
#if CONFIG_SECURE_ENABLE_TEE
//----------------------Unpacking and loading the TEE app----------------
unpack_load_tee_app(&tee_data);
#endif
//----------------------Enable Cache---------------- //----------------------Enable Cache----------------
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
// Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) // 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); ESP_LOGD(TAG, "start: 0x%08"PRIx32, entry_addr);
bootloader_atexit(); 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)); typedef void (*entry_t)(void) __attribute__((noreturn));
entry_t entry = ((entry_t) entry_addr); entry_t entry = ((entry_t) entry_addr);
// TODO: we have used quite a bit of stack at this point. // TODO: we have used quite a bit of stack at this point.
// use "movsp" instruction to reset stack back to where ROM stack starts. // use "movsp" instruction to reset stack back to where ROM stack starts.
(*entry)(); (*entry)();
#endif
} }
void bootloader_reset(void) void bootloader_reset(void)

View File

@@ -0,0 +1,260 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#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);
}

View File

@@ -23,6 +23,10 @@
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED #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)) #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
static const char *TAG = "secure_boot_v2"; 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); 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 /* 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) */ (Also, ensure if that public revoke bit is not set for the matched key) */
bool match = false; bool match = false;
@@ -285,6 +311,18 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t
match = true; 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) { 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; 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 #if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
/* Revoke the empty signature blocks */ /* Revoke the empty signature blocks */
if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) { if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) {

View File

@@ -13,7 +13,7 @@
#include "esp_log.h" #include "esp_log.h"
// startup_internal.h is necessary for startup function definition, which does not exist on Linux (TODO: IDF-9950) // 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" #include "esp_private/startup_internal.h"
static const char *TAG = "app_init"; 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 // startup function definition and execution does not exist on the Linux target
// (TODO: IDF-9950) // (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) ESP_SYSTEM_INIT_FN(init_show_app_info, CORE, BIT(0), 20)
{ {
// Load the current ELF SHA256 // Load the current ELF SHA256

View File

@@ -145,6 +145,9 @@ if(NOT non_os_build)
list(APPEND srcs "esp_clock_output.c") list(APPEND srcs "esp_clock_output.c")
endif() endif()
else() else()
if(ESP_TEE_BUILD)
list(APPEND srcs "esp_clk.c" "hw_random.c")
endif()
# Requires "_esp_error_check_failed()" function # Requires "_esp_error_check_failed()" function
list(APPEND priv_requires "esp_system") list(APPEND priv_requires "esp_system")
endif() endif()

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -9,7 +9,10 @@
#include <sys/param.h> #include <sys/param.h>
#include <sys/lock.h> #include <sys/lock.h>
#if !NON_OS_BUILD
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#endif
#include "esp_attr.h" #include "esp_attr.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
@@ -52,7 +55,11 @@
// g_ticks_us defined in ROMs for PRO and APP CPU // g_ticks_us defined in ROMs for PRO and APP CPU
extern uint32_t g_ticks_per_us_pro; 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; static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED;
#endif
#if SOC_RTC_MEM_SUPPORTED #if SOC_RTC_MEM_SUPPORTED
typedef struct { typedef struct {
@@ -64,6 +71,7 @@ typedef struct {
_Static_assert(sizeof(retain_mem_t) == 24, "retain_mem_t must be 24 bytes"); _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"); _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 __attribute__((section(".rtc_timer_data_in_rtc_mem"))) retain_mem_t s_rtc_timer_retain_mem;
static uint32_t calc_checksum(void) static uint32_t calc_checksum(void)
@@ -77,6 +85,7 @@ static uint32_t calc_checksum(void)
return checksum; return checksum;
} }
#define IS_RETAIN_MEM_VALID() (s_rtc_timer_retain_mem.checksum == calc_checksum()) #define IS_RETAIN_MEM_VALID() (s_rtc_timer_retain_mem.checksum == calc_checksum())
#endif // NON_OS_BUILD
#endif // SOC_RTC_MEM_SUPPORTED #endif // SOC_RTC_MEM_SUPPORTED
inline static int IRAM_ATTR s_get_cpu_freq_mhz(void) 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; return rtc_clk_xtal_freq_get() * MHZ;
} }
#if !NON_OS_BUILD
uint64_t esp_rtc_get_time_us(void) uint64_t esp_rtc_get_time_us(void)
{ {
portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock); 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; return esp_rtc_time_us;
#endif #endif
} }
#endif
void esp_clk_slowclk_cal_set(uint32_t new_cal) void esp_clk_slowclk_cal_set(uint32_t new_cal)
{ {
@@ -214,6 +225,7 @@ uint64_t esp_clk_rtc_time(void)
#endif #endif
} }
#if !NON_OS_BUILD
void esp_clk_private_lock(void) void esp_clk_private_lock(void)
{ {
portENTER_CRITICAL(&s_esp_rtc_time_lock); portENTER_CRITICAL(&s_esp_rtc_time_lock);
@@ -223,3 +235,4 @@ void esp_clk_private_unlock(void)
{ {
portEXIT_CRITICAL(&s_esp_rtc_time_lock); portEXIT_CRITICAL(&s_esp_rtc_time_lock);
} }
#endif

View File

@@ -13,9 +13,12 @@
#include "esp_cpu.h" #include "esp_cpu.h"
#include "soc/wdev_reg.h" #include "soc/wdev_reg.h"
#include "esp_private/esp_clk.h" #include "esp_private/esp_clk.h"
#include "esp_private/startup_internal.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if !ESP_TEE_BUILD
#include "esp_private/startup_internal.h"
#endif
#if SOC_LP_TIMER_SUPPORTED #if SOC_LP_TIMER_SUPPORTED
#include "hal/lp_timer_hal.h" #include "hal/lp_timer_hal.h"
#endif #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) ESP_SYSTEM_INIT_FN(init_rng_clock, SECONDARY, BIT(0), 102)
{ {
_lp_clkrst_ll_enable_rng_clock(true); _lp_clkrst_ll_enable_rng_clock(true);

View File

@@ -15,6 +15,7 @@
#include "xtensa_api.h" #include "xtensa_api.h"
#include "xt_utils.h" #include "xt_utils.h"
#elif __riscv #elif __riscv
#include "riscv/csr.h"
#include "riscv/rv_utils.h" #include "riscv/rv_utils.h"
#endif #endif
#include "esp_intr_alloc.h" #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(); return (int)rv_utils_get_core_id();
#endif #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 * @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__ #ifdef __XTENSA__
xt_utils_set_vecbase((uint32_t)ivt_addr); xt_utils_set_vecbase((uint32_t)ivt_addr);
#else #else
rv_utils_set_mtvec((uint32_t)ivt_addr); rv_utils_set_xtvec((uint32_t)ivt_addr);
#endif #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); assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
#ifdef __XTENSA__ #ifdef __XTENSA__
xthal_set_intclear((unsigned) (1 << intr_num)); 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 #else
rv_utils_intr_edge_ack((unsigned) intr_num); rv_utils_intr_edge_ack((unsigned) intr_num);
#endif #endif
#endif
} }
/* -------------------------------------------------- Memory Ports ----------------------------------------------------- /* -------------------------------------------------- Memory Ports -----------------------------------------------------

View File

@@ -61,9 +61,9 @@ extern "C" {
#define regi2c_write_reg_mask_raw esp_rom_regi2c_write_mask #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. * without the need of a lock.
*/ */
#define regi2c_ctrl_read_reg regi2c_read_reg_raw #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_enter_critical(void);
void regi2c_exit_critical(void); void regi2c_exit_critical(void);
#endif // BOOTLOADER_BUILD #endif // NON_OS_BUILD
/* Convenience macros for the above functions, these use register definitions /* Convenience macros for the above functions, these use register definitions
* from regi2c_xxx.h header files. * from regi2c_xxx.h header files.

View File

@@ -649,9 +649,13 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
} }
#endif #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 #if SOC_INT_PLIC_SUPPORTED
/* Make sure the interrupt is not delegated to user mode (IDF uses machine mode only) */ /* Make sure the interrupt is not delegated to user mode (IDF uses machine mode only) */
RV_CLEAR_CSR(mideleg, BIT(intr)); RV_CLEAR_CSR(mideleg, BIT(intr));
#endif
#endif #endif
portEXIT_CRITICAL(&spinlock); portEXIT_CRITICAL(&spinlock);

View File

@@ -1,4 +1,6 @@
if(BOOTLOADER_BUILD) idf_build_get_property(non_os_build NON_OS_BUILD)
if(non_os_build)
return() return()
endif() endif()

View File

@@ -1,3 +1,5 @@
idf_build_get_property(non_os_build NON_OS_BUILD)
set(srcs "rtc_clk_init.c" set(srcs "rtc_clk_init.c"
"rtc_clk.c" "rtc_clk.c"
"pmu_param.c" "pmu_param.c"
@@ -8,7 +10,7 @@ set(srcs "rtc_clk_init.c"
"ocode_init.c" "ocode_init.c"
) )
if(NOT BOOTLOADER_BUILD) if(NOT non_os_build)
list(APPEND srcs "sar_periph_ctrl.c") list(APPEND srcs "sar_periph_ctrl.c")
endif() endif()

View File

@@ -62,6 +62,10 @@ static void esp_cpu_configure_invalid_regions(void)
// 7. End of address space // 7. End of address space
PMA_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE); PMA_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE);
PMA_ENTRY_SET_TOR(12, UINT32_MAX, PMA_TOR | 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) void esp_cpu_configure_region_protection(void)
@@ -112,6 +116,14 @@ void esp_cpu_configure_region_protection(void)
// //
esp_cpu_configure_invalid_regions(); 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 // Configure all the valid address regions using PMP
// //

View File

@@ -6,6 +6,7 @@
#include "esp_cpu.h" #include "esp_cpu.h"
#include "esp_riscv_intr.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) 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) * Interrupts 3, 4 and 7 are unavailable for PULP CPU as they are bound to Core-Local Interrupts (CLINT)
*/ */
// [TODO: IDF-2465] // [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->priority = 1;
intr_desc_ret->type = ESP_CPU_INTR_TYPE_NA; intr_desc_ret->type = ESP_CPU_INTR_TYPE_NA;

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,15 +10,40 @@
#include "../ext_mem_layout.h" #include "../ext_mem_layout.h"
#include "hal/mmu_types.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 * The start addresses in this list should always be sorted from low to high, as MMU driver will need to
* coalesce adjacent regions * coalesce adjacent regions
*/ */
const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = { const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {
[0] = { [0] = {
.start = SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW, .start = MMU_IRAM0_LINEAR_ADDRESS_LOW,
.end = SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH, .end = MMU_IRAM0_LINEAR_ADDRESS_HIGH,
.size = SOC_BUS_SIZE(SOC_MMU_IRAM0_LINEAR), .size = MMU_IRAM0_LINEAR_ADDRESS_HIGH - MMU_IRAM0_LINEAR_ADDRESS_LOW,
.bus_id = CACHE_BUS_IBUS0 | CACHE_BUS_DBUS0, .bus_id = CACHE_BUS_IBUS0 | CACHE_BUS_DBUS0,
.targets = MMU_TARGET_FLASH0, .targets = MMU_TARGET_FLASH0,
.caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT, .caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT,

View File

@@ -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_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_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_OTA = 0x00, //!< OTA selection partition
ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition
ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS 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_SPIFFS = 0x82, //!< SPIFFS partition
ESP_PARTITION_SUBTYPE_DATA_LITTLEFS = 0x83, //!< LITTLEFS 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") #if __has_include("extra_partition_subtypes.inc")
#include "extra_partition_subtypes.inc" #include "extra_partition_subtypes.inc"
#endif #endif

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -11,6 +11,7 @@
#include "esp_private/sar_periph_ctrl.h" #include "esp_private/sar_periph_ctrl.h"
#include "freertos/FreeRTOS.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. * 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; return 0;
#endif #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();
}

View File

@@ -126,6 +126,12 @@ if(CONFIG_ESP_ROM_HAS_VERSION)
rom_linker_script("version") rom_linker_script("version")
endif() endif()
if(ESP_TEE_BUILD)
if(target STREQUAL "esp32c6")
rom_linker_script("spiflash")
endif()
endif()
if(BOOTLOADER_BUILD) if(BOOTLOADER_BUILD)
if(target STREQUAL "esp32") if(target STREQUAL "esp32")
if(NOT CONFIG_SPI_FLASH_ROM_DRIVER_PATCH) if(NOT CONFIG_SPI_FLASH_ROM_DRIVER_PATCH)

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -259,11 +259,11 @@ gpio_bypass_matrix_in = 0x40000714;
***************************************/ ***************************************/
/* Functions */ /* Functions */
esprv_intc_int_set_priority = 0x40000718; PROVIDE( esprv_intc_int_set_priority = 0x40000718 );
esprv_intc_int_set_threshold = 0x4000071c; PROVIDE( esprv_intc_int_set_threshold = 0x4000071c );
esprv_intc_int_enable = 0x40000720; PROVIDE( esprv_intc_int_enable = 0x40000720 );
esprv_intc_int_disable = 0x40000724; PROVIDE( esprv_intc_int_disable = 0x40000724 );
esprv_intc_int_set_type = 0x40000728; PROVIDE( esprv_intc_int_set_type = 0x40000728 );
PROVIDE( intr_handler_set = 0x4000072c ); PROVIDE( intr_handler_set = 0x4000072c );
intr_matrix_set = 0x40000730; intr_matrix_set = 0x40000730;
ets_intr_lock = 0x40000734; ets_intr_lock = 0x40000734;

View File

@@ -1,4 +1,5 @@
idf_build_get_property(target IDF_TARGET) 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 # On Linux, we only support a few features, hence this simple component registration
if(${target} STREQUAL "linux") 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") list(APPEND srcs "fpga_overrides_rng.c")
endif() endif()
if(BOOTLOADER_BUILD) if(BOOTLOADER_BUILD OR esp_tee_build)
# "_esp_error_check_failed()" requires spi_flash module # "_esp_error_check_failed()" requires spi_flash module
# Bootloader relies on some Kconfig options defined in esp_system. # Bootloader relies on some Kconfig options defined in esp_system.
idf_component_register(SRCS "${srcs}" REQUIRES spi_flash) idf_component_register(SRCS "${srcs}" REQUIRES spi_flash)

View File

@@ -121,7 +121,7 @@ menu "ESP System Settings"
config ESP_SYSTEM_PMP_IDRAM_SPLIT config ESP_SYSTEM_PMP_IDRAM_SPLIT
bool "Enable IRAM/DRAM split protection" 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" default "y"
help help
If enabled, the CPU watches all the memory access and raises an exception in case 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 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. 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 config ESP_SYSTEM_MEMPROT_FEATURE
bool "Enable memory protection" bool "Enable memory protection"
depends on SOC_MEMPROT_SUPPORTED depends on SOC_MEMPROT_SUPPORTED
@@ -592,7 +599,8 @@ menu "ESP System Settings"
config ESP_SYSTEM_HW_STACK_GUARD config ESP_SYSTEM_HW_STACK_GUARD
bool "Hardware 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 default y
help help
This config allows to trigger a panic interrupt when Stack Pointer register goes out of allocated stack This config allows to trigger a panic interrupt when Stack Pointer register goes out of allocated stack

View File

@@ -15,7 +15,13 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#include "ld.common" #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_END 0x4086E610 /* 2nd stage bootloader iram_loader_seg start address */
#define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START #define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START
@@ -35,8 +41,14 @@ MEMORY
*/ */
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS #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 */ /* Flash mapped instruction data */
irom_seg (RX) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20 irom_seg (RX) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20
#endif
/** /**
* (0x20 offset above is a convenience for the app binary image generation. * (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 sram_seg (RWX) : org = SRAM_SEG_START, len = SRAM_SEG_SIZE
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS #if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
#if CONFIG_SECURE_ENABLE_TEE
/* Flash mapped constant data */ /* 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 drom_seg (R) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20
#endif
/* (See irom_seg for meaning of 0x20 offset in the above.) */ /* (See irom_seg for meaning of 0x20 offset in the above.) */
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS #endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS

View File

@@ -173,11 +173,22 @@ SECTIONS
/* Vectors go to start of IRAM */ /* Vectors go to start of IRAM */
ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned"); ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned");
_vector_table_start = ABSOLUTE(.);
KEEP(*(.exception_vectors_table.text)); KEEP(*(.exception_vectors_table.text));
KEEP(*(.exception_vectors.text)); KEEP(*(.exception_vectors.text));
ALIGNED_SYMBOL(4, _invalid_pc_placeholder) 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 */ /* Code marked as running out of IRAM */
_iram_text_start = ABSOLUTE(.); _iram_text_start = ABSOLUTE(.);

View File

@@ -419,6 +419,15 @@ void IRAM_ATTR call_start_cpu0(void)
esp_cpu_intr_set_mtvt_addr(&_mtvt_table); esp_cpu_intr_set_mtvt_addr(&_mtvt_table);
#endif #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); rst_reas[0] = esp_rom_get_reset_reason(0);
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
rst_reas[1] = esp_rom_get_reset_reason(1); rst_reas[1] = esp_rom_get_reset_reason(1);
@@ -606,8 +615,12 @@ void IRAM_ATTR call_start_cpu0(void)
#else #else
ESP_EARLY_LOGI(TAG, "Multicore app"); ESP_EARLY_LOGI(TAG, "Multicore app");
#endif #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(); bootloader_init_mem();
#endif
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
s_cpu_up[0] = true; s_cpu_up[0] = true;

View File

@@ -63,6 +63,12 @@ ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10)
return ESP_OK; 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) 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) -> // [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 #endif // CONFIG_ESP_BROWNOUT_DET
return ESP_OK; return ESP_OK;
} }
#endif
ESP_SYSTEM_INIT_FN(init_newlib_time, CORE, BIT(0), 105) ESP_SYSTEM_INIT_FN(init_newlib_time, CORE, BIT(0), 105)
{ {

View File

@@ -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()

View File

@@ -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

View File

@@ -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 <stdint.h>
#include <stdio.h>
#include <stddef.h>
#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

View File

@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#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

View File

@@ -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 <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#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

View File

@@ -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})

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -0,0 +1,218 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#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);
}

View File

@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#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;
}

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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()

View File

@@ -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})

View File

@@ -0,0 +1,341 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#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;
}

View File

@@ -0,0 +1,288 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#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;
}

View File

@@ -0,0 +1,545 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <sys/param.h>
#include <sys/queue.h>
#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
}

View File

@@ -0,0 +1,304 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#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;
}

View File

@@ -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

View File

@@ -0,0 +1,278 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <sys/queue.h>
#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

View File

@@ -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})

View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#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);
}

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/queue.h>
#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;
}

View File

@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#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);

View File

@@ -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})

View File

@@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#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;
}

View File

@@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#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);
}

View File

@@ -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

View File

@@ -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}
)

View File

@@ -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 <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#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

View File

@@ -0,0 +1,669 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#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);
}

View File

@@ -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);
}

View File

@@ -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}\"")

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,125 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#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
}

View File

@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <sys/param.h>
#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");

View File

@@ -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;
}

View File

@@ -0,0 +1,123 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#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();
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -0,0 +1,410 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#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

View File

@@ -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) {}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/json_generator:
version: "^1.1.2"
rules:
- if: "target in [esp32c6]"

View File

@@ -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

View File

@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifndef __ASSEMBLER__
#include <stdlib.h>
#include <stdint.h>
#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__

View File

@@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <string.h>
#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

View File

@@ -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!");

View File

@@ -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)

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <assert.h>
#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;
}

View File

@@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <assert.h>
#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;
}

View File

@@ -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");
}

View File

@@ -0,0 +1,179 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#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");
}

View File

@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdint.h>
#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);
}

Some files were not shown because too many files have changed in this diff Show More