diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index a2c2df6444..ba08b72385 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -219,6 +219,31 @@ menu "Bootloader config" It allow to test anti-rollback implemention without permanent write eFuse bits. In partition table should be exist this partition `emul_efuse, data, 5, , 0x2000`. + config BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP + bool "Skip image validation when exiting deep sleep" + depends on (SECURE_BOOT_ENABLED && SECURE_BOOT_INSECURE) || !SECURE_BOOT_ENABLED + default n + help + This option disables the normal validation of an image coming out of + deep sleep (checksums, SHA256, and signature). This is a trade-off + between wakeup performance from deep sleep, and image integrity checks. + + Only enable this if you know what you are doing. It should not be used + in conjunction with using deep_sleep() entry and changing the active OTA + partition as this would skip the validation upon first load of the new + OTA partition. + + config BOOTLOADER_RESERVE_RTC_SIZE + hex + default 0x10 if BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP + default 0 + help + Reserve RTC FAST memory for Skip image validation. This option in bytes. + This option reserves an area in the RTC FAST memory (access only PRO_CPU). + Used to save the addresses of the selected application. + When a wakeup occurs (from Deep sleep), the bootloader retrieves it and + loads the application without validation. + endmenu # Bootloader diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index d0a9469d66..09d4bda3da 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -42,12 +42,12 @@ typedef struct { uint8_t image_digest[32]; /* appended SHA-256 digest */ } esp_image_metadata_t; -/* Mode selection for esp_image_load() */ typedef enum { - ESP_IMAGE_VERIFY, /* Verify image contents, load metadata. Print errors. */ - ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, load metadata. Don't print errors. */ + ESP_IMAGE_VERIFY, /* Verify image contents, not load to memory, load metadata. Print errors. */ + ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, not load to memory, load metadata. Don't print errors. */ #ifdef BOOTLOADER_BUILD - ESP_IMAGE_LOAD, /* Verify image contents, load to memory. Print errors. */ + ESP_IMAGE_LOAD, /* Verify image contents, load to memory, load metadata. Print errors. */ + ESP_IMAGE_LOAD_NO_VALIDATE, /* Not verify image contents, load to memory, load metadata. Print errors. */ #endif } esp_image_load_mode_t; @@ -134,6 +134,24 @@ esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t */ esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metadata_t *data); +/** + * @brief Load an app image without verification (available only in space of bootloader). + * + * If encryption is enabled, data will be transparently decrypted. + * + * @param part Partition to load the app from. + * @param[inout] data Pointer to the image metadata structure which is be filled in by this function. + * 'start_addr' member should be set (to the start address of the image.) + * Other fields will all be initialised by this function. + * + * @return + * - ESP_OK if verify or load was successful + * - ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs + * - ESP_ERR_IMAGE_INVALID if the image appears invalid. + * - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid. + */ +esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_image_metadata_t *data); + /** * @brief Verify the bootloader image. * diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index 3f9d9fe8a7..7d5f67ac7e 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -28,6 +28,7 @@ #include "esp32/rom/uart.h" #include "esp32/rom/gpio.h" #include "esp32/rom/secure_boot.h" +#include "esp32/rom/rtc.h" #include "soc/soc.h" #include "soc/cpu.h" @@ -416,8 +417,31 @@ static void set_actual_ota_seq(const bootloader_state_t *bs, int index) update_anti_rollback(&bs->ota[index]); #endif } +#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) + esp_partition_pos_t partition = index_to_partition(bs, index); + bootloader_common_update_rtc_retain_mem(&partition, true); +#endif } +#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP +void bootloader_utility_load_boot_image_from_deep_sleep(void) +{ + if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { + esp_partition_pos_t* partition = bootloader_common_get_rtc_retain_mem_partition(); + if (partition != NULL) { + esp_image_metadata_t image_data; + if (bootloader_load_image_no_verify(partition, &image_data) == ESP_OK) { + ESP_LOGI(TAG, "Fast booting app from partition at offset 0x%x", partition->offset); + bootloader_common_update_rtc_retain_mem(NULL, true); + load_image(&image_data); + } + } + ESP_LOGE(TAG, "Fast booting is not successful"); + ESP_LOGI(TAG, "Try to load an app as usual with all validations"); + } +} +#endif + #define TRY_LOG_FORMAT "Trying partition index %d offs 0x%x size 0x%x" void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 4bda87af19..b0db9b3984 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -97,14 +97,17 @@ static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_ha static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data) { #ifdef BOOTLOADER_BUILD - bool do_load = (mode == ESP_IMAGE_LOAD); + bool do_load = (mode == ESP_IMAGE_LOAD) || (mode == ESP_IMAGE_LOAD_NO_VALIDATE); + bool do_verify = (mode == ESP_IMAGE_LOAD) || (mode == ESP_IMAGE_VERIFY) || (mode == ESP_IMAGE_VERIFY_SILENT); #else - bool do_load = false; // Can't load the image in app mode + bool do_load = false; // Can't load the image in app mode + bool do_verify = true; // In app mode is avalible only verify mode #endif - bool silent = (mode == ESP_IMAGE_VERIFY_SILENT); + bool silent = (mode == ESP_IMAGE_VERIFY_SILENT); esp_err_t err = ESP_OK; // checksum the image a word at a time. This shaves 30-40ms per MB of image size uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL; + uint32_t *checksum = NULL; bootloader_sha256_handle_t sha_handle = NULL; if (data == NULL || part == NULL) { @@ -125,41 +128,45 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ goto err; } - // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended + if (do_verify) { + checksum = &checksum_word; + + // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended #ifdef SECURE_BOOT_CHECK_SIGNATURE - if (1) { + if (1) { #else - if (data->image.hash_appended) { + if (data->image.hash_appended) { #endif - sha_handle = bootloader_sha256_start(); - if (sha_handle == NULL) { - return ESP_ERR_NO_MEM; + sha_handle = bootloader_sha256_start(); + if (sha_handle == NULL) { + return ESP_ERR_NO_MEM; + } + bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t)); } - bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t)); - } - ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x", - data->image.magic, - data->image.segment_count, - data->image.spi_mode, - data->image.spi_size, - data->image.entry_addr); + ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x", + data->image.magic, + data->image.segment_count, + data->image.spi_mode, + data->image.spi_size, + data->image.entry_addr); - err = verify_image_header(data->start_addr, &data->image, silent); - if (err != ESP_OK) { - goto err; - } + err = verify_image_header(data->start_addr, &data->image, silent); + if (err != ESP_OK) { + goto err; + } - if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) { - FAIL_LOAD("image at 0x%x segment count %d exceeds max %d", - data->start_addr, data->image.segment_count, ESP_IMAGE_MAX_SEGMENTS); - } + if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) { + FAIL_LOAD("image at 0x%x segment count %d exceeds max %d", + data->start_addr, data->image.segment_count, ESP_IMAGE_MAX_SEGMENTS); + } + } // if (do_verify) uint32_t next_addr = data->start_addr + sizeof(esp_image_header_t); for(int i = 0; i < data->image.segment_count; i++) { esp_image_segment_header_t *header = &data->segments[i]; ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr); - err = process_segment(i, next_addr, header, silent, do_load, sha_handle, &checksum_word); + err = process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum); if (err != ESP_OK) { goto err; } @@ -168,58 +175,61 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ next_addr += header->data_len; } - // Segments all loaded, verify length - uint32_t end_addr = next_addr; - if (end_addr < data->start_addr) { - FAIL_LOAD("image offset has wrapped"); - } - - data->image_len = end_addr - data->start_addr; - ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr); - if (!esp_cpu_in_ocd_debug_mode()) { - err = verify_checksum(sha_handle, checksum_word, data); - if (err != ESP_OK) { - goto err; + if (do_verify) { + // Segments all loaded, verify length + uint32_t end_addr = next_addr; + if (end_addr < data->start_addr) { + FAIL_LOAD("image offset has wrapped"); } - } - if (data->image_len > part->size) { - FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size); - } - bool is_bootloader = (data->start_addr == ESP_BOOTLOADER_OFFSET); - /* For secure boot, we don't verify signature on bootloaders. + data->image_len = end_addr - data->start_addr; + ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr); + if (NULL != checksum && !esp_cpu_in_ocd_debug_mode()) { + err = verify_checksum(sha_handle, checksum_word, data); + if (err != ESP_OK) { + goto err; + } + } - For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because esptool.py may have - rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead. - */ - if (!is_bootloader) { + if (data->image_len > part->size) { + FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size); + } + + bool is_bootloader = (data->start_addr == ESP_BOOTLOADER_OFFSET); + /* For secure boot, we don't verify signature on bootloaders. + + For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because esptool.py may have + rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead. + */ + if (!is_bootloader) { #ifdef SECURE_BOOT_CHECK_SIGNATURE - // secure boot images have a signature appended - err = verify_secure_boot_signature(sha_handle, data); + // secure boot images have a signature appended + err = verify_secure_boot_signature(sha_handle, data); #else - // No secure boot, but SHA-256 can be appended for basic corruption detection - if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) { - err = verify_simple_hash(sha_handle, data); - } + // No secure boot, but SHA-256 can be appended for basic corruption detection + if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) { + err = verify_simple_hash(sha_handle, data); + } #endif // SECURE_BOOT_CHECK_SIGNATURE - } else { // is_bootloader - // bootloader may still have a sha256 digest handle open - if (sha_handle != NULL) { - bootloader_sha256_finish(sha_handle, NULL); + } else { // is_bootloader + // bootloader may still have a sha256 digest handle open + if (sha_handle != NULL) { + bootloader_sha256_finish(sha_handle, NULL); + } } - } - if (data->image.hash_appended) { - const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN); - if (hash == NULL) { - err = ESP_FAIL; - goto err; + if (data->image.hash_appended) { + const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN); + if (hash == NULL) { + err = ESP_FAIL; + goto err; + } + memcpy(data->image_digest, hash, HASH_LEN); + bootloader_munmap(hash); } - memcpy(data->image_digest, hash, HASH_LEN); - bootloader_munmap(hash); - } + sha_handle = NULL; + } // if (do_verify) - sha_handle = NULL; if (err != ESP_OK) { goto err; } @@ -263,6 +273,15 @@ esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metad #endif } +esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_image_metadata_t *data) +{ +#ifdef BOOTLOADER_BUILD + return image_load(ESP_IMAGE_LOAD_NO_VALIDATE, part, data); +#else + return ESP_FAIL; +#endif +} + esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data) { return image_load(mode, part, data); @@ -396,6 +415,13 @@ err: static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, uint32_t data_len, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum) { + // If we are not loading, and the checksum is empty, skip processing this + // segment for data + if(!do_load && checksum == NULL) { + ESP_LOGD(TAG, "skipping checksum for segment"); + return ESP_OK; + } + const uint32_t *data = (const uint32_t *)bootloader_mmap(data_addr, data_len); if(!data) { ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", @@ -422,7 +448,9 @@ static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, ui for (int i = 0; i < data_len; i += 4) { int w_i = i/4; // Word index uint32_t w = src[w_i]; - *checksum ^= w; + if (checksum != NULL) { + *checksum ^= w; + } #ifdef BOOTLOADER_BUILD if (do_load) { dest[w_i] = w ^ ((w_i & 1) ? ram_obfs_value[0] : ram_obfs_value[1]); diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index d88cdc0342..514b9ebd82 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -21,6 +21,12 @@ #define CONFIG_BT_RESERVE_DRAM 0 #endif +#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) +#define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE) +#else +#define ESP_BOOTLOADER_RESERVE_RTC 0 +#endif + #if defined(CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE) ASSERT((CONFIG_ESP32_FIXED_STATIC_RAM_SIZE <= 0x2c200), @@ -74,7 +80,7 @@ MEMORY rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000 /* RTC fast memory (same block as above), viewed from data bus */ - rtc_data_seg(RW) : org = 0x3ff80000, len = 0x2000 + rtc_data_seg(RW) : org = 0x3ff80000, len = 0x2000 - ESP_BOOTLOADER_RESERVE_RTC /* RTC slow memory (data accessible). Persists over deep sleep.