From 1e32fa2cf7cf328d965a8f5015cbb28d787beb58 Mon Sep 17 00:00:00 2001 From: Tim Nordell Date: Fri, 12 Apr 2019 15:35:34 -0500 Subject: [PATCH 1/6] bootloader: Remove extraneous newlines from some debug statements ESP_LOGD(...) provides its own new lines so remove these from these debug strings. Signed-off-by: Tim Nordell --- components/bootloader_support/src/esp_image_format.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 620d884a2e..95959325d0 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -494,15 +494,15 @@ static bool should_load(uint32_t load_addr) if (!load_rtc_memory) { if (load_addr >= SOC_RTC_IRAM_LOW && load_addr < SOC_RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC fast memory segment at 0x%08x\n", load_addr); + ESP_LOGD(TAG, "Skipping RTC fast memory segment at 0x%08x", load_addr); return false; } if (load_addr >= SOC_RTC_DRAM_LOW && load_addr < SOC_RTC_DRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC fast memory segment at 0x%08x\n", load_addr); + ESP_LOGD(TAG, "Skipping RTC fast memory segment at 0x%08x", load_addr); return false; } if (load_addr >= SOC_RTC_DATA_LOW && load_addr < SOC_RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC slow memory segment at 0x%08x\n", load_addr); + ESP_LOGD(TAG, "Skipping RTC slow memory segment at 0x%08x", load_addr); return false; } } From 82984f05394bc04e5abda365bc734d168c2d69b7 Mon Sep 17 00:00:00 2001 From: Tim Nordell Date: Fri, 12 Apr 2019 15:34:46 -0500 Subject: [PATCH 2/6] bootloader: Do not obfuscate RAM if we are not doing image validation No need to take this step if we are not doing image validation. The obfuscation only buys us a tiny bit of "security" anyways since the main parts of flash are memory mapped, too. This saves a little bit of wake-up time when waking up from deep sleep when the BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP option is set. Signed-off-by: Tim Nordell --- components/bootloader_support/src/esp_image_format.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 95959325d0..4bda87af19 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -225,7 +225,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ } #ifdef BOOTLOADER_BUILD - if (do_load) { // Need to deobfuscate RAM + if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) { // Need to deobfuscate RAM for (int i = 0; i < data->image.segment_count; i++) { uint32_t load_addr = data->segments[i].load_addr; if (should_load(load_addr)) { @@ -403,6 +403,12 @@ static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, ui return ESP_FAIL; } + if (checksum == NULL && sha_handle == NULL) { + memcpy((void *)load_addr, data, data_len); + bootloader_munmap(data); + return ESP_OK; + } + #ifdef BOOTLOADER_BUILD // Set up the obfuscation value to use for loading while (ram_obfs_value[0] == 0 || ram_obfs_value[1] == 0) { From 43393cf4d17dadb478b2a9e0fda473b121af7f48 Mon Sep 17 00:00:00 2001 From: Tim Nordell Date: Fri, 12 Apr 2019 15:32:47 -0500 Subject: [PATCH 3/6] bootloader: Support for skipping validation upon wake from deep sleep This saves time when waking up from deep sleep, but potentially decreases the security of the system. If the application able to modify itself (especially areas that are loaded into RAM) in flash while running without crashing or is modifies the cached bits of information about what was last booted from the bootloader, this could cause security issues if the user does a "deep sleep reset" since the full validation is skipped. Signed-off-by: Tim Nordell --- components/bootloader/Kconfig.projbuild | 25 +++ .../include/esp_image_format.h | 26 ++- .../src/bootloader_utility.c | 24 +++ .../bootloader_support/src/esp_image_format.c | 166 ++++++++++-------- components/esp32/ld/esp32.ld | 8 +- 5 files changed, 175 insertions(+), 74 deletions(-) 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. From 83db8d402b4fc08aff5ba3d4d103ae3253f90a5b Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Fri, 5 Jul 2019 18:05:35 +0800 Subject: [PATCH 4/6] examples: Add the fast wakeup option for deep sleep examples The CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP option is enabled by default for the following examples: - system/deep_sleep, - system/ulp, - system/ulp_adc. --- examples/system/deep_sleep/README.md | 1 + examples/system/deep_sleep/sdkconfig.defaults | 1 + examples/system/ulp/README.md | 2 ++ examples/system/ulp/sdkconfig.defaults | 1 + examples/system/ulp_adc/README.md | 2 ++ examples/system/ulp_adc/sdkconfig.defaults | 1 + 6 files changed, 8 insertions(+) diff --git a/examples/system/deep_sleep/README.md b/examples/system/deep_sleep/README.md index 3d4438de7a..c25c316fa7 100644 --- a/examples/system/deep_sleep/README.md +++ b/examples/system/deep_sleep/README.md @@ -9,3 +9,4 @@ The following wake up sources are configured: - Touch: wake up the chip if any of the touch pads are pressed (GPIO32, GPIO33) - ULP: wake up when the chip temperature changes by more than ~5 degrees Celsius (this value hasn't been characterized exactly yet). +In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot. \ No newline at end of file diff --git a/examples/system/deep_sleep/sdkconfig.defaults b/examples/system/deep_sleep/sdkconfig.defaults index c80761b50f..8a0c139042 100644 --- a/examples/system/deep_sleep/sdkconfig.defaults +++ b/examples/system/deep_sleep/sdkconfig.defaults @@ -4,3 +4,4 @@ CONFIG_ESP32_ULP_COPROC_ENABLED=y CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=512 CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y +CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y \ No newline at end of file diff --git a/examples/system/ulp/README.md b/examples/system/ulp/README.md index 4b2c5341a1..3c854043ed 100644 --- a/examples/system/ulp/README.md +++ b/examples/system/ulp/README.md @@ -12,6 +12,8 @@ Upon wakeup, the main program saves total edge count into NVS and returns to dee In this example the input signal is connected to GPIO0. Note that this pin was chosen because most development boards have a button connected to it, so the pulses to be counted can be generated by pressing the button. For real world applications this is not a good choice of a pin, because GPIO0 also acts as a bootstrapping pin. To change the pin number, check the ESP32 Chip Pin List document and adjust `gpio_num` and `ulp_io_number` variables in main.c. +In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot. + ## Example output ``` diff --git a/examples/system/ulp/sdkconfig.defaults b/examples/system/ulp/sdkconfig.defaults index a5e2b8ccd7..7214df17c1 100644 --- a/examples/system/ulp/sdkconfig.defaults +++ b/examples/system/ulp/sdkconfig.defaults @@ -6,3 +6,4 @@ CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_BOOTLOADER_LOG_LEVEL=2 CONFIG_LOG_DEFAULT_LEVEL_WARN=y CONFIG_LOG_DEFAULT_LEVEL=2 +CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y diff --git a/examples/system/ulp_adc/README.md b/examples/system/ulp_adc/README.md index f07cf0fc77..7948dd4821 100644 --- a/examples/system/ulp_adc/README.md +++ b/examples/system/ulp_adc/README.md @@ -16,6 +16,8 @@ Measurement frequency, Hz | Average current, uA 50 | 20 100 | 37 +In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot. + ## Example output Below is the output from this example. diff --git a/examples/system/ulp_adc/sdkconfig.defaults b/examples/system/ulp_adc/sdkconfig.defaults index a5e2b8ccd7..7214df17c1 100644 --- a/examples/system/ulp_adc/sdkconfig.defaults +++ b/examples/system/ulp_adc/sdkconfig.defaults @@ -6,3 +6,4 @@ CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_BOOTLOADER_LOG_LEVEL=2 CONFIG_LOG_DEFAULT_LEVEL_WARN=y CONFIG_LOG_DEFAULT_LEVEL=2 +CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y From abffc3b11d7d0bd936cb20a35634d93d8b253e5d Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Fri, 5 Jul 2019 18:11:33 +0800 Subject: [PATCH 5/6] docs: Add a description of the fast wakeup --- docs/en/api-guides/bootloader.rst | 4 ++++ docs/en/api-guides/deep-sleep-stub.rst | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/en/api-guides/bootloader.rst b/docs/en/api-guides/bootloader.rst index c699ac2fd1..a849eb6953 100644 --- a/docs/en/api-guides/bootloader.rst +++ b/docs/en/api-guides/bootloader.rst @@ -55,6 +55,10 @@ After the GPIO input is deactivated and the device reboots, the normally configu :ref:`CONFIG_BOOTLOADER_HOLD_TIME_GPIO` - this is hold time of GPIO for reset/test mode (by default 5 seconds). The GPIO must be held low continuously for this period of time after reset before a factory reset or test partition boot (as applicable) is performed. +Fast boot from Deep Sleep +------------------------- +The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows to reduce the wake-up time (useful to reduce consumption). This option is available when the :ref:`CONFIG_SECURE_BOOT_ENABLED` option is disabled. Reduction of time is achieved due to the lack of image verification. During the first boot, the bootloader stores the address of the application being launched in the RTC FAST memory. And during the awakening, this address is used for booting without any checks, thus fast loading is achieved. + Customer bootloader --------------------- The current bootloader implementation allows the customer to override it. To do this, you must copy the folder `/esp-idf/components/bootloader` and then edit `/your_project/components/bootloader/subproject/main/bootloader_main.c`. diff --git a/docs/en/api-guides/deep-sleep-stub.rst b/docs/en/api-guides/deep-sleep-stub.rst index 6fd547271a..5cd98144eb 100644 --- a/docs/en/api-guides/deep-sleep-stub.rst +++ b/docs/en/api-guides/deep-sleep-stub.rst @@ -88,4 +88,4 @@ For example, the equivalent example in ``rtc_wake_stub_counter.c``:: The second way is a better option if you need to use strings, or write other more complex code. - +To reduce wake-up time use the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option, see more information in :doc:`Fast boot from Deep Sleep `. From c543aac91ed54dc93c4cd5a46756648c3ca1169b Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Fri, 5 Jul 2019 18:18:58 +0800 Subject: [PATCH 6/6] bootloader: API for the fast wakeup and custom using RTC mem Added "Reserve RTC FAST memory for custom purposes" option. Added a boot counter. --- components/bootloader/Kconfig.projbuild | 24 ++++++- .../subproject/main/bootloader_start.c | 8 +++ .../include/bootloader_common.h | 62 ++++++++++++++++++ .../include/esp_image_format.h | 28 +++++++++ .../include_bootloader/bootloader_utility.h | 10 +++ .../src/bootloader_common.c | 63 +++++++++++++++++++ .../src/bootloader_utility.c | 2 +- components/esp32/ld/esp32.ld | 4 +- 8 files changed, 198 insertions(+), 3 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index ba08b72385..2d4a050919 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -235,7 +235,7 @@ menu "Bootloader config" config BOOTLOADER_RESERVE_RTC_SIZE hex - default 0x10 if BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP + default 0x10 if BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP || BOOTLOADER_CUSTOM_RESERVE_RTC default 0 help Reserve RTC FAST memory for Skip image validation. This option in bytes. @@ -244,6 +244,28 @@ menu "Bootloader config" When a wakeup occurs (from Deep sleep), the bootloader retrieves it and loads the application without validation. + config BOOTLOADER_CUSTOM_RESERVE_RTC + bool "Reserve RTC FAST memory for custom purposes" + default n + help + This option allows the customer to place data in the RTC FAST memory, + this area remains valid when rebooted, except for power loss. + This memory is located at a fixed address and is available + for both the bootloader and the application. + (The application and bootoloader must be compiled with the same option). + The RTC FAST memory has access only through PRO_CPU. + + config BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE + hex "Size in bytes for custom purposes" + range 0 0x10 + default 0 + depends on BOOTLOADER_CUSTOM_RESERVE_RTC + help + This option reserves in RTC FAST memory the area for custom purposes. + If you want to create your own bootloader and save more information + in this area of memory, you can increase it. It must be a multiple of 4 bytes. + This area (rtc_retain_mem_t) is reserved and has access from the bootloader and an application. + endmenu # Bootloader diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index e9bd32d881..f93fa5694b 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -41,6 +41,14 @@ void __attribute__((noreturn)) call_start_cpu0(void) bootloader_reset(); } +#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP + // If this boot is a wake up from the deep sleep then go to the short way, + // try to load the application which worked before deep sleep. + // It skips a lot of checks due to it was done before (while first boot). + bootloader_utility_load_boot_image_from_deep_sleep(); + // If it is not successful try to load an application as usual. +#endif + // 2. Select the number of boot partition bootloader_state_t bs = { 0 }; int boot_index = select_partition_number(&bs); diff --git a/components/bootloader_support/include/bootloader_common.h b/components/bootloader_support/include/bootloader_common.h index 4b887f55f9..1058b20cc5 100644 --- a/components/bootloader_support/include/bootloader_common.h +++ b/components/bootloader_support/include/bootloader_common.h @@ -146,6 +146,68 @@ esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t */ void bootloader_common_vddsdio_configure(void); +#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC ) +/** + * @brief Returns partition from rtc_retain_mem + * + * Uses to get the partition of application which was worked before to go to the deep sleep. + * This partition was stored in rtc_retain_mem. + * Note: This function operates the RTC FAST memory which available only for PRO_CPU. + * Make sure that this function is used only PRO_CPU. + * + * @return partition: If rtc_retain_mem is valid. + * - NULL: If it is not valid. + */ +esp_partition_pos_t* bootloader_common_get_rtc_retain_mem_partition(void); + +/** + * @brief Update the partition and reboot_counter in rtc_retain_mem. + * + * This function saves the partition of application for fast booting from the deep sleep. + * An algorithm uses this partition to avoid reading the otadata and does not validate an image. + * Note: This function operates the RTC FAST memory which available only for PRO_CPU. + * Make sure that this function is used only PRO_CPU. + * + * @param[in] partition App partition description. Can be NULL, in this case rtc_retain_mem.partition is not updated. + * @param[in] reboot_counter If true then update reboot_counter. + * + */ +void bootloader_common_update_rtc_retain_mem(esp_partition_pos_t* partition, bool reboot_counter); + +/** + * @brief Reset entire rtc_retain_mem. + * + * Note: This function operates the RTC FAST memory which available only for PRO_CPU. + * Make sure that this function is used only PRO_CPU. + */ +void bootloader_common_reset_rtc_retain_mem(void); + +/** + * @brief Returns reboot_counter from rtc_retain_mem + * + * The reboot_counter counts the number of reboots. Reset only when power is off. + * The very first launch of the application will be from 1. + * Overflow is not possible, it will stop at the value UINT16_MAX. + * Note: This function operates the RTC FAST memory which available only for PRO_CPU. + * Make sure that this function is used only PRO_CPU. + * + * @return reboot_counter: 1..65535 + * - 0: If rtc_retain_mem is not valid. + */ +uint16_t bootloader_common_get_rtc_retain_mem_reboot_counter(void); + +/** + * @brief Returns rtc_retain_mem + * + * Note: This function operates the RTC FAST memory which available only for PRO_CPU. + * Make sure that this function is used only PRO_CPU. + * + * @return rtc_retain_mem + */ +rtc_retain_mem_t* bootloader_common_get_rtc_retain_mem(void); + +#endif + #ifdef __cplusplus } #endif diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 09d4bda3da..4e656a17b6 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -51,6 +51,34 @@ typedef enum { #endif } esp_image_load_mode_t; +typedef struct { + esp_partition_pos_t partition; /*!< Partition of application which worked before goes to the deep sleep. */ + uint16_t reboot_counter; /*!< Reboot counter. Reset only when power is off. */ + uint16_t reserve; /*!< Reserve */ +#ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC + uint8_t custom[CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE]; /*!< Reserve for custom propose */ +#endif + uint32_t crc; /*!< Check sum crc32 */ +} rtc_retain_mem_t; + +#ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC +_Static_assert(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE % 4 == 0, "CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE must be a multiple of 4 bytes"); +#endif + +#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) || defined(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC) +_Static_assert(CONFIG_BOOTLOADER_RESERVE_RTC_SIZE % 4 == 0, "CONFIG_BOOTLOADER_RESERVE_RTC_SIZE must be a multiple of 4 bytes"); +#endif + +#ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC +#define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE) +#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) +#define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE) +#endif + +#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) || defined(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC) +_Static_assert(sizeof(rtc_retain_mem_t) <= ESP_BOOTLOADER_RESERVE_RTC, "Reserved RTC area must exceed size of rtc_retain_mem_t"); +#endif + /** * @brief Verify and (optionally, in bootloader mode) load an app image. * diff --git a/components/bootloader_support/include_bootloader/bootloader_utility.h b/components/bootloader_support/include_bootloader/bootloader_utility.h index 84d0cb48f3..4f31f5acbc 100644 --- a/components/bootloader_support/include_bootloader/bootloader_utility.h +++ b/components/bootloader_support/include_bootloader/bootloader_utility.h @@ -54,6 +54,16 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs) */ __attribute__((noreturn)) void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index); +#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP +/** + * @brief Load that application which was worked before we go to the deep sleep. + * + * Checks the reboot reason if it is the deep sleep and has a valid partition in the RTC memory + * then try to load the application which was worked before we go to the deep sleep. + * + */ +void bootloader_utility_load_boot_image_from_deep_sleep(void); +#endif /** * @brief Software reset the ESP32 diff --git a/components/bootloader_support/src/bootloader_common.c b/components/bootloader_support/src/bootloader_common.c index a10fb2f2ac..acc7644ea3 100644 --- a/components/bootloader_support/src/bootloader_common.c +++ b/components/bootloader_support/src/bootloader_common.c @@ -270,3 +270,66 @@ void bootloader_common_vddsdio_configure(void) } #endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST } + + +#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC ) + +rtc_retain_mem_t *const rtc_retain_mem = (rtc_retain_mem_t *)(SOC_RTC_DRAM_HIGH - sizeof(rtc_retain_mem_t)); + +static bool check_rtc_retain_mem(void) +{ + return crc32_le(UINT32_MAX, (uint8_t*)rtc_retain_mem, sizeof(rtc_retain_mem_t) - sizeof(rtc_retain_mem->crc)) == rtc_retain_mem->crc && rtc_retain_mem->crc != UINT32_MAX; +} + +static void update_rtc_retain_mem_crc(void) +{ + rtc_retain_mem->crc = crc32_le(UINT32_MAX, (uint8_t*)rtc_retain_mem, sizeof(rtc_retain_mem_t) - sizeof(rtc_retain_mem->crc)); +} + +void bootloader_common_reset_rtc_retain_mem(void) +{ + memset(rtc_retain_mem, 0, sizeof(rtc_retain_mem_t)); +} + +uint16_t bootloader_common_get_rtc_retain_mem_reboot_counter(void) +{ + if (check_rtc_retain_mem()) { + return rtc_retain_mem->reboot_counter; + } + return 0; +} + +esp_partition_pos_t* bootloader_common_get_rtc_retain_mem_partition(void) +{ + if (check_rtc_retain_mem()) { + return &rtc_retain_mem->partition; + } + return NULL; +} + +void bootloader_common_update_rtc_retain_mem(esp_partition_pos_t* partition, bool reboot_counter) +{ + if (reboot_counter) { + if (!check_rtc_retain_mem()) { + bootloader_common_reset_rtc_retain_mem(); + } + if (++rtc_retain_mem->reboot_counter == 0) { + // do not allow to overflow. Stop it. + --rtc_retain_mem->reboot_counter; + } + + } + + if (partition != NULL) { + rtc_retain_mem->partition.offset = partition->offset; + rtc_retain_mem->partition.size = partition->size; + } + + update_rtc_retain_mem_crc(); +} + +rtc_retain_mem_t* bootloader_common_get_rtc_retain_mem(void) +{ + return rtc_retain_mem; +} +#endif \ No newline at end of file diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index 7d5f67ac7e..b9d76ed9bc 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -417,7 +417,7 @@ 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 ) +#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC ) esp_partition_pos_t partition = index_to_partition(bs, index); bootloader_common_update_rtc_retain_mem(&partition, true); #endif diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index 514b9ebd82..a52b56f90d 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -21,7 +21,9 @@ #define CONFIG_BT_RESERVE_DRAM 0 #endif -#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) +#ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC +#define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE) +#elif 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