feat(app_update): Add auto-confirmation of OTA updates

Enable rollback auto-confirmation by default
This commit is contained in:
Konstantin Kondrashov
2026-09-01 12:53:21 +03:00
committed by Konstantin Kondrashov
parent dce5812654
commit de1e5035b5
27 changed files with 351 additions and 70 deletions
+7
View File
@@ -5,6 +5,7 @@ if(${target} STREQUAL "linux")
endif()
idf_component_register(SRCS "esp_ota_ops.c"
"ota_auto_confirm.c"
INCLUDE_DIRS "include"
REQUIRES partition_table bootloader_support
esp_app_format esp_bootloader_format esp_partition
@@ -16,6 +17,12 @@ idf_component_register(SRCS "esp_ota_ops.c"
# link time without app_update forcing mbedtls in by itself.
idf_component_optional_requires(PRIVATE mbedtls)
if(CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP)
# ota_auto_confirm.c has no other externally referenced symbol, so force the linker to pull
# it in from the archive to override the weak esp_ota_confirm_rollback_hook() in freertos.
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_ota_confirm_rollback_hook")
endif()
idf_define_esp_err_codes(HEADERS include/esp_ota_ops.h)
if(NOT BOOTLOADER_BUILD)
+5 -5
View File
@@ -127,7 +127,7 @@ static esp_err_t image_validate(const esp_partition_t *partition, esp_image_load
static esp_ota_img_states_t set_new_state_otadata(void)
{
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
ESP_LOGD(TAG, "Monitoring the first boot of the app is enabled.");
return ESP_OTA_IMG_NEW;
#else
@@ -176,7 +176,7 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
return ESP_ERR_OTA_PARTITION_CONFLICT;
}
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
esp_ota_img_states_t ota_state_running_part;
if (esp_ota_get_state_partition(running_partition, &ota_state_running_part) == ESP_OK) {
if (ota_state_running_part == ESP_OTA_IMG_PENDING_VERIFY) {
@@ -221,7 +221,7 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
}
}
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
if (is_ota_partition(partition)) {
esp_ota_invalidate_inactive_ota_data_slot();
}
@@ -259,7 +259,7 @@ esp_err_t esp_ota_resume(const esp_partition_t *partition, const size_t erase_si
return ESP_ERR_OTA_PARTITION_CONFLICT;
}
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
// Mirror esp_ota_begin(): refuse to resume an OTA into an app slot while the running
// app is still pending verification, otherwise the rollback target could be
// overwritten during the unconfirmed window.
@@ -1376,7 +1376,7 @@ esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_i
const esp_partition_t *running_app_part = esp_ota_get_running_partition();
esp_err_t ret = ESP_FAIL;
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
esp_ota_img_states_t running_app_state;
ret = esp_ota_get_state_partition(running_app_part, &running_app_state);
if (ret != ESP_OK) {
+45
View File
@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#include "esp_ota_ops.h"
#include "esp_log.h"
ESP_LOG_ATTR_TAG(TAG, "ota_auto_confirm");
// Strong override of the weak hook declared in components/freertos/app_startup.c
void esp_ota_confirm_rollback_hook(void)
{
esp_ota_img_states_t ota_state;
const esp_partition_t *running = esp_ota_get_running_partition();
if (esp_ota_get_state_partition(running, &ota_state) != ESP_OK) {
return;
}
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
ESP_LOGI(TAG, "Auto-confirming OTA application as valid");
} else if (ota_state == ESP_OTA_IMG_NEW) {
/* The bootloader does not support rollback: it did not transition
* the OTA state from ESP_OTA_IMG_NEW to ESP_OTA_IMG_PENDING_VERIFY.
* Mark as VALID to keep the state consistent. */
ESP_LOGW(TAG, "Bootloader is not capable of rollback");
} else {
return;
}
esp_err_t err = esp_ota_mark_app_valid_cancel_rollback();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mark app valid: %s", esp_err_to_name(err));
}
}
#endif /* CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP */
@@ -4,6 +4,7 @@ components/app_update/test_apps:
enable:
- if: CONFIG_NAME == "defaults" and IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32c61", "esp32h2", "esp32p4", "esp32s2", "esp32s3"]
- if: CONFIG_NAME == "rollback" and IDF_TARGET in ["esp32", "esp32c3", "esp32s3", "esp32p4"]
- if: CONFIG_NAME == "rollback_disabled" and IDF_TARGET in ["esp32", "esp32c3", "esp32s3", "esp32p4"]
- if: CONFIG_NAME == "xip_psram" and SOC_SPIRAM_XIP_SUPPORTED == 1
# S2 doesn't have ROM for flash
- if: CONFIG_NAME == "xip_psram_with_rom_impl" and (SOC_SPIRAM_XIP_SUPPORTED == 1 and IDF_TARGET != "esp32s2")
@@ -11,6 +11,7 @@
#include "bootloader_common.h"
#include "../bootloader_flash/include/bootloader_flash_priv.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "unity.h"
#include "utils_update.h"
#include "sdkconfig.h"
@@ -248,6 +249,7 @@ static void test_flow5(void)
TEST_CASE_MULTIPLE_STAGES("Switching between factory, test, factory", "[app_update][timeout=90][reset=SW_CPU_RESET, SW_CPU_RESET, DEEPSLEEP_RESET]", start_test, test_flow5, test_flow5, test_flow5);
#endif
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_rollback1(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -265,7 +267,7 @@ static void test_rollback1(void)
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_state_partition(cur_app, &ota_state));
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
@@ -277,7 +279,7 @@ static void test_rollback1(void)
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
@@ -329,7 +331,9 @@ static void test_rollback1_1(void)
// 4 Stage: run OTA0 -> check it -> esp_ota_mark_app_invalid_rollback_and_reboot() -> reboot
// 5 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test rollback. factory, OTA0, OTA0, rollback -> factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback1, test_rollback1, test_rollback1, test_rollback1_1);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_rollback2(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -347,7 +351,7 @@ static void test_rollback2(void)
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_state_partition(cur_app, &ota_state));
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
@@ -359,7 +363,7 @@ static void test_rollback2(void)
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
@@ -370,7 +374,7 @@ static void test_rollback2(void)
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
update_partition = app_update();
TEST_ESP_OK(esp_ota_get_state_partition(update_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_NEW, ota_state);
@@ -382,7 +386,7 @@ static void test_rollback2(void)
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_UNDEFINED, ota_state);
TEST_ESP_OK(esp_ota_mark_app_invalid_rollback_and_reboot());
#else
@@ -416,7 +420,7 @@ static void test_rollback2_1(void)
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
TEST_ESP_OK(esp_ota_get_state_partition(invalid_partition, &ota_state));
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ASSERT_EQUAL(ESP_OTA_IMG_INVALID, ota_state);
#else
TEST_ASSERT_EQUAL(ESP_OTA_IMG_ABORTED, ota_state);
@@ -430,7 +434,9 @@ static void test_rollback2_1(void)
// 4 Stage: run OTA1 -> check it -> PENDING_VERIFY/esp_ota_mark_app_invalid_rollback_and_reboot() -> reboot
// 5 Stage: run OTA0(rollback) -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test rollback. factory, OTA0, OTA1, rollback -> OTA0", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback2, test_rollback2, test_rollback2, test_rollback2_1);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_erase_last_app_flow(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -484,7 +490,9 @@ static void test_erase_last_app_rollback(void)
// 4 Stage: run OTA1 -> check it -> erase OTA0 and rollback -> reboot
// 5 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Test erase_last_boot_app_partition. factory, OTA1, OTA0, factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_rollback);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_flow6(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -515,6 +523,7 @@ static void test_flow6(void)
// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS
TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0 using esp_ota_write_with_offset", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow6, test_flow6);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
TEST_CASE("Test esp_partition_get_sha256 returns ESP_ERR_IMAGE_INVALID when image is invalid", "[partitions]")
{
@@ -538,6 +547,7 @@ TEST_CASE("Test esp_partition_get_sha256 returns ESP_ERR_IMAGE_INVALID when imag
TEST_ASSERT_EQUAL_MEMORY_MESSAGE(sha_256_cur_app, sha_256_other_app, sizeof(sha_256_cur_app), "must be the same");
}
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_rollback3(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -567,7 +577,7 @@ static void test_rollback3(void)
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
update_partition = esp_ota_get_next_update_partition(NULL);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
// two partitions are valid
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
esp_ota_img_states_t ota_state;
@@ -578,7 +588,7 @@ static void test_rollback3(void)
esp_ota_handle_t update_handle = 0;
TEST_ESP_OK(esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle));
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
// After esp_ota_begin, the only one partition is valid
// ota data slots do not have an entry about the update_partition.
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_ota_get_state_partition(update_partition, &ota_state));
@@ -610,7 +620,7 @@ static void test_rollback3_1(void)
TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
const esp_partition_t* next_update_partition = esp_ota_get_next_update_partition(NULL);
TEST_ASSERT_NOT_NULL(next_update_partition);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
// ota data slots do not have an entry about the next_update_partition.
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_ota_get_state_partition(next_update_partition, &ota_state));
#endif
@@ -618,7 +628,9 @@ static void test_rollback3_1(void)
}
TEST_CASE_MULTIPLE_STAGES("Test rollback. Updated partition invalidated after esp_ota_begin", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback3, test_rollback3, test_rollback3, test_rollback3_1);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#ifndef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_rollback4(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
@@ -643,7 +655,7 @@ static void test_rollback4(void)
// This will not change the running partition since we haven't rebooted.
// The esp_rewrite_otadata() will update the otadata for the non-running partition only.
app_update();
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
// The last call to esp_rewrite_otadata should have updated the otadata for the non-running partition only.
// Therefore, calling esp_ota_get_state_partition on the running partition should succeed and not return ESP_ERR_NOT_FOUND
const esp_partition_t* running_partition;
@@ -666,3 +678,41 @@ static void test_rollback4(void)
}
TEST_CASE_MULTIPLE_STAGES("Test esp_rewrite_otadata. Updated sequence number for non-running partition always", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_rollback4, test_rollback4, test_rollback4);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
static void test_ota_auto_confirm(void)
{
uint8_t boot_count = get_boot_count_from_nvs();
boot_count++;
set_boot_count_in_nvs(boot_count);
ESP_LOGI(TAG, "boot count %d", boot_count);
const esp_partition_t *cur_app = get_running_firmware();
esp_ota_img_states_t ota_state = 0x5555AAAA;
switch (boot_count) {
case 2:
ESP_LOGI(TAG, "Factory: writing OTA0 and rebooting");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
app_update();
reboot_as_deep_sleep();
break;
case 3:
ESP_LOGI(TAG, "OTA0: verifying OTA app was auto-confirmed during startup");
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
erase_ota_data();
break;
default:
erase_ota_data();
TEST_FAIL_MESSAGE("Unexpected stage");
break;
}
}
// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep.
// 2 Stage: run factory -> copy factory to OTA0 -> reboot --//--
// 3 Stage: run OTA0 -> verify OTA app was auto-confirmed, state is VALID -> PASS
TEST_CASE_MULTIPLE_STAGES("Test OTA auto-confirm during startup (PENDING_VERIFY -> VALID)", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_ota_auto_confirm, test_ota_auto_confirm);
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
@@ -288,7 +288,7 @@ void reset_output_pin(uint32_t num_pin)
void mark_app_valid(void)
{
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
#endif
}
@@ -57,6 +57,7 @@ def test_app_update_xip_psram_rom_impl(dut: Dut) -> None:
'config',
[
'rollback',
'rollback_disabled',
],
indirect=True,
)
@@ -1 +1 @@
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_BY_APP=y
@@ -0,0 +1,2 @@
# Covers the CONFIG_BOOTLOADER_APP_ROLLBACK=n code paths, no longer exercised by any other config.
CONFIG_BOOTLOADER_APP_ROLLBACK=n
+42 -11
View File
@@ -1,20 +1,51 @@
menu "Application Rollback"
config BOOTLOADER_APP_ROLLBACK_ENABLE
bool "Enable app rollback support"
default n
config BOOTLOADER_APP_ROLLBACK
bool "Enable application rollback"
default y
help
After updating the app, the bootloader runs a new app with the "ESP_OTA_IMG_PENDING_VERIFY" state set.
This state prevents the re-run of this app. After the first boot of the new app in the user code, the
function should be called to confirm the operability of the app or vice versa about its non-operability.
If the app is working, then it is marked as valid. Otherwise, it is marked as not valid and rolls back to
the previous working app. A reboot is performed, and the app is booted before the software update.
Note: If during the first boot a new app the power goes out or the WDT works, then roll back will happen.
Rollback is possible only between the apps with the same security versions.
After an OTA update, the bootloader starts the new application in the
ESP_OTA_IMG_PENDING_VERIFY state. If the application resets, crashes, or
loses power before being confirmed valid, the bootloader marks it as
aborted on the next boot and selects the previously working application
instead.
Disabling this option removes the automatic recovery path after a
failed OTA update: a failed update can leave the device with an
unbootable application.
choice BOOTLOADER_APP_ROLLBACK_CONFIRM_MODE
prompt "Rollback confirmation checkpoint"
depends on BOOTLOADER_APP_ROLLBACK
default BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
config BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP
bool "Confirm app automatically during system startup"
help
The application is marked valid near the end of system startup,
immediately before app_main is called. Reaching app_main is treated
as a successful boot; this confirms IDF startup completed but does
not verify application-specific functionality.
Since the state is already ESP_OTA_IMG_VALID by the time app_main
runs, application code cannot use esp_ota_get_state_partition() to
detect the first boot of a new application.
config BOOTLOADER_APP_ROLLBACK_CONFIRM_BY_APP
bool "Application decides the confirmation checkpoint"
help
The application must confirm itself valid during its first boot by
calling esp_ota_mark_app_valid_cancel_rollback(), or reject the
update with esp_ota_mark_app_invalid_rollback_and_reboot(). If
neither is called before a reset, the bootloader marks the app as
aborted and rolls back to the previous working application.
Rollback is possible only between apps with the same security version.
endchoice
config BOOTLOADER_APP_ANTI_ROLLBACK
bool "Enable app anti-rollback support"
depends on BOOTLOADER_APP_ROLLBACK_ENABLE
depends on BOOTLOADER_APP_ROLLBACK_CONFIRM_BY_APP
default n
help
This option prevents rollback to previous firmware/application image with lower security version.
+2 -1
View File
@@ -9,7 +9,8 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO CONFIG_BOOTLOADER_LO
CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG
CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE
CONFIG_APP_ROLLBACK_ENABLE CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
CONFIG_APP_ROLLBACK_ENABLE CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_BY_APP
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_BY_APP
CONFIG_APP_ANTI_ROLLBACK CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK
CONFIG_APP_SECURE_VERSION CONFIG_BOOTLOADER_APP_SECURE_VERSION
CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD CONFIG_BOOTLOADER_APP_SEC_VER_SIZE_EFUSE_FIELD
@@ -390,7 +390,7 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
ESP_LOGD(TAG, "otadata[0]: sequence values 0x%08"PRIx32, otadata[0].ota_seq);
ESP_LOGD(TAG, "otadata[1]: sequence values 0x%08"PRIx32, otadata[1].ota_seq);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
bool write_encrypted = esp_efuse_is_flash_encryption_enabled();
for (int i = 0; i < 2; ++i) {
if (otadata[i].ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
@@ -440,13 +440,13 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
uint32_t ota_seq = otadata[active_otadata].ota_seq - 1; // Raw OTA sequence number. May be more than # of OTA slots
boot_index = ota_seq % bs->app_count; // Actual OTA partition selection
ESP_LOGD(TAG, "Mapping seq %"PRIu32" -> OTA slot %d", ota_seq, boot_index);
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK
if (otadata[active_otadata].ota_state == ESP_OTA_IMG_NEW) {
ESP_LOGD(TAG, "otadata[%d] is selected as new and marked PENDING_VERIFY state", active_otadata);
otadata[active_otadata].ota_state = ESP_OTA_IMG_PENDING_VERIFY;
write_otadata(&otadata[active_otadata], bs->ota_info.offset + FLASH_SECTOR_SIZE * active_otadata, write_encrypted);
}
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#endif // CONFIG_BOOTLOADER_APP_ROLLBACK
#ifdef CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK
if (otadata[active_otadata].ota_state == ESP_OTA_IMG_VALID) {
+7 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -197,6 +197,12 @@ static void main_task(void* args)
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
#endif // CONFIG_ESP_TASK_WDT
// app_update overrides it to auto-confirm an OTA rollback right before app_main.
void __attribute__((weak)) esp_ota_confirm_rollback_hook(void);
if (esp_ota_confirm_rollback_hook != NULL) {
esp_ota_confirm_rollback_hook();
}
/*
Note: Be careful when changing the "Calling app_main()" log below as multiple pytest scripts expect this log as a
start-of-application marker.