mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(app_update): Add auto-confirmation of OTA updates
Enable rollback auto-confirmation by default
This commit is contained in:
committed by
Konstantin Kondrashov
parent
dce5812654
commit
de1e5035b5
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user