From ee605e35b4b3ad5c3109465a9520322af40bc516 Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Fri, 14 Jun 2024 15:39:42 +0300 Subject: [PATCH] feat(bootloader): BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP for C2 (without RTC_MEM) --- components/bootloader/Kconfig.projbuild | 4 +-- .../private_include/bootloader_utility.h | 5 ++++ .../src/bootloader_utility.c | 26 ++++++++++++++++--- docs/en/api-guides/bootloader.rst | 12 ++++++--- examples/system/deep_sleep/README.md | 2 +- examples/system/deep_sleep/partitions.csv | 7 +++++ .../system/deep_sleep/pytest_deep_sleep.py | 8 ++---- examples/system/deep_sleep/sdkconfig.ci.basic | 1 + .../deep_sleep/sdkconfig.ci.esp32_singlecore | 1 + examples/system/deep_sleep/sdkconfig.defaults | 1 + 10 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 examples/system/deep_sleep/partitions.csv diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index ca817d0c6a5e..c318399d963d 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -360,9 +360,9 @@ menu "Bootloader config" # options, allowing to turn on "allow insecure options" and have secure boot with # "skip validation when existing deep sleep". Keeping this to avoid a breaking change, # but - as noted in help - it invalidates the integrity of Secure Boot checks - depends on SOC_RTC_FAST_MEM_SUPPORTED && ((SECURE_BOOT && SECURE_BOOT_INSECURE) || !SECURE_BOOT) + depends on ((SECURE_BOOT && SECURE_BOOT_INSECURE) || !SECURE_BOOT) default n - select BOOTLOADER_RESERVE_RTC_MEM + select BOOTLOADER_RESERVE_RTC_MEM if SOC_RTC_FAST_MEM_SUPPORTED help This option disables the normal validation of an image coming out of deep sleep (checksums, SHA256, and signature). This is a trade-off diff --git a/components/bootloader_support/private_include/bootloader_utility.h b/components/bootloader_support/private_include/bootloader_utility.h index 3867ad4778b2..94040cf41a9e 100644 --- a/components/bootloader_support/private_include/bootloader_utility.h +++ b/components/bootloader_support/private_include/bootloader_utility.h @@ -56,9 +56,14 @@ __attribute__((__noreturn__)) void bootloader_utility_load_boot_image(const boot /** * @brief Load that application which was worked before we go to the deep sleep. * + * If chip supports the RTC memory: * 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. * + * If chip does not support the RTC memory: + * Checks the reboot reason if it is the deep sleep then the partition table is read + * to select and load an application which was worked before we go to the deep sleep. + * */ void bootloader_utility_load_boot_image_from_deep_sleep(void); #endif diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index b28aa656aa9c..4e469b40183e 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -461,15 +461,33 @@ static void set_actual_ota_seq(const bootloader_state_t *bs, int index) void bootloader_utility_load_boot_image_from_deep_sleep(void) { if (esp_rom_get_reset_reason(0) == RESET_REASON_CORE_DEEP_SLEEP) { +#if SOC_RTC_FAST_MEM_SUPPORTED esp_partition_pos_t *partition = bootloader_common_get_rtc_retain_mem_partition(); - if (partition != NULL) { + esp_image_metadata_t image_data; + if (partition != NULL && bootloader_load_image_no_verify(partition, &image_data) == ESP_OK) { + ESP_LOGI(TAG, "Fast booting app from partition at offset 0x%"PRIx32, partition->offset); + bootloader_common_update_rtc_retain_mem(NULL, true); + load_image(&image_data); + } +#else // !SOC_RTC_FAST_MEM_SUPPORTED + bootloader_state_t bs = {0}; + if (bootloader_utility_load_partition_table(&bs)) { + int index_of_last_loaded_app = FACTORY_INDEX; + esp_ota_select_entry_t otadata[2]; + if (bs.ota_info.size && bootloader_common_read_otadata(&bs.ota_info, otadata) == ESP_OK) { + int active_otadata = bootloader_common_get_active_otadata(otadata); + if (active_otadata != -1) { + index_of_last_loaded_app = (otadata[active_otadata].ota_seq - 1) % bs.app_count; + } + } + esp_partition_pos_t partition = index_to_partition(&bs, index_of_last_loaded_app); 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%"PRIx32, partition->offset); - bootloader_common_update_rtc_retain_mem(NULL, true); + if (partition.size && bootloader_load_image_no_verify(&partition, &image_data) == ESP_OK) { + ESP_LOGI(TAG, "Fast booting app from partition at offset 0x%"PRIx32, partition.offset); load_image(&image_data); } } +#endif // !SOC_RTC_FAST_MEM_SUPPORTED ESP_LOGE(TAG, "Fast booting is not successful"); ESP_LOGI(TAG, "Try to load an app as usual with all validations"); } diff --git a/docs/en/api-guides/bootloader.rst b/docs/en/api-guides/bootloader.rst index b8167b462574..f2b4d6277c93 100644 --- a/docs/en/api-guides/bootloader.rst +++ b/docs/en/api-guides/bootloader.rst @@ -163,12 +163,18 @@ Options to work around this are: When Secure Boot V2 is enabled, there is also an absolute binary size limit of {IDF_TARGET_MAX_BOOTLOADER_SIZE} (excluding the 4 KB signature), because the bootloader is first loaded into a fixed size buffer for verification. +Fast Boot from Deep-Sleep +------------------------- + +The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows the wake-up time from Deep-sleep to be reduced (useful for reducing power consumption). This option is available when the :ref:`CONFIG_SECURE_BOOT` option is disabled or :ref:`CONFIG_SECURE_BOOT_INSECURE` is enabled along with Secure Boot. The reduction in time is achieved by ignoring image verification. + .. only:: SOC_RTC_FAST_MEM_SUPPORTED - Fast Boot from Deep-Sleep - ------------------------- + During the first boot, the bootloader stores the address of the application being launched in the RTC FAST memory. After waking up from deep sleep, this address is used to boot the application again without any checks, resulting in a significantly faster load. + +.. only:: not SOC_RTC_FAST_MEM_SUPPORTED - The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows the wake-up time from Deep-sleep to be reduced (useful for reducing power consumption). This option is available when :ref:`CONFIG_SECURE_BOOT` 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. + The {IDF_TARGET_NAME} does not have RTC memory, so a running partition cannot be saved there; instead, the entire partition table is read to select the correct application. During wake-up, the selected application is loaded without any checks, resulting in a significantly faster load. Custom Bootloader ----------------- diff --git a/examples/system/deep_sleep/README.md b/examples/system/deep_sleep/README.md index 1d6129b5bed9..681fc586540d 100644 --- a/examples/system/deep_sleep/README.md +++ b/examples/system/deep_sleep/README.md @@ -19,7 +19,7 @@ Note: Some wake up sources can be disabled via configuration (see section on [pr Warning: On ESP32, touch wake up source cannot be used together with EXT0 wake up source. If they co-exist, IDF will give a runtime error and the program will crash. By default in this example, touch wake up is enabled, and the other two are disabled. You can switch to enable the other wake up sources via menuconfig. -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. +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 (ESP32-C2 does not have RTC memory, so a running partition cannot be saved there, instead the partition table is read to select an application). This example allows you to skip all image checks and speed up the boot. ## How to use example diff --git a/examples/system/deep_sleep/partitions.csv b/examples/system/deep_sleep/partitions.csv new file mode 100644 index 000000000000..a48d23ea2e7d --- /dev/null +++ b/examples/system/deep_sleep/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x4000, +otadata, data, ota, , 0x2000, +factory, app, factory, , 600K, +ota_0, app, ota_0, , 600K, +ota_1, app, ota_1, , 600K, diff --git a/examples/system/deep_sleep/pytest_deep_sleep.py b/examples/system/deep_sleep/pytest_deep_sleep.py index 883dd2f89bb4..a026e16def3a 100644 --- a/examples/system/deep_sleep/pytest_deep_sleep.py +++ b/examples/system/deep_sleep/pytest_deep_sleep.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 import logging import time @@ -67,11 +67,7 @@ def expect_enable_deep_sleep_no_touch() -> None: logging.info('Host measured sleep time at {:.2f}s'.format(sleep_time)) assert 18 < sleep_time < 22 # note: high tolerance as measuring time on the host may have some timing skew - # This line indicates that the CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP option set in sdkconfig.defaults - # has correctly allowed skipping verification on wakeup - # Note: this feature depends on rtc mem - if dut.app.sdkconfig.get('SOC_RTC_MEM_SUPPORTED') is True: - dut.expect_exact('boot: Fast booting app from partition', timeout=2) + dut.expect_exact('boot: Fast booting app from partition', timeout=2) # Check that it measured 2xxxxms in deep sleep, i.e at least 20 seconds: expect_enable_deep_sleep() diff --git a/examples/system/deep_sleep/sdkconfig.ci.basic b/examples/system/deep_sleep/sdkconfig.ci.basic index 6aa89f368a23..87020672f4c0 100644 --- a/examples/system/deep_sleep/sdkconfig.ci.basic +++ b/examples/system/deep_sleep/sdkconfig.ci.basic @@ -8,3 +8,4 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_RESERVE_MEM=512 CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y CONFIG_RTC_CLK_SRC_INT_RC=y +CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/examples/system/deep_sleep/sdkconfig.ci.esp32_singlecore b/examples/system/deep_sleep/sdkconfig.ci.esp32_singlecore index d4587def0fd2..0cabcb5f0277 100644 --- a/examples/system/deep_sleep/sdkconfig.ci.esp32_singlecore +++ b/examples/system/deep_sleep/sdkconfig.ci.esp32_singlecore @@ -13,3 +13,4 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_RESERVE_MEM=512 CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y CONFIG_RTC_CLK_SRC_INT_RC=y +CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/examples/system/deep_sleep/sdkconfig.defaults b/examples/system/deep_sleep/sdkconfig.defaults index 9f61a9a651e9..b508ab21b76f 100644 --- a/examples/system/deep_sleep/sdkconfig.defaults +++ b/examples/system/deep_sleep/sdkconfig.defaults @@ -5,3 +5,4 @@ CONFIG_ULP_COPROC_RESERVE_MEM=512 CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y CONFIG_RTC_CLK_SRC_INT_RC=y CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y +CONFIG_PARTITION_TABLE_CUSTOM=y