mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 05:34:32 +02:00
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:
@@ -111,6 +111,7 @@
|
||||
/components/esp_rom/ @esp-idf-codeowners/system @esp-idf-codeowners/bluetooth @esp-idf-codeowners/wifi
|
||||
/components/esp_security/ @esp-idf-codeowners/security
|
||||
/components/esp_system/ @esp-idf-codeowners/system
|
||||
/components/esp_tee/ @esp-idf-codeowners/security
|
||||
/components/esp_timer/ @esp-idf-codeowners/system
|
||||
/components/esp-tls/ @esp-idf-codeowners/app-utilities
|
||||
/components/esp_vfs_*/ @esp-idf-codeowners/storage
|
||||
|
@@ -13,29 +13,7 @@ endif()
|
||||
# Add the following build specifications here, since these seem to be dependent
|
||||
# on config values on the root Kconfig.
|
||||
|
||||
if(NOT BOOTLOADER_BUILD)
|
||||
|
||||
if(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(BOOTLOADER_BUILD)
|
||||
|
||||
if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
@@ -57,6 +35,37 @@ else() # BOOTLOADER_BUILD
|
||||
list(APPEND compile_options "-O2")
|
||||
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()
|
||||
|
||||
if(CONFIG_COMPILER_CXX_EXCEPTIONS)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
@@ -7,7 +8,9 @@ endif()
|
||||
idf_component_register(PRIV_REQUIRES partition_table esptool_py)
|
||||
|
||||
# Do not generate flash file when building bootloader or is in early expansion of the build
|
||||
if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER)
|
||||
# This also applies to the ESP-TEE build, as the esp_tee component only requires the
|
||||
# Kconfig options from the bootloader
|
||||
if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
|
||||
set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH})
|
||||
|
||||
# Do not generate flash file when building bootloader
|
||||
if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER)
|
||||
if(BOOTLOADER_BUILD OR esp_tee_build OR NOT CONFIG_APP_BUILD_BOOTLOADER)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
@@ -32,7 +32,8 @@ set(COMPONENTS
|
||||
main
|
||||
efuse
|
||||
esp_system
|
||||
newlib)
|
||||
newlib
|
||||
esp_tee)
|
||||
|
||||
# EXTRA_COMPONENT_DIRS can be populated with directories containing one or several components.
|
||||
# Make sure this variable contains `bootloader_components` directory of the project being compiled.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -53,6 +53,11 @@ void __attribute__((noreturn)) call_start_cpu0(void)
|
||||
bootloader_reset();
|
||||
}
|
||||
|
||||
// 2.1 Load the TEE image
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
bootloader_utility_load_tee_image(&bs);
|
||||
#endif
|
||||
|
||||
// 3. Load the app image for booting
|
||||
bootloader_utility_load_boot_image(&bs, boot_index);
|
||||
}
|
||||
|
@@ -71,6 +71,7 @@ SECTIONS
|
||||
*libbootloader_support.a:bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable)
|
||||
*libbootloader_support.a:bootloader_efuse.*(.literal .text .literal.* .text.*)
|
||||
*libbootloader_support.a:bootloader_utility.*(.literal .text .literal.* .text.*)
|
||||
*libbootloader_support.a:bootloader_utility_tee.*(.literal .text .literal.* .text.*)
|
||||
*libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*)
|
||||
*libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*)
|
||||
*libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*)
|
||||
|
@@ -1,9 +1,38 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
endif()
|
||||
|
||||
if(esp_tee_build)
|
||||
set(tee_inc_dirs "include"
|
||||
"private_include"
|
||||
"bootloader_flash/include")
|
||||
|
||||
set(tee_srcs "src/flash_partitions.c"
|
||||
"src/${IDF_TARGET}/bootloader_sha.c"
|
||||
"src/bootloader_common_loader.c"
|
||||
"src/esp_image_format.c"
|
||||
"src/bootloader_utility.c"
|
||||
"src/bootloader_utility_tee.c"
|
||||
"bootloader_flash/src/bootloader_flash.c")
|
||||
|
||||
if(CONFIG_SECURE_BOOT_V2_ENABLED)
|
||||
if(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME)
|
||||
list(APPEND tee_srcs "src/secure_boot_v2/secure_boot_signatures_bootloader.c"
|
||||
"src/secure_boot_v2/secure_boot.c"
|
||||
"src/${IDF_TARGET}/secure_boot_secure_features.c")
|
||||
endif()
|
||||
list(APPEND priv_requires efuse)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${tee_srcs}
|
||||
INCLUDE_DIRS ${tee_inc_dirs}
|
||||
PRIV_REQUIRES efuse)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(srcs
|
||||
"src/bootloader_common.c"
|
||||
"src/bootloader_common_loader.c"
|
||||
@@ -49,6 +78,9 @@ if(BOOTLOADER_BUILD OR CONFIG_APP_BUILD_TYPE_RAM)
|
||||
"src/${IDF_TARGET}/bootloader_soc.c"
|
||||
"src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c"
|
||||
)
|
||||
if(CONFIG_SECURE_ENABLE_TEE)
|
||||
list(APPEND srcs "src/bootloader_utility_tee.c")
|
||||
endif()
|
||||
list(APPEND priv_requires hal)
|
||||
if(CONFIG_ESP_ROM_REV0_HAS_NO_ECDSA_INTERFACE)
|
||||
list(APPEND srcs
|
||||
|
@@ -13,11 +13,12 @@
|
||||
#include "hal/efuse_ll.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
|
||||
#ifndef BOOTLOADER_BUILD
|
||||
#if !NON_OS_BUILD
|
||||
#include "spi_flash_mmap.h"
|
||||
#endif
|
||||
#include "hal/spi_flash_ll.h"
|
||||
#include "rom/spi_flash.h"
|
||||
#include "esp_private/cache_utils.h"
|
||||
#if !CONFIG_IDF_TARGET_ESP32
|
||||
#include "hal/spimem_flash_ll.h"
|
||||
#endif
|
||||
@@ -44,7 +45,7 @@
|
||||
#define ESP_BOOTLOADER_SPIFLASH_QE_GD_SR2 BIT1 // QE position when you write 8 bits(for SR2) at one time.
|
||||
#define ESP_BOOTLOADER_SPIFLASH_QE_SR1_2BYTE BIT9 // QE position when you write 16 bits at one time.
|
||||
|
||||
#ifndef BOOTLOADER_BUILD
|
||||
#if !NON_OS_BUILD
|
||||
/* Normal app version maps to spi_flash_mmap.h operations...
|
||||
*/
|
||||
static const char *TAG = "bootloader_mmap";
|
||||
@@ -111,7 +112,7 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
|
||||
return esp_flash_erase_region(NULL, start_addr, size);
|
||||
}
|
||||
|
||||
#else //BOOTLOADER_BUILD
|
||||
#else // NON_OS_BUILD
|
||||
/* Bootloader version, uses ROM functions only */
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/cache.h"
|
||||
@@ -128,15 +129,46 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/opi_flash.h"
|
||||
#endif
|
||||
|
||||
#if ESP_TEE_BUILD
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp32c6/rom/spi_flash.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "bootloader_flash";
|
||||
|
||||
/*
|
||||
* NOTE: Memory mapping strategy
|
||||
*
|
||||
* Bootloader:
|
||||
* - Uses the first N-1 MMU entries for general memory mapping.
|
||||
* - Reserves the Nth (last) MMU entry for flash read through the cache
|
||||
* (auto-decryption).
|
||||
* - This strategy is viable because the bootloader runs exclusively
|
||||
* on the device from the internal SRAM.
|
||||
*
|
||||
* ESP-TEE (Trusted Execution Environment)
|
||||
* - Cannot adopt the strategy used by the bootloader as the TEE app operates
|
||||
* in parallel to the REE.
|
||||
* - The few initial MMU entries have already been taken by the TEE and REE
|
||||
* application flash IDROM segments.
|
||||
* - The REE could have also mapped some custom flash partitions it requires.
|
||||
* - Therefore, the TEE uses MMU entries from the end of the range, with the number
|
||||
* of entries corresponding to the size of its IDROM segment sizes.
|
||||
* - The final MMU entry in this range is reserved for flash reads through the
|
||||
* cache (auto-decryption).
|
||||
* - The pages used by TEE are protected by PMP (Physical Memory Protection).
|
||||
* While REE attempts to mmap this protected area would trigger a load access
|
||||
* fault, this is unlikely since the MMU can address up to 16MB at once.
|
||||
*/
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/* Use first 50 blocks in MMU for bootloader_mmap,
|
||||
50th block for bootloader_flash_read
|
||||
*/
|
||||
#define MMU_BLOCK0_VADDR SOC_DROM_LOW
|
||||
#define MMAP_MMU_SIZE (0x320000)
|
||||
#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE)
|
||||
#define MMU_TOTAL_SIZE (0x320000)
|
||||
#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE)
|
||||
#define FLASH_READ_VADDR MMU_BLOCK50_VADDR
|
||||
|
||||
#else // !CONFIG_IDF_TARGET_ESP32
|
||||
@@ -150,21 +182,89 @@ static const char *TAG = "bootloader_flash";
|
||||
* On ESP32S2 we use `(SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW)`.
|
||||
* As this code is in bootloader, we keep this on ESP32S2
|
||||
*/
|
||||
#define MMAP_MMU_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped
|
||||
#define MMU_TOTAL_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped
|
||||
#else
|
||||
#define MMAP_MMU_SIZE (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_DRAM_FLASH_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped
|
||||
#define MMU_TOTAL_SIZE (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_DRAM_FLASH_ADDRESS_LOW) // This mmu size means that the mmu size to be mapped
|
||||
#endif
|
||||
#define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMAP_MMU_SIZE - SPI_FLASH_MMU_PAGE_SIZE)
|
||||
#define FLASH_READ_VADDR MMU_BLOCK63_VADDR
|
||||
#define MMU_END_VADDR (MMU_BLOCK0_VADDR + MMU_TOTAL_SIZE)
|
||||
#define MMU_BLOCKL_VADDR (MMU_END_VADDR - 1 * CONFIG_MMU_PAGE_SIZE)
|
||||
#define FLASH_READ_VADDR MMU_BLOCKL_VADDR
|
||||
#endif
|
||||
|
||||
#if !ESP_TEE_BUILD
|
||||
#define MMAP_MMU_SIZE (MMU_TOTAL_SIZE)
|
||||
// Represents the MMU pages available for mmapping by the bootloader
|
||||
#define MMU_FREE_PAGES (MMAP_MMU_SIZE / CONFIG_MMU_PAGE_SIZE)
|
||||
#define FLASH_MMAP_VADDR (MMU_BLOCK0_VADDR)
|
||||
#else /* ESP_TEE_BUILD */
|
||||
#define MMAP_MMU_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
|
||||
// Represents the MMU pages available for mmapping by the TEE
|
||||
#define MMU_FREE_PAGES (MMAP_MMU_SIZE / CONFIG_MMU_PAGE_SIZE)
|
||||
#define FLASH_MMAP_VADDR (MMU_END_VADDR - (MMU_FREE_PAGES + 1) * CONFIG_MMU_PAGE_SIZE)
|
||||
#endif /* !ESP_TEE_BUILD */
|
||||
|
||||
static bool mapped;
|
||||
|
||||
// Required for bootloader_flash_munmap() for ESP-TEE
|
||||
static uint32_t current_mapped_size;
|
||||
|
||||
// Current bootloader mapping (ab)used for bootloader_read()
|
||||
static uint32_t current_read_mapping = UINT32_MAX;
|
||||
|
||||
#if ESP_TEE_BUILD && CONFIG_IDF_TARGET_ESP32C6
|
||||
extern void spi_common_set_dummy_output(esp_rom_spiflash_read_mode_t mode);
|
||||
extern void spi_dummy_len_fix(uint8_t spi, uint8_t freqdiv);
|
||||
|
||||
/* TODO: [ESP-TEE] Workarounds for the ROM read API
|
||||
*
|
||||
* The esp_rom_spiflash_read API requires two workarounds on ESP32-C6 ECO0:
|
||||
*
|
||||
* 1. [IDF-7199] Call esp_rom_spiflash_write API once before reading.
|
||||
* Without this, reads return corrupted data.
|
||||
*
|
||||
* 2. Configure ROM flash parameters before each read using the function below.
|
||||
* Without this, the first byte read is corrupted.
|
||||
*
|
||||
* Note: These workarounds are not needed for ESP32-C6 ECO1 and later versions.
|
||||
*/
|
||||
static void rom_read_api_workaround(void)
|
||||
{
|
||||
static bool is_first_call = true;
|
||||
if (is_first_call) {
|
||||
uint32_t dummy_val = UINT32_MAX;
|
||||
uint32_t dest_addr = ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN;
|
||||
esp_rom_spiflash_write(dest_addr, &dummy_val, sizeof(dummy_val));
|
||||
is_first_call = false;
|
||||
}
|
||||
|
||||
uint32_t freqdiv = 0;
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHFREQ_80M
|
||||
freqdiv = 1;
|
||||
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
|
||||
freqdiv = 2;
|
||||
#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M
|
||||
freqdiv = 4;
|
||||
#endif
|
||||
|
||||
esp_rom_spiflash_read_mode_t read_mode;
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO
|
||||
read_mode = ESP_ROM_SPIFLASH_QIO_MODE;
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
read_mode = ESP_ROM_SPIFLASH_QOUT_MODE;
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_DIO
|
||||
read_mode = ESP_ROM_SPIFLASH_DIO_MODE;
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT
|
||||
read_mode = ESP_ROM_SPIFLASH_DOUT_MODE;
|
||||
#endif
|
||||
|
||||
esp_rom_spiflash_config_clk(freqdiv, 1);
|
||||
spi_dummy_len_fix(1, freqdiv);
|
||||
esp_rom_spiflash_config_readmode(read_mode);
|
||||
spi_common_set_dummy_output(read_mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t bootloader_mmap_get_free_pages(void)
|
||||
{
|
||||
/**
|
||||
@@ -188,13 +288,15 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
|
||||
uint32_t src_paddr_aligned = src_paddr & MMU_FLASH_MASK;
|
||||
//The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled.
|
||||
uint32_t size_after_paddr_aligned = (src_paddr - src_paddr_aligned) + size;
|
||||
|
||||
uint32_t actual_mapped_len = 0;
|
||||
/**
|
||||
* @note 1
|
||||
* Will add here a check to make sure the vaddr is on read-only and executable buses, since we use others for psram
|
||||
* Now simply check if it's valid vaddr, didn't check if it's readable, writable or executable.
|
||||
* TODO: IDF-4710
|
||||
*/
|
||||
if (mmu_ll_check_valid_ext_vaddr_region(0, MMU_BLOCK0_VADDR, size_after_paddr_aligned, MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION) == 0) {
|
||||
if (mmu_ll_check_valid_ext_vaddr_region(0, FLASH_MMAP_VADDR, size_after_paddr_aligned, MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION) == 0) {
|
||||
ESP_EARLY_LOGE(TAG, "vaddr not valid");
|
||||
return NULL;
|
||||
}
|
||||
@@ -204,15 +306,25 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
|
||||
Cache_Read_Disable(0);
|
||||
Cache_Flush(0);
|
||||
#else
|
||||
/* NOTE: [ESP-TEE] Cache suspension vs disabling
|
||||
*
|
||||
* For ESP-TEE , we use suspend the cache instead of disabling it to avoid flushing the entire cache.
|
||||
* This prevents performance hits when returning to the REE app due to cache misses.
|
||||
* This is not applicable to the bootloader as it runs exclusively on the device from the internal SRAM.
|
||||
*/
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#else
|
||||
cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//---------------Do mapping------------------------
|
||||
ESP_EARLY_LOGD(TAG, "rodata starts from paddr=0x%08" PRIx32 ", size=0x%" PRIx32 ", will be mapped to vaddr=0x%08" PRIx32, src_paddr, size, (uint32_t)MMU_BLOCK0_VADDR);
|
||||
ESP_EARLY_LOGD(TAG, "rodata starts from paddr=0x%08" PRIx32 ", size=0x%" PRIx32 ", will be mapped to vaddr=0x%08" PRIx32, src_paddr, size, (uint32_t)FLASH_MMAP_VADDR);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t count = GET_REQUIRED_MMU_PAGES(size, src_paddr);
|
||||
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR, src_paddr_aligned, 64, count);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)MMU_BLOCK0_VADDR, count * SPI_FLASH_MMU_PAGE_SIZE);
|
||||
int e = cache_flash_mmu_set(0, 0, FLASH_MMAP_VADDR, src_paddr_aligned, 64, count);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)FLASH_MMAP_VADDR, count * SPI_FLASH_MMU_PAGE_SIZE);
|
||||
if (e != 0) {
|
||||
ESP_EARLY_LOGE(TAG, "cache_flash_mmu_set failed: %d", e);
|
||||
Cache_Read_Enable(0);
|
||||
@@ -223,9 +335,8 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
|
||||
* This hal won't return error, it assumes the inputs are valid. The related check should be done in `bootloader_mmap()`.
|
||||
* See above comments (note 1) about IDF-4710
|
||||
*/
|
||||
uint32_t actual_mapped_len = 0;
|
||||
mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_BLOCK0_VADDR, src_paddr_aligned, size_after_paddr_aligned, &actual_mapped_len);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)MMU_BLOCK0_VADDR, actual_mapped_len);
|
||||
mmu_hal_map_region(0, MMU_TARGET_FLASH0, FLASH_MMAP_VADDR, src_paddr_aligned, size_after_paddr_aligned, &actual_mapped_len);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", src_paddr_aligned, (uint32_t)FLASH_MMAP_VADDR, actual_mapped_len);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -238,14 +349,19 @@ const void *bootloader_mmap(uint32_t src_paddr, uint32_t size)
|
||||
Cache_Read_Enable(0);
|
||||
#else
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, MMU_BLOCK0_VADDR, actual_mapped_len);
|
||||
cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, FLASH_MMAP_VADDR, actual_mapped_len);
|
||||
#endif
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#else
|
||||
cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mapped = true;
|
||||
current_mapped_size = actual_mapped_len;
|
||||
|
||||
return (void *)(MMU_BLOCK0_VADDR + (src_paddr - src_paddr_aligned));
|
||||
return (void *)(FLASH_MMAP_VADDR + (src_paddr - src_paddr_aligned));
|
||||
}
|
||||
|
||||
void bootloader_munmap(const void *mapping)
|
||||
@@ -257,11 +373,18 @@ void bootloader_munmap(const void *mapping)
|
||||
Cache_Flush(0);
|
||||
mmu_init(0);
|
||||
#else
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
mmu_hal_unmap_all();
|
||||
#else
|
||||
cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
mmu_hal_unmap_region(0, FLASH_MMAP_VADDR, current_mapped_size);
|
||||
cache_hal_invalidate_addr(FLASH_MMAP_VADDR, current_mapped_size);
|
||||
cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#endif
|
||||
#endif
|
||||
mapped = false;
|
||||
current_read_mapping = UINT32_MAX;
|
||||
current_mapped_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +408,11 @@ static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, s
|
||||
Cache_Read_Disable(0);
|
||||
Cache_Flush(0);
|
||||
#else
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#elif CONFIG_ESP32C6_REV_MIN_0
|
||||
rom_read_api_workaround();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
esp_rom_spiflash_result_t r = esp_rom_spiflash_read(src_addr, dest, size);
|
||||
@@ -293,7 +420,9 @@ static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, s
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
Cache_Read_Enable(0);
|
||||
#else
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return spi_to_esp_err(r);
|
||||
@@ -316,7 +445,13 @@ static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest
|
||||
Cache_Read_Disable(0);
|
||||
Cache_Flush(0);
|
||||
#else
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_disable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#else
|
||||
cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
//---------------Invalidating entries at to-be-mapped v_addr------------------------
|
||||
cache_hal_invalidate_addr(FLASH_READ_VADDR, SPI_FLASH_MMU_PAGE_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//---------------Do mapping------------------------
|
||||
@@ -337,13 +472,18 @@ static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest
|
||||
Cache_Read_Enable(0);
|
||||
#else
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, MMU_BLOCK0_VADDR, actual_mapped_len);
|
||||
cache_ll_invalidate_addr(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL, FLASH_MMAP_VADDR, actual_mapped_len);
|
||||
#endif
|
||||
#if !ESP_TEE_BUILD
|
||||
cache_hal_enable(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#else
|
||||
cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at));
|
||||
dest_words[word] = *map_ptr;
|
||||
current_read_mapping = UINT32_MAX;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -372,7 +512,6 @@ esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool a
|
||||
|
||||
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
|
||||
{
|
||||
esp_err_t err;
|
||||
size_t alignment = write_encrypted ? 32 : 4;
|
||||
if ((dest_addr % alignment) != 0) {
|
||||
ESP_EARLY_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment);
|
||||
@@ -387,16 +526,29 @@ esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = bootloader_flash_unlock();
|
||||
esp_err_t err = bootloader_flash_unlock();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_rom_spiflash_result_t rc = ESP_ROM_SPIFLASH_RESULT_OK;
|
||||
|
||||
if (write_encrypted && !ENCRYPTION_IS_VIRTUAL) {
|
||||
return spi_to_esp_err(esp_rom_spiflash_write_encrypted(dest_addr, src, size));
|
||||
rc = esp_rom_spiflash_write_encrypted(dest_addr, src, size);
|
||||
} else {
|
||||
return spi_to_esp_err(esp_rom_spiflash_write(dest_addr, src, size));
|
||||
rc = esp_rom_spiflash_write(dest_addr, src, size);
|
||||
}
|
||||
/* NOTE: [ESP-TEE] Cache flushing after flash writes/erases
|
||||
*
|
||||
* After writing or erasing the flash, we need to flush the cache at locations
|
||||
* corresponding to the destination write/erase address. This prevents stale data
|
||||
* from being read from already memory-mapped addresses that were modified.
|
||||
*/
|
||||
#if ESP_TEE_BUILD
|
||||
spi_flash_check_and_flush_cache(dest_addr, size);
|
||||
#endif
|
||||
|
||||
return spi_to_esp_err(rc);
|
||||
}
|
||||
|
||||
esp_err_t bootloader_flash_erase_sector(size_t sector)
|
||||
@@ -426,6 +578,10 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
|
||||
++sector;
|
||||
}
|
||||
}
|
||||
#if ESP_TEE_BUILD
|
||||
spi_flash_check_and_flush_cache(start_addr, size);
|
||||
#endif
|
||||
|
||||
return spi_to_esp_err(rc);
|
||||
}
|
||||
|
||||
@@ -480,7 +636,7 @@ void bootloader_flash_32bits_address_map_enable(esp_rom_spiflash_read_mode_t fla
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // BOOTLOADER_BUILD
|
||||
#endif // NON_OS_BUILD
|
||||
|
||||
|
||||
FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
|
||||
@@ -671,7 +827,7 @@ void bootloader_spi_flash_reset(void)
|
||||
#define XMC_SUPPORT CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT
|
||||
#define XMC_VENDOR_ID_1 0x20
|
||||
|
||||
#if BOOTLOADER_BUILD
|
||||
#if NON_OS_BUILD
|
||||
#define BOOTLOADER_FLASH_LOG(level, ...) ESP_EARLY_LOG##level(TAG, ##__VA_ARGS__)
|
||||
#else
|
||||
static DRAM_ATTR char bootloader_flash_tag[] = "bootloader_flash";
|
||||
|
@@ -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
|
@@ -21,6 +21,8 @@ extern "C" {
|
||||
#define PART_SUBTYPE_OTA_FLAG 0x10
|
||||
#define PART_SUBTYPE_OTA_MASK 0x0f
|
||||
#define PART_SUBTYPE_TEST 0x20
|
||||
#define PART_SUBTYPE_TEE_0 0x30
|
||||
#define PART_SUBTYPE_TEE_1 0x31
|
||||
|
||||
#define PART_TYPE_DATA 0x01
|
||||
#define PART_SUBTYPE_DATA_OTA 0x00
|
||||
@@ -38,6 +40,9 @@ extern "C" {
|
||||
#define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00
|
||||
#define PART_SUBTYPE_PARTITION_TABLE_OTA 0x01
|
||||
|
||||
#define PART_SUBTYPE_DATA_TEE_OTA 0x90
|
||||
#define PART_SUBTYPE_DATA_TEE_SEC_STORAGE 0x91
|
||||
|
||||
#define PART_TYPE_END 0xff
|
||||
#define PART_SUBTYPE_END 0xff
|
||||
|
||||
|
45
components/bootloader_support/include/esp_tee_ota_utils.h
Normal file
45
components/bootloader_support/include/esp_tee_ota_utils.h
Normal 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
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -21,11 +21,16 @@ extern "C"
|
||||
#define SPI_ERROR_LOG "spi flash error"
|
||||
|
||||
#define MAX_OTA_SLOTS 16
|
||||
#define MAX_TEE_OTA_SLOTS 2
|
||||
|
||||
typedef struct {
|
||||
esp_partition_pos_t ota_info;
|
||||
esp_partition_pos_t factory;
|
||||
esp_partition_pos_t test;
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
esp_partition_pos_t tee_ota_info;
|
||||
esp_partition_pos_t tee[MAX_TEE_OTA_SLOTS];
|
||||
#endif
|
||||
esp_partition_pos_t ota[MAX_OTA_SLOTS];
|
||||
uint32_t app_count;
|
||||
uint32_t selected_subtype;
|
||||
|
@@ -38,6 +38,13 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs);
|
||||
*/
|
||||
int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs);
|
||||
|
||||
/**
|
||||
* @brief Load and verify the TEE image from the selected partition
|
||||
*
|
||||
* @param bs Bootloader state structure
|
||||
*/
|
||||
void bootloader_utility_load_tee_image(const bootloader_state_t *bs);
|
||||
|
||||
/**
|
||||
* @brief Load the selected partition and start application.
|
||||
*
|
||||
|
@@ -52,6 +52,10 @@
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_fault.h"
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
#include "bootloader_utility_tee.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "boot";
|
||||
|
||||
/* Reduce literal size for some generic string literals */
|
||||
@@ -69,6 +73,21 @@ static void set_cache_and_start_app(uint32_t drom_addr,
|
||||
uint32_t irom_size,
|
||||
const esp_image_metadata_t *data);
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
/* NOTE: Required by other sources for secure boot routine */
|
||||
esp_image_metadata_t tee_data;
|
||||
static uint8_t tee_boot_part = UINT8_MAX;
|
||||
|
||||
static void unpack_load_tee_app(const esp_image_metadata_t *data);
|
||||
static void set_cache_and_load_tee_app(uint32_t drom_addr,
|
||||
uint32_t drom_load_addr,
|
||||
uint32_t drom_size,
|
||||
uint32_t irom_addr,
|
||||
uint32_t irom_load_addr,
|
||||
uint32_t irom_size,
|
||||
const esp_image_metadata_t *data);
|
||||
#endif
|
||||
|
||||
esp_err_t bootloader_common_read_otadata(const esp_partition_pos_t *ota_info, esp_ota_select_entry_t *two_otadata)
|
||||
{
|
||||
const esp_ota_select_entry_t *ota_select_map;
|
||||
@@ -162,6 +181,13 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs)
|
||||
bs->test = partition->pos;
|
||||
partition_usage = "test app";
|
||||
break;
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
case PART_SUBTYPE_TEE_0: /* TEE binary */
|
||||
case PART_SUBTYPE_TEE_1:
|
||||
bs->tee[partition->subtype & 0x01] = partition->pos;
|
||||
partition_usage = "TEE app";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* OTA binary */
|
||||
if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) {
|
||||
@@ -195,6 +221,15 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs)
|
||||
esp_efuse_init_virtual_mode_in_flash(partition->pos.offset, partition->pos.size);
|
||||
#endif
|
||||
break;
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
case PART_SUBTYPE_DATA_TEE_OTA: /* TEE ota data */
|
||||
bs->tee_ota_info = partition->pos;
|
||||
partition_usage = "TEE OTA data";
|
||||
break;
|
||||
case PART_SUBTYPE_DATA_TEE_SEC_STORAGE: /* TEE secure storage */
|
||||
partition_usage = "TEE secure storage";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
partition_usage = "Unknown data";
|
||||
break;
|
||||
@@ -518,6 +553,29 @@ void bootloader_utility_load_boot_image_from_deep_sleep(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
void bootloader_utility_load_tee_image(const bootloader_state_t *bs)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
uint8_t tee_active_part = bootloader_utility_tee_get_boot_partition(&bs->tee_ota_info);
|
||||
if (tee_active_part != PART_SUBTYPE_TEE_0 && tee_active_part != PART_SUBTYPE_TEE_1) {
|
||||
ESP_LOGE(TAG, "Failed to find valid TEE app");
|
||||
bootloader_reset();
|
||||
}
|
||||
|
||||
uint8_t tee_part_idx = tee_active_part & 0x01;
|
||||
const esp_partition_pos_t *tee_active_part_pos = &bs->tee[tee_part_idx];
|
||||
err = bootloader_load_image(tee_active_part_pos, &tee_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to load TEE app");
|
||||
bootloader_reset();
|
||||
}
|
||||
tee_boot_part = tee_part_idx;
|
||||
|
||||
ESP_LOGI(TAG, "Loaded TEE app from partition at offset 0x%"PRIx32, tee_active_part_pos->offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TRY_LOG_FORMAT "Trying partition index %d offs 0x%"PRIx32" size 0x%"PRIx32
|
||||
|
||||
void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index)
|
||||
@@ -856,6 +914,127 @@ static bool s_flash_seg_needs_map(uint32_t vaddr)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* TODO: [IDF-11689] Unify the TEE-specific app loading implementation with
|
||||
* the existing app loading implementation.
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
static void unpack_load_tee_app(const esp_image_metadata_t *data)
|
||||
{
|
||||
/**
|
||||
* note:
|
||||
* On chips with shared D/I external vaddr, we don't divide them into either D or I,
|
||||
* as essentially they are the same.
|
||||
* We integrate all the hardware difference into this `unpack_load_app` function.
|
||||
*/
|
||||
uint32_t rom_addr[2] = {};
|
||||
uint32_t rom_load_addr[2] = {};
|
||||
uint32_t rom_size[2] = {};
|
||||
int rom_index = 0; //shall not exceed 2
|
||||
|
||||
// Find DROM & IROM addresses, to configure MMU mappings
|
||||
for (int i = 0; i < data->image.segment_count; i++) {
|
||||
const esp_image_segment_header_t *header = &data->segments[i];
|
||||
const uint32_t addr = header->load_addr;
|
||||
|
||||
//`SOC_DROM_LOW` and `SOC_DROM_HIGH` are the same as `SOC_IROM_LOW` and `SOC_IROM_HIGH`, reasons are in above `note`
|
||||
if ((addr >= SOC_DROM_LOW && addr < SOC_DROM_HIGH)
|
||||
#if SOC_MMU_PER_EXT_MEM_TARGET
|
||||
|| (addr >= SOC_EXTRAM_LOW && addr < SOC_EXTRAM_HIGH)
|
||||
#endif
|
||||
) {
|
||||
/**
|
||||
* D/I are shared, but there should not be a third segment on flash/psram
|
||||
*/
|
||||
assert(rom_index < 2);
|
||||
rom_addr[rom_index] = data->segment_data[i];
|
||||
rom_load_addr[rom_index] = header->load_addr;
|
||||
rom_size[rom_index] = header->data_len;
|
||||
rom_index++;
|
||||
}
|
||||
}
|
||||
assert(rom_index == 2);
|
||||
|
||||
ESP_EARLY_LOGD(TAG, "calling set_cache_and_start_tee_app");
|
||||
set_cache_and_load_tee_app(rom_addr[0],
|
||||
rom_load_addr[0],
|
||||
rom_size[0],
|
||||
rom_addr[1],
|
||||
rom_load_addr[1],
|
||||
rom_size[1],
|
||||
data);
|
||||
}
|
||||
|
||||
static void set_cache_and_load_tee_app(
|
||||
uint32_t drom_addr,
|
||||
uint32_t drom_load_addr,
|
||||
uint32_t drom_size,
|
||||
uint32_t irom_addr,
|
||||
uint32_t irom_load_addr,
|
||||
uint32_t irom_size,
|
||||
const esp_image_metadata_t *data)
|
||||
{
|
||||
uint32_t drom_load_addr_aligned = 0, drom_addr_aligned = 0;
|
||||
uint32_t irom_load_addr_aligned = 0, irom_addr_aligned = 0;
|
||||
uint32_t actual_mapped_len = 0;
|
||||
|
||||
const uint32_t mmu_page_size = data->mmu_page_size;
|
||||
#if SOC_MMU_PAGE_SIZE_CONFIGURABLE
|
||||
// re-configure MMU page size
|
||||
mmu_ll_set_page_size(0, mmu_page_size);
|
||||
#endif //SOC_MMU_PAGE_SIZE_CONFIGURABLE
|
||||
|
||||
if (drom_addr != 0) {
|
||||
drom_load_addr_aligned = drom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
|
||||
drom_addr_aligned = drom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
|
||||
ESP_EARLY_LOGV(TAG, "TEE rodata starts from paddr=0x%08x, vaddr=0x%08x, size=0x%x", drom_addr, drom_load_addr, drom_size);
|
||||
|
||||
//The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled.
|
||||
if (s_flash_seg_needs_map(drom_load_addr_aligned)) {
|
||||
mmu_hal_map_region(0, MMU_TARGET_FLASH0, drom_load_addr_aligned, drom_addr_aligned, drom_size, &actual_mapped_len);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping rodata, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, drom_load_addr_aligned, actual_mapped_len);
|
||||
}
|
||||
//we use the MMU_LL_END_DROM_ENTRY_ID mmu entry as a map page for app to find the boot partition
|
||||
mmu_hal_map_region(0, MMU_TARGET_FLASH0, MMU_DROM_END_ENTRY_VADDR_FROM_VAL(mmu_page_size), drom_addr_aligned, mmu_page_size, &actual_mapped_len);
|
||||
ESP_EARLY_LOGV(TAG, "mapped one page of the rodata, from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", drom_addr_aligned, drom_load_addr_aligned, actual_mapped_len);
|
||||
}
|
||||
|
||||
if (irom_addr != 0) {
|
||||
irom_load_addr_aligned = irom_load_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
|
||||
irom_addr_aligned = irom_addr & MMU_FLASH_MASK_FROM_VAL(mmu_page_size);
|
||||
ESP_EARLY_LOGV(TAG, "TEE text starts from paddr=0x%08x, vaddr=0x%08x, size=0x%x", irom_addr, irom_load_addr, irom_size);
|
||||
//The addr is aligned, so we add the mask off length to the size, to make sure the corresponding buses are enabled.
|
||||
irom_size = (irom_load_addr - irom_load_addr_aligned) + irom_size;
|
||||
|
||||
if (s_flash_seg_needs_map(irom_load_addr_aligned)) {
|
||||
mmu_hal_map_region(0, MMU_TARGET_FLASH0, irom_load_addr_aligned, irom_addr_aligned, irom_size, &actual_mapped_len);
|
||||
ESP_EARLY_LOGV(TAG, "after mapping text, starting from paddr=0x%08" PRIx32 " and vaddr=0x%08" PRIx32 ", 0x%" PRIx32 " bytes are mapped", irom_addr_aligned, irom_load_addr_aligned, actual_mapped_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (drom_load_addr_aligned != 0) {
|
||||
cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, drom_load_addr_aligned, drom_size);
|
||||
cache_ll_l1_enable_bus(0, bus_mask);
|
||||
}
|
||||
|
||||
if (irom_load_addr_aligned != 0) {
|
||||
cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, irom_load_addr_aligned, irom_size);
|
||||
cache_ll_l1_enable_bus(0, bus_mask);
|
||||
}
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
if (drom_load_addr_aligned != 0) {
|
||||
cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(1, drom_load_addr_aligned, drom_size);
|
||||
cache_ll_l1_enable_bus(1, bus_mask);
|
||||
}
|
||||
|
||||
if (irom_load_addr_aligned != 0) {
|
||||
cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(1, irom_load_addr_aligned, irom_size);
|
||||
cache_ll_l1_enable_bus(1, bus_mask);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // CONFIG_SECURE_ENABLE_TEE
|
||||
|
||||
static void set_cache_and_start_app(
|
||||
uint32_t drom_addr,
|
||||
uint32_t drom_load_addr,
|
||||
@@ -943,6 +1122,11 @@ static void set_cache_and_start_app(
|
||||
cache_ll_l1_enable_bus(1, bus_mask);
|
||||
#endif
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
//----------------------Unpacking and loading the TEE app----------------
|
||||
unpack_load_tee_app(&tee_data);
|
||||
#endif
|
||||
|
||||
//----------------------Enable Cache----------------
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// Application will need to do Cache_Flush(1) and Cache_Read_Enable(1)
|
||||
@@ -953,12 +1137,26 @@ static void set_cache_and_start_app(
|
||||
|
||||
ESP_LOGD(TAG, "start: 0x%08"PRIx32, entry_addr);
|
||||
bootloader_atexit();
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
ESP_LOGI(TAG, "Current privilege level - %d", esp_cpu_get_curr_privilege_level());
|
||||
/* NOTE: TEE Initialization and REE Switch
|
||||
* This call will not return back. After TEE initialization,
|
||||
* it will switch to the REE and execute the user application.
|
||||
*/
|
||||
typedef void (*esp_tee_init_t)(uint32_t, uint32_t, uint8_t) __attribute__((noreturn));
|
||||
esp_tee_init_t esp_tee_init = ((esp_tee_init_t) tee_data.image.entry_addr);
|
||||
|
||||
ESP_LOGI(TAG, "Starting TEE: Entry point - 0x%"PRIx32, (uint32_t)esp_tee_init);
|
||||
(*esp_tee_init)(entry_addr, drom_addr, tee_boot_part);
|
||||
#else
|
||||
typedef void (*entry_t)(void) __attribute__((noreturn));
|
||||
entry_t entry = ((entry_t) entry_addr);
|
||||
|
||||
// TODO: we have used quite a bit of stack at this point.
|
||||
// use "movsp" instruction to reset stack back to where ROM stack starts.
|
||||
(*entry)();
|
||||
#endif
|
||||
}
|
||||
|
||||
void bootloader_reset(void)
|
||||
|
260
components/bootloader_support/src/bootloader_utility_tee.c
Normal file
260
components/bootloader_support/src/bootloader_utility_tee.c
Normal 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);
|
||||
}
|
@@ -23,6 +23,10 @@
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
extern esp_image_metadata_t tee_data;
|
||||
#endif
|
||||
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
static const char *TAG = "secure_boot_v2";
|
||||
|
||||
@@ -268,6 +272,28 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t
|
||||
ESP_LOGW(TAG, "App has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", app_key_digests.num_digests, boot_key_digests.num_digests);
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
/* Generate the TEE public key digests */
|
||||
bool tee_match = false;
|
||||
esp_image_sig_public_key_digests_t tee_key_digests = {0};
|
||||
|
||||
ret = s_calculate_image_public_key_digests(tee_data.start_addr, tee_data.image_len - SIG_BLOCK_PADDING, &tee_key_digests);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "TEE signature block is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tee_key_digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No valid TEE signature blocks found.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%d signature block(s) found appended to the tee.", tee_key_digests.num_digests);
|
||||
if (tee_key_digests.num_digests > boot_key_digests.num_digests) {
|
||||
ESP_LOGW(TAG, "TEE has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", tee_key_digests.num_digests, boot_key_digests.num_digests);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Confirm if at least one public key from the application matches a public key in the bootloader
|
||||
(Also, ensure if that public revoke bit is not set for the matched key) */
|
||||
bool match = false;
|
||||
@@ -285,6 +311,18 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < tee_key_digests.num_digests; j++) {
|
||||
if (!memcmp(boot_key_digests.key_digests[i], tee_key_digests.key_digests[j], ESP_SECURE_BOOT_DIGEST_LEN)) {
|
||||
ESP_LOGI(TAG, "TEE key(%d) matches with bootloader key(%d).", j, i);
|
||||
tee_match = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (match == false) {
|
||||
@@ -292,6 +330,13 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
if (tee_match == false) {
|
||||
ESP_LOGE(TAG, "No TEE key digest matches the bootloader key digest.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
/* Revoke the empty signature blocks */
|
||||
if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) {
|
||||
|
@@ -13,7 +13,7 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
// startup_internal.h is necessary for startup function definition, which does not exist on Linux (TODO: IDF-9950)
|
||||
#if !CONFIG_IDF_TARGET_LINUX
|
||||
#if !CONFIG_IDF_TARGET_LINUX && !ESP_TEE_BUILD
|
||||
#include "esp_private/startup_internal.h"
|
||||
|
||||
static const char *TAG = "app_init";
|
||||
@@ -118,7 +118,7 @@ int esp_app_get_elf_sha256(char* dst, size_t size)
|
||||
|
||||
// startup function definition and execution does not exist on the Linux target
|
||||
// (TODO: IDF-9950)
|
||||
#if !CONFIG_IDF_TARGET_LINUX
|
||||
#if !CONFIG_IDF_TARGET_LINUX && !ESP_TEE_BUILD
|
||||
ESP_SYSTEM_INIT_FN(init_show_app_info, CORE, BIT(0), 20)
|
||||
{
|
||||
// Load the current ELF SHA256
|
||||
|
@@ -145,6 +145,9 @@ if(NOT non_os_build)
|
||||
list(APPEND srcs "esp_clock_output.c")
|
||||
endif()
|
||||
else()
|
||||
if(ESP_TEE_BUILD)
|
||||
list(APPEND srcs "esp_clk.c" "hw_random.c")
|
||||
endif()
|
||||
# Requires "_esp_error_check_failed()" function
|
||||
list(APPEND priv_requires "esp_system")
|
||||
endif()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -9,7 +9,10 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/lock.h>
|
||||
|
||||
#if !NON_OS_BUILD
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#endif
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/soc_caps.h"
|
||||
@@ -52,7 +55,11 @@
|
||||
// g_ticks_us defined in ROMs for PRO and APP CPU
|
||||
extern uint32_t g_ticks_per_us_pro;
|
||||
|
||||
// Any code utilizing locks, which depend on FreeRTOS, should be omitted
|
||||
// when building for Non-OS environments
|
||||
#if !NON_OS_BUILD
|
||||
static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
#endif
|
||||
|
||||
#if SOC_RTC_MEM_SUPPORTED
|
||||
typedef struct {
|
||||
@@ -64,6 +71,7 @@ typedef struct {
|
||||
_Static_assert(sizeof(retain_mem_t) == 24, "retain_mem_t must be 24 bytes");
|
||||
_Static_assert(offsetof(retain_mem_t, checksum) == sizeof(retain_mem_t) - sizeof(uint32_t), "Wrong offset for checksum field in retain_mem_t structure");
|
||||
|
||||
#if !NON_OS_BUILD
|
||||
static __attribute__((section(".rtc_timer_data_in_rtc_mem"))) retain_mem_t s_rtc_timer_retain_mem;
|
||||
|
||||
static uint32_t calc_checksum(void)
|
||||
@@ -77,6 +85,7 @@ static uint32_t calc_checksum(void)
|
||||
return checksum;
|
||||
}
|
||||
#define IS_RETAIN_MEM_VALID() (s_rtc_timer_retain_mem.checksum == calc_checksum())
|
||||
#endif // NON_OS_BUILD
|
||||
#endif // SOC_RTC_MEM_SUPPORTED
|
||||
|
||||
inline static int IRAM_ATTR s_get_cpu_freq_mhz(void)
|
||||
@@ -108,6 +117,7 @@ int IRAM_ATTR esp_clk_xtal_freq(void)
|
||||
return rtc_clk_xtal_freq_get() * MHZ;
|
||||
}
|
||||
|
||||
#if !NON_OS_BUILD
|
||||
uint64_t esp_rtc_get_time_us(void)
|
||||
{
|
||||
portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
|
||||
@@ -161,6 +171,7 @@ uint64_t esp_rtc_get_time_us(void)
|
||||
return esp_rtc_time_us;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp_clk_slowclk_cal_set(uint32_t new_cal)
|
||||
{
|
||||
@@ -214,6 +225,7 @@ uint64_t esp_clk_rtc_time(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !NON_OS_BUILD
|
||||
void esp_clk_private_lock(void)
|
||||
{
|
||||
portENTER_CRITICAL(&s_esp_rtc_time_lock);
|
||||
@@ -223,3 +235,4 @@ void esp_clk_private_unlock(void)
|
||||
{
|
||||
portEXIT_CRITICAL(&s_esp_rtc_time_lock);
|
||||
}
|
||||
#endif
|
||||
|
@@ -13,9 +13,12 @@
|
||||
#include "esp_cpu.h"
|
||||
#include "soc/wdev_reg.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_private/startup_internal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if !ESP_TEE_BUILD
|
||||
#include "esp_private/startup_internal.h"
|
||||
#endif
|
||||
|
||||
#if SOC_LP_TIMER_SUPPORTED
|
||||
#include "hal/lp_timer_hal.h"
|
||||
#endif
|
||||
@@ -100,7 +103,7 @@ void esp_fill_random(void *buf, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
#if SOC_RNG_CLOCK_IS_INDEPENDENT
|
||||
#if SOC_RNG_CLOCK_IS_INDEPENDENT && !ESP_TEE_BUILD
|
||||
ESP_SYSTEM_INIT_FN(init_rng_clock, SECONDARY, BIT(0), 102)
|
||||
{
|
||||
_lp_clkrst_ll_enable_rng_clock(true);
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "xtensa_api.h"
|
||||
#include "xt_utils.h"
|
||||
#elif __riscv
|
||||
#include "riscv/csr.h"
|
||||
#include "riscv/rv_utils.h"
|
||||
#endif
|
||||
#include "esp_intr_alloc.h"
|
||||
@@ -129,6 +130,27 @@ FORCE_INLINE_ATTR __attribute__((pure)) int esp_cpu_get_core_id(void)
|
||||
return (int)rv_utils_get_core_id();
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Get the current [RISC-V] CPU core's privilege level
|
||||
*
|
||||
* This function returns the current privilege level of the CPU core executing
|
||||
* this function.
|
||||
*
|
||||
* @return The current CPU core's privilege level, -1 if not supported.
|
||||
*/
|
||||
|
||||
FORCE_INLINE_ATTR __attribute__((always_inline)) int esp_cpu_get_curr_privilege_level(void)
|
||||
{
|
||||
#ifdef __XTENSA__
|
||||
return -1;
|
||||
#else
|
||||
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2
|
||||
return PRV_M;
|
||||
#else
|
||||
return RV_READ_CSR(CSR_PRV_MODE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the current stack pointer address
|
||||
@@ -229,7 +251,7 @@ FORCE_INLINE_ATTR void esp_cpu_intr_set_ivt_addr(const void *ivt_addr)
|
||||
#ifdef __XTENSA__
|
||||
xt_utils_set_vecbase((uint32_t)ivt_addr);
|
||||
#else
|
||||
rv_utils_set_mtvec((uint32_t)ivt_addr);
|
||||
rv_utils_set_xtvec((uint32_t)ivt_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -429,9 +451,14 @@ FORCE_INLINE_ATTR void esp_cpu_intr_edge_ack(int intr_num)
|
||||
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
|
||||
#ifdef __XTENSA__
|
||||
xthal_set_intclear((unsigned) (1 << intr_num));
|
||||
#else
|
||||
#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD
|
||||
extern esprv_int_mgmt_t esp_tee_intr_sec_srv_cb;
|
||||
esp_tee_intr_sec_srv_cb(2, TEE_INTR_EDGE_ACK_SRV_ID, intr_num);
|
||||
#else
|
||||
rv_utils_intr_edge_ack((unsigned) intr_num);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- Memory Ports -----------------------------------------------------
|
||||
|
@@ -61,9 +61,9 @@ extern "C" {
|
||||
#define regi2c_write_reg_mask_raw esp_rom_regi2c_write_mask
|
||||
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
#if NON_OS_BUILD
|
||||
/**
|
||||
* If compiling for the bootloader, ROM functions can be called directly,
|
||||
* If compiling for the non-FreeRTOS builds (e.g. bootloader), ROM functions can be called directly,
|
||||
* without the need of a lock.
|
||||
*/
|
||||
#define regi2c_ctrl_read_reg regi2c_read_reg_raw
|
||||
@@ -83,7 +83,7 @@ void regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add,
|
||||
void regi2c_enter_critical(void);
|
||||
void regi2c_exit_critical(void);
|
||||
|
||||
#endif // BOOTLOADER_BUILD
|
||||
#endif // NON_OS_BUILD
|
||||
|
||||
/* Convenience macros for the above functions, these use register definitions
|
||||
* from regi2c_xxx.h header files.
|
||||
|
@@ -649,9 +649,13 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
}
|
||||
#endif
|
||||
|
||||
/* NOTE: ESP-TEE is responsible for all interrupt-related configurations
|
||||
* when enabled. The following code is not applicable in that case */
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
#if SOC_INT_PLIC_SUPPORTED
|
||||
/* Make sure the interrupt is not delegated to user mode (IDF uses machine mode only) */
|
||||
RV_CLEAR_CSR(mideleg, BIT(intr));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
|
@@ -1,4 +1,6 @@
|
||||
if(BOOTLOADER_BUILD)
|
||||
idf_build_get_property(non_os_build NON_OS_BUILD)
|
||||
|
||||
if(non_os_build)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
idf_build_get_property(non_os_build NON_OS_BUILD)
|
||||
|
||||
set(srcs "rtc_clk_init.c"
|
||||
"rtc_clk.c"
|
||||
"pmu_param.c"
|
||||
@@ -8,7 +10,7 @@ set(srcs "rtc_clk_init.c"
|
||||
"ocode_init.c"
|
||||
)
|
||||
|
||||
if(NOT BOOTLOADER_BUILD)
|
||||
if(NOT non_os_build)
|
||||
list(APPEND srcs "sar_periph_ctrl.c")
|
||||
|
||||
endif()
|
||||
|
@@ -62,6 +62,10 @@ static void esp_cpu_configure_invalid_regions(void)
|
||||
// 7. End of address space
|
||||
PMA_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE);
|
||||
PMA_ENTRY_SET_TOR(12, UINT32_MAX, PMA_TOR | PMA_NONE);
|
||||
|
||||
PMA_ENTRY_CFG_RESET(13);
|
||||
PMA_ENTRY_CFG_RESET(14);
|
||||
PMA_ENTRY_CFG_RESET(15);
|
||||
}
|
||||
|
||||
void esp_cpu_configure_region_protection(void)
|
||||
@@ -112,6 +116,14 @@ void esp_cpu_configure_region_protection(void)
|
||||
//
|
||||
esp_cpu_configure_invalid_regions();
|
||||
|
||||
/* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader
|
||||
* to prevent errors before TEE initialization. TEE will handle all other
|
||||
* memory protection.
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD
|
||||
return;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Configure all the valid address regions using PMP
|
||||
//
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_riscv_intr.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_desc_ret)
|
||||
{
|
||||
@@ -16,7 +17,17 @@ void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_
|
||||
* Interrupts 3, 4 and 7 are unavailable for PULP CPU as they are bound to Core-Local Interrupts (CLINT)
|
||||
*/
|
||||
// [TODO: IDF-2465]
|
||||
const uint32_t rsvd_mask = BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(7);
|
||||
const uint32_t base_rsvd_mask = BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(7);
|
||||
|
||||
/* On the ESP32-C6, interrupt 14 is reserved for ESP-TEE
|
||||
* for operations related to secure peripherals under its control
|
||||
* (e.g. AES, SHA, APM)
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
const uint32_t rsvd_mask = base_rsvd_mask | BIT(14);
|
||||
#else
|
||||
const uint32_t rsvd_mask = base_rsvd_mask;
|
||||
#endif
|
||||
|
||||
intr_desc_ret->priority = 1;
|
||||
intr_desc_ret->type = ESP_CPU_INTR_TYPE_NA;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,15 +10,40 @@
|
||||
#include "../ext_mem_layout.h"
|
||||
#include "hal/mmu_types.h"
|
||||
|
||||
/* NOTE: With ESP-TEE enabled:
|
||||
* - The start address is moved by the size of TEE IDROM segments since these
|
||||
* segments are placed at the start of the linear address space
|
||||
* - TEE IROM and DROM segments are both 64KB (CONFIG_SECURE_TEE_IROM_SIZE,
|
||||
* CONFIG_SECURE_TEE_DROM_SIZE) for now. Thus, the number of reserved entries
|
||||
* from the start would be (64KB + 64KB)/MMU_PAGE_SIZE
|
||||
* - The last few MMU entries are reserved for TEE flash operations. The number
|
||||
* of reserved entries matches the size of TEE IDROM segments (IROM + DROM)
|
||||
* plus one additional entry, i.e. (64KB + 64KB)/MMU_PAGE_SIZE + 1
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
#define TEE_MMU_MEM_REG_START_OFFS (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
|
||||
#define TEE_MMU_RESV_PAGES ((CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE) / CONFIG_MMU_PAGE_SIZE)
|
||||
#define TEE_MMU_MEM_REG_END_OFFS ((TEE_MMU_RESV_PAGES + 1) * CONFIG_MMU_PAGE_SIZE)
|
||||
|
||||
#define MMU_MEM_REG_START_ADDR_W_TEE (SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW + TEE_MMU_MEM_REG_START_OFFS)
|
||||
#define MMU_MEM_REG_END_ADDR_W_TEE (SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH - TEE_MMU_MEM_REG_END_OFFS)
|
||||
|
||||
#define MMU_IRAM0_LINEAR_ADDRESS_LOW MMU_MEM_REG_START_ADDR_W_TEE
|
||||
#define MMU_IRAM0_LINEAR_ADDRESS_HIGH MMU_MEM_REG_END_ADDR_W_TEE
|
||||
#else
|
||||
#define MMU_IRAM0_LINEAR_ADDRESS_LOW SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW
|
||||
#define MMU_IRAM0_LINEAR_ADDRESS_HIGH SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The start addresses in this list should always be sorted from low to high, as MMU driver will need to
|
||||
* coalesce adjacent regions
|
||||
*/
|
||||
const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {
|
||||
[0] = {
|
||||
.start = SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW,
|
||||
.end = SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH,
|
||||
.size = SOC_BUS_SIZE(SOC_MMU_IRAM0_LINEAR),
|
||||
.start = MMU_IRAM0_LINEAR_ADDRESS_LOW,
|
||||
.end = MMU_IRAM0_LINEAR_ADDRESS_HIGH,
|
||||
.size = MMU_IRAM0_LINEAR_ADDRESS_HIGH - MMU_IRAM0_LINEAR_ADDRESS_LOW,
|
||||
.bus_id = CACHE_BUS_IBUS0 | CACHE_BUS_DBUS0,
|
||||
.targets = MMU_TARGET_FLASH0,
|
||||
.caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT,
|
||||
|
@@ -95,6 +95,11 @@ typedef enum {
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition
|
||||
ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_APP_TEE_MIN = 0x30, //!< Base for TEE partition subtypes
|
||||
ESP_PARTITION_SUBTYPE_APP_TEE_0 = ESP_PARTITION_SUBTYPE_APP_TEE_MIN + 0, //!< TEE partition 0
|
||||
ESP_PARTITION_SUBTYPE_APP_TEE_1 = ESP_PARTITION_SUBTYPE_APP_TEE_MIN + 1, //!< TEE partition 1
|
||||
ESP_PARTITION_SUBTYPE_APP_TEE_MAX = ESP_PARTITION_SUBTYPE_APP_TEE_1, //!< Max subtype of TEE partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition
|
||||
@@ -108,6 +113,9 @@ typedef enum {
|
||||
ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_LITTLEFS = 0x83, //!< LITTLEFS partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_DATA_TEE_OTA = 0x90, //!< TEE OTA selection partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE= 0x91, //!< TEE secure storage partition
|
||||
|
||||
#if __has_include("extra_partition_subtypes.inc")
|
||||
#include "extra_partition_subtypes.inc"
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "esp_private/sar_periph_ctrl.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#include "hal/efuse_hal.h"
|
||||
|
||||
/*
|
||||
* This file is used to override the hooks provided by the PHY lib for some system features.
|
||||
@@ -99,3 +100,20 @@ int16_t phy_get_tsens_value(void)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* NOTE:: With ESP-TEE enabled, we override certain functions from the libphy
|
||||
* component archive which directly access the eFuse later (e.g. REG_READ)
|
||||
* with the HAL APIs.
|
||||
*
|
||||
* In the future, ESP-TEE would need to protect the entire eFuse range through
|
||||
* APM and expects users to use HAL APIs which would be redirected as service calls.
|
||||
*/
|
||||
void esp_phy_efuse_get_mac(uint8_t *mac)
|
||||
{
|
||||
efuse_hal_get_mac(mac);
|
||||
}
|
||||
|
||||
uint32_t esp_phy_efuse_get_chip_ver_pkg(void)
|
||||
{
|
||||
return efuse_hal_get_chip_ver_pkg();
|
||||
}
|
||||
|
@@ -126,6 +126,12 @@ if(CONFIG_ESP_ROM_HAS_VERSION)
|
||||
rom_linker_script("version")
|
||||
endif()
|
||||
|
||||
if(ESP_TEE_BUILD)
|
||||
if(target STREQUAL "esp32c6")
|
||||
rom_linker_script("spiflash")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BOOTLOADER_BUILD)
|
||||
if(target STREQUAL "esp32")
|
||||
if(NOT CONFIG_SPI_FLASH_ROM_DRIVER_PATCH)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -259,11 +259,11 @@ gpio_bypass_matrix_in = 0x40000714;
|
||||
***************************************/
|
||||
|
||||
/* Functions */
|
||||
esprv_intc_int_set_priority = 0x40000718;
|
||||
esprv_intc_int_set_threshold = 0x4000071c;
|
||||
esprv_intc_int_enable = 0x40000720;
|
||||
esprv_intc_int_disable = 0x40000724;
|
||||
esprv_intc_int_set_type = 0x40000728;
|
||||
PROVIDE( esprv_intc_int_set_priority = 0x40000718 );
|
||||
PROVIDE( esprv_intc_int_set_threshold = 0x4000071c );
|
||||
PROVIDE( esprv_intc_int_enable = 0x40000720 );
|
||||
PROVIDE( esprv_intc_int_disable = 0x40000724 );
|
||||
PROVIDE( esprv_intc_int_set_type = 0x40000728 );
|
||||
PROVIDE( intr_handler_set = 0x4000072c );
|
||||
intr_matrix_set = 0x40000730;
|
||||
ets_intr_lock = 0x40000734;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
|
||||
# On Linux, we only support a few features, hence this simple component registration
|
||||
if(${target} STREQUAL "linux")
|
||||
@@ -22,7 +23,7 @@ if(CONFIG_IDF_ENV_FPGA OR CONFIG_ESP_BRINGUP_BYPASS_RANDOM_SETTING)
|
||||
list(APPEND srcs "fpga_overrides_rng.c")
|
||||
endif()
|
||||
|
||||
if(BOOTLOADER_BUILD)
|
||||
if(BOOTLOADER_BUILD OR esp_tee_build)
|
||||
# "_esp_error_check_failed()" requires spi_flash module
|
||||
# Bootloader relies on some Kconfig options defined in esp_system.
|
||||
idf_component_register(SRCS "${srcs}" REQUIRES spi_flash)
|
||||
|
@@ -121,7 +121,7 @@ menu "ESP System Settings"
|
||||
|
||||
config ESP_SYSTEM_PMP_IDRAM_SPLIT
|
||||
bool "Enable IRAM/DRAM split protection"
|
||||
depends on SOC_CPU_IDRAM_SPLIT_USING_PMP
|
||||
depends on SOC_CPU_IDRAM_SPLIT_USING_PMP && !SECURE_ENABLE_TEE
|
||||
default "y"
|
||||
help
|
||||
If enabled, the CPU watches all the memory access and raises an exception in case
|
||||
@@ -141,6 +141,13 @@ menu "ESP System Settings"
|
||||
Warning: on ESP32-P4 this will also mark the memory area used for BOOTLOADER_RESERVE_RTC_MEM
|
||||
as executable. If you consider this a security risk then do not activate this option.
|
||||
|
||||
config ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE
|
||||
bool "Enable memory protection (via TEE)"
|
||||
depends on SECURE_ENABLE_TEE
|
||||
default "y"
|
||||
help
|
||||
This option enables the default memory protection provided by TEE.
|
||||
|
||||
config ESP_SYSTEM_MEMPROT_FEATURE
|
||||
bool "Enable memory protection"
|
||||
depends on SOC_MEMPROT_SUPPORTED
|
||||
@@ -592,7 +599,8 @@ menu "ESP System Settings"
|
||||
|
||||
config ESP_SYSTEM_HW_STACK_GUARD
|
||||
bool "Hardware stack guard"
|
||||
depends on SOC_ASSIST_DEBUG_SUPPORTED
|
||||
# TODO: [ESP-TEE] IDF-10770
|
||||
depends on SOC_ASSIST_DEBUG_SUPPORTED && !SECURE_ENABLE_TEE
|
||||
default y
|
||||
help
|
||||
This config allows to trigger a panic interrupt when Stack Pointer register goes out of allocated stack
|
||||
|
@@ -15,7 +15,13 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "ld.common"
|
||||
|
||||
#define SRAM_SEG_START 0x40800000
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
#define SRAM_SEG_START (0x40800000)
|
||||
#else
|
||||
#define SRAM_SEG_START (0x40800000 + CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE)
|
||||
#define FLASH_SEG_OFFSET (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
|
||||
#endif // CONFIG_SECURE_ENABLE_TEE
|
||||
|
||||
#define SRAM_SEG_END 0x4086E610 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START
|
||||
|
||||
@@ -35,8 +41,14 @@ MEMORY
|
||||
*/
|
||||
|
||||
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
/* Flash mapped instruction data */
|
||||
irom_seg (RX) : org = 0x42000020 + FLASH_SEG_OFFSET,
|
||||
len = IDRAM0_2_SEG_SIZE - FLASH_SEG_OFFSET - 0x20
|
||||
#else
|
||||
/* Flash mapped instruction data */
|
||||
irom_seg (RX) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20
|
||||
#endif
|
||||
|
||||
/**
|
||||
* (0x20 offset above is a convenience for the app binary image generation.
|
||||
@@ -54,8 +66,14 @@ MEMORY
|
||||
sram_seg (RWX) : org = SRAM_SEG_START, len = SRAM_SEG_SIZE
|
||||
|
||||
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
/* Flash mapped constant data */
|
||||
drom_seg (R) : org = 0x42000020 + FLASH_SEG_OFFSET,
|
||||
len = IDRAM0_2_SEG_SIZE - FLASH_SEG_OFFSET - 0x20
|
||||
#else
|
||||
/* Flash mapped instruction data */
|
||||
drom_seg (R) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20
|
||||
#endif
|
||||
|
||||
/* (See irom_seg for meaning of 0x20 offset in the above.) */
|
||||
#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS
|
||||
|
@@ -173,11 +173,22 @@ SECTIONS
|
||||
|
||||
/* Vectors go to start of IRAM */
|
||||
ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned");
|
||||
_vector_table_start = ABSOLUTE(.);
|
||||
KEEP(*(.exception_vectors_table.text));
|
||||
KEEP(*(.exception_vectors.text));
|
||||
|
||||
ALIGNED_SYMBOL(4, _invalid_pc_placeholder)
|
||||
|
||||
/* esp_tee_config_t structure: used to share information between the TEE and REE
|
||||
* (e.g. interrupt handler addresses, REE flash text-rodata boundaries, etc.)
|
||||
* This symbol is expected by the TEE at an offset of 0x300 from the vector table start.
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
ALIGNED_SYMBOL(0x10, _esp_tee_app_cfg)
|
||||
ASSERT(ABSOLUTE(.) == _vector_table_start + 0x2e0, "esp_tee_app_cfg must be at an offset 0x2e0 from the vector table start");
|
||||
*libesp_tee.a:(.esp_tee_app_cfg);
|
||||
#endif
|
||||
|
||||
/* Code marked as running out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
|
||||
|
@@ -419,6 +419,15 @@ void IRAM_ATTR call_start_cpu0(void)
|
||||
esp_cpu_intr_set_mtvt_addr(&_mtvt_table);
|
||||
#endif
|
||||
|
||||
/* NOTE: When ESP-TEE is enabled, this sets up the callback function
|
||||
* which redirects all the interrupt management for the REE (user app)
|
||||
* to the TEE by raising the appropriate service calls.
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
extern uint32_t esp_tee_service_call(int argc, ...);
|
||||
esprv_int_setup_mgmt_cb((void *)esp_tee_service_call);
|
||||
#endif
|
||||
|
||||
rst_reas[0] = esp_rom_get_reset_reason(0);
|
||||
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
|
||||
rst_reas[1] = esp_rom_get_reset_reason(1);
|
||||
@@ -606,8 +615,12 @@ void IRAM_ATTR call_start_cpu0(void)
|
||||
#else
|
||||
ESP_EARLY_LOGI(TAG, "Multicore app");
|
||||
#endif
|
||||
|
||||
/* NOTE: When ESP-TEE is enabled, it configures its own memory protection
|
||||
* scheme using the CPU-inherent features PMP and PMA and the APM peripheral.
|
||||
*/
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
bootloader_init_mem();
|
||||
#endif
|
||||
|
||||
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
|
||||
s_cpu_up[0] = true;
|
||||
|
@@ -63,6 +63,12 @@ ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* NOTE: When ESP-TEE is enabled, the Brownout Detection module is part
|
||||
* of the TEE and is initialized in the TEE startup routine itself.
|
||||
* It is protected from all REE accesses through memory protection mechanisms,
|
||||
* as it is a critical module for device functioning.
|
||||
*/
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104)
|
||||
{
|
||||
// [refactor-todo] leads to call chain rtc_is_register (driver) -> esp_intr_alloc (esp32/esp32s2) ->
|
||||
@@ -76,6 +82,7 @@ ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104)
|
||||
#endif // CONFIG_ESP_BROWNOUT_DET
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_SYSTEM_INIT_FN(init_newlib_time, CORE, BIT(0), 105)
|
||||
{
|
||||
|
138
components/esp_tee/CMakeLists.txt
Normal file
138
components/esp_tee/CMakeLists.txt
Normal 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()
|
144
components/esp_tee/Kconfig.projbuild
Normal file
144
components/esp_tee/Kconfig.projbuild
Normal 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
|
90
components/esp_tee/include/esp_tee.h
Normal file
90
components/esp_tee/include/esp_tee.h
Normal 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
|
37
components/esp_tee/include/private/esp_tee_app.h
Normal file
37
components/esp_tee/include/private/esp_tee_app.h
Normal 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
|
100
components/esp_tee/include/private/esp_tee_binary.h
Normal file
100
components/esp_tee/include/private/esp_tee_binary.h
Normal 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
|
52
components/esp_tee/project_include.cmake
Normal file
52
components/esp_tee/project_include.cmake
Normal 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})
|
42
components/esp_tee/scripts/esp32c6/secure_service.tbl
Normal file
42
components/esp_tee/scripts/esp32c6/secure_service.tbl
Normal 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
|
76
components/esp_tee/scripts/secure_service_hdr.py
Normal file
76
components/esp_tee/scripts/secure_service_hdr.py
Normal 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()
|
50
components/esp_tee/scripts/secure_service_tbl.py
Normal file
50
components/esp_tee/scripts/secure_service_tbl.py
Normal 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()
|
24
components/esp_tee/scripts/secure_service_wrap.py
Normal file
24
components/esp_tee/scripts/secure_service_wrap.py
Normal 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()
|
218
components/esp_tee/src/esp_secure_service_wrapper.c
Normal file
218
components/esp_tee/src/esp_secure_service_wrapper.c
Normal 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);
|
||||
}
|
76
components/esp_tee/src/esp_tee.c
Normal file
76
components/esp_tee/src/esp_tee.c
Normal 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;
|
||||
}
|
34
components/esp_tee/src/esp_tee_config.c
Normal file
34
components/esp_tee/src/esp_tee_config.c
Normal 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,
|
||||
};
|
13
components/esp_tee/src/esp_tee_u2m_switch.S
Normal file
13
components/esp_tee/src/esp_tee_u2m_switch.S
Normal 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
|
87
components/esp_tee/subproject/CMakeLists.txt
Normal file
87
components/esp_tee/subproject/CMakeLists.txt
Normal 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()
|
@@ -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})
|
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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})
|
@@ -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);
|
||||
}
|
@@ -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
|
@@ -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)
|
@@ -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;
|
||||
}
|
@@ -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);
|
@@ -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})
|
@@ -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;
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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
|
@@ -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}
|
||||
)
|
@@ -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
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
92
components/esp_tee/subproject/main/CMakeLists.txt
Normal file
92
components/esp_tee/subproject/main/CMakeLists.txt
Normal 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}\"")
|
@@ -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
|
@@ -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
|
534
components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S
Normal file
534
components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S
Normal 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
|
125
components/esp_tee/subproject/main/common/brownout.c
Normal file
125
components/esp_tee/subproject/main/common/brownout.c
Normal 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
|
||||
}
|
43
components/esp_tee/subproject/main/common/esp_app_desc_tee.c
Normal file
43
components/esp_tee/subproject/main/common/esp_app_desc_tee.c
Normal 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");
|
196
components/esp_tee/subproject/main/common/multi_heap.c
Normal file
196
components/esp_tee/subproject/main/common/multi_heap.c
Normal 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;
|
||||
}
|
123
components/esp_tee/subproject/main/common/panic/esp_tee_panic.c
Normal file
123
components/esp_tee/subproject/main/common/panic/esp_tee_panic.c
Normal 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();
|
||||
}
|
@@ -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);
|
@@ -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);
|
||||
}
|
@@ -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
|
410
components/esp_tee/subproject/main/core/esp_secure_services.c
Normal file
410
components/esp_tee/subproject/main/core/esp_secure_services.c
Normal 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
|
186
components/esp_tee/subproject/main/core/esp_tee_init.c
Normal file
186
components/esp_tee/subproject/main/core/esp_tee_init.c
Normal 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) {}
|
185
components/esp_tee/subproject/main/core/esp_tee_intr.c
Normal file
185
components/esp_tee/subproject/main/core/esp_tee_intr.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
6
components/esp_tee/subproject/main/idf_component.yml
Normal file
6
components/esp_tee/subproject/main/idf_component.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/json_generator:
|
||||
version: "^1.1.2"
|
||||
rules:
|
||||
- if: "target in [esp32c6]"
|
@@ -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
|
78
components/esp_tee/subproject/main/include/esp_tee_intr.h
Normal file
78
components/esp_tee/subproject/main/include/esp_tee_intr.h
Normal 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__
|
100
components/esp_tee/subproject/main/include/multi_heap.h
Normal file
100
components/esp_tee/subproject/main/include/multi_heap.h
Normal 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
|
243
components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in
Normal file
243
components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in
Normal 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!");
|
29
components/esp_tee/subproject/main/ld/esp_tee_ld.cmake
Normal file
29
components/esp_tee/subproject/main/ld/esp_tee_ld.cmake
Normal 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)
|
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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");
|
||||
}
|
@@ -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");
|
||||
}
|
@@ -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
Reference in New Issue
Block a user