1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[safe_mode] Detect bootloader rollback support at runtime (#13230)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Swoboda
2026-01-15 14:17:00 -05:00
committed by GitHub
parent 9003844eda
commit 0dc5a7c9a4
2 changed files with 27 additions and 3 deletions

View File

@@ -9,7 +9,7 @@
#include <cinttypes> #include <cinttypes>
#include <cstdio> #include <cstdio>
#ifdef USE_OTA_ROLLBACK #if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#endif #endif
@@ -26,6 +26,17 @@ void SafeModeComponent::dump_config() {
this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds
this->safe_mode_num_attempts_, this->safe_mode_num_attempts_,
this->safe_mode_enable_time_ / 1000); // because milliseconds this->safe_mode_enable_time_ / 1000); // because milliseconds
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
const char *state_str;
if (this->ota_state_ == ESP_OTA_IMG_NEW) {
state_str = "not supported";
} else if (this->ota_state_ == ESP_OTA_IMG_PENDING_VERIFY) {
state_str = "supported";
} else {
state_str = "support unknown";
}
ESP_LOGCONFIG(TAG, " Bootloader rollback: %s", state_str);
#endif
if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) {
auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_; auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_;
@@ -36,7 +47,7 @@ void SafeModeComponent::dump_config() {
} }
} }
#ifdef USE_OTA_ROLLBACK #if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
const esp_partition_t *last_invalid = esp_ota_get_last_invalid_partition(); const esp_partition_t *last_invalid = esp_ota_get_last_invalid_partition();
if (last_invalid != nullptr) { if (last_invalid != nullptr) {
ESP_LOGW(TAG, ESP_LOGW(TAG,
@@ -55,7 +66,7 @@ void SafeModeComponent::loop() {
ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter"); ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter");
this->clean_rtc(); this->clean_rtc();
this->boot_successful_ = true; this->boot_successful_ = true;
#ifdef USE_OTA_ROLLBACK #if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
// Mark OTA partition as valid to prevent rollback // Mark OTA partition as valid to prevent rollback
esp_ota_mark_app_valid_cancel_rollback(); esp_ota_mark_app_valid_cancel_rollback();
#endif #endif
@@ -90,6 +101,12 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en
this->safe_mode_num_attempts_ = num_attempts; this->safe_mode_num_attempts_ = num_attempts;
this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false); this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
// Check partition state to detect if bootloader supports rollback
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_get_state_partition(running, &this->ota_state_);
#endif
uint32_t rtc_val = this->read_rtc_(); uint32_t rtc_val = this->read_rtc_();
this->safe_mode_rtc_value_ = rtc_val; this->safe_mode_rtc_value_ = rtc_val;

View File

@@ -5,6 +5,10 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
#include <esp_ota_ops.h>
#endif
namespace esphome::safe_mode { namespace esphome::safe_mode {
/// SafeModeComponent provides a safe way to recover from repeated boot failures /// SafeModeComponent provides a safe way to recover from repeated boot failures
@@ -42,6 +46,9 @@ class SafeModeComponent : public Component {
// Group 1-byte members together to minimize padding // Group 1-byte members together to minimize padding
bool boot_successful_{false}; ///< set to true after boot is considered successful bool boot_successful_{false}; ///< set to true after boot is considered successful
uint8_t safe_mode_num_attempts_{0}; uint8_t safe_mode_num_attempts_{0};
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
esp_ota_img_states_t ota_state_{ESP_OTA_IMG_UNDEFINED};
#endif
// Larger objects at the end // Larger objects at the end
ESPPreferenceObject rtc_; ESPPreferenceObject rtc_;
#ifdef USE_SAFE_MODE_CALLBACK #ifdef USE_SAFE_MODE_CALLBACK