fix: confirm successful OTA boot before restart

This commit is contained in:
torlando-agent[bot]
2026-07-24 19:21:56 +00:00
parent 12d25b2acc
commit 0ea2be3fda
4 changed files with 56 additions and 0 deletions
+7
View File
@@ -1312,6 +1312,13 @@ void SettingsScreen::set_lxmf_address(const Bytes& hash) {
_lxmf_address = hash;
}
void SettingsScreen::set_firmware_version(const String& version) {
if (_label_firmware) {
String text = "Firmware: " + version;
lv_label_set_text(_label_firmware, text.c_str());
}
}
void SettingsScreen::set_gps(TinyGPSPlus* gps) {
_gps = gps;
}
+3
View File
@@ -176,6 +176,9 @@ public:
*/
void set_lxmf_address(const RNS::Bytes& hash);
/** Set the exact running firmware build for post-reboot verification. */
void set_firmware_version(const String& version);
/**
* Set GPS pointer for status display
*/
+31
View File
@@ -12,6 +12,7 @@
#include <esp_system.h>
#include <esp_heap_caps.h>
#include <esp_task_wdt.h>
#include <esp_ota_ops.h>
#include <new> // placement new
#include <soc/rtc_cntl_reg.h>
@@ -956,6 +957,30 @@ void setup_hardware() {
INFO("Power enabled (early init)");
}
void confirm_running_firmware() {
const esp_partition_t* running = esp_ota_get_running_partition();
if (!running) {
ERROR("Unable to identify running OTA partition");
return;
}
esp_ota_img_states_t state = ESP_OTA_IMG_UNDEFINED;
esp_err_t state_result = esp_ota_get_state_partition(running, &state);
INFOF("Running firmware: version=%s partition=%s subtype=%d address=0x%lx state=%d",
FIRMWARE_VERSION, running->label, static_cast<int>(running->subtype),
static_cast<unsigned long>(running->address), static_cast<int>(state));
if (state_result == ESP_OK &&
(state == ESP_OTA_IMG_NEW || state == ESP_OTA_IMG_PENDING_VERIFY)) {
esp_err_t result = esp_ota_mark_app_valid_cancel_rollback();
if (result == ESP_OK) {
INFO("Running firmware marked valid; OTA rollback cancelled");
} else {
ERRORF("Failed to mark running firmware valid: %s", esp_err_to_name(result));
}
}
}
void setup_lvgl_and_ui() {
INFO("\n=== LVGL & UI Initialization ===");
@@ -1275,6 +1300,7 @@ void setup_ui_manager() {
// Configure settings screen
UI::LXMF::SettingsScreen* settings = ui_manager->get_settings_screen();
if (settings) {
settings->set_firmware_version(FIRMWARE_VERSION);
// Pass GPS for status display
settings->set_gps(&gps);
@@ -1789,6 +1815,11 @@ void setup() {
Serial.flush();
});
// Columba writes app0 plus fresh OTA-selection data. This framework has
// bootloader rollback enabled, so confirm the app only after filesystem,
// LXMF, and UI initialization have all completed successfully.
confirm_running_firmware();
// Boot profiling complete
BOOT_PROFILE_COMPLETE();
#ifdef BOOT_PROFILING_ENABLED
@@ -7,6 +7,7 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
UI_MANAGER = REPO_ROOT / "lib/tdeck_ui/UI/LXMF/UIManager.cpp"
CHAT_SCREEN = REPO_ROOT / "lib/tdeck_ui/UI/LXMF/ChatScreen.cpp"
COMPOSE_SCREEN = REPO_ROOT / "lib/tdeck_ui/UI/LXMF/ComposeScreen.cpp"
MAIN = REPO_ROOT / "src/main.cpp"
def function_body(source: str, signature: str, next_signature: str) -> str:
@@ -84,3 +85,17 @@ def test_storage_error_dialogs_are_coalesced():
source = UI_MANAGER.read_text()
assert "if (storage_error_dialog) return;" in source
assert "storage_error_dialog = nullptr;" in source
def test_successful_boot_cancels_ota_rollback_after_subsystems_initialize():
source = MAIN.read_text()
confirm = function_body(
source,
"void confirm_running_firmware()",
"void setup_lvgl_and_ui()",
)
assert "ESP_OTA_IMG_PENDING_VERIFY" in confirm
assert "esp_ota_mark_app_valid_cancel_rollback()" in confirm
setup = function_body(source, "void setup()", "void loop()")
assert setup.index("setup_lxmf();") < setup.index("setup_ui_manager();")
assert setup.index("setup_ui_manager();") < setup.index("confirm_running_firmware();")