diff --git a/src/helpers/ota/OtaApply.cpp b/src/helpers/ota/OtaApply.cpp index bce0d6664..0f12e5d2f 100644 --- a/src/helpers/ota/OtaApply.cpp +++ b/src/helpers/ota/OtaApply.cpp @@ -419,6 +419,14 @@ void ota_reboot_to_apply() { esp_restart(); } // boots the slot armed by ota_ap #elif defined(NRF52_PLATFORM) // single-slot: verify + mark APPROVED + hand off to the bootloader +// Capture a real OTAFIX result before board power-management initialization +// consumes GPREGRET2. Ignore shutdown reasons and staging handoff markers that +// share the register but are not bootloader apply results. +static uint8_t g_bootloader_last_rc = 0; +static void __attribute__((constructor(101))) ota_capture_bootloader_last_rc() { + g_bootloader_last_rc = ota_nrf52_boot_result_or_zero((uint8_t)NRF_POWER->GPREGRET2); +} + struct InplacePatchDims { uint32_t memory = 0; uint32_t segment = 0; @@ -465,12 +473,7 @@ void ota_reboot_to_apply() { // public: set the apply magic + NVIC_SystemReset(); // does not return } -uint8_t ota_bootloader_last_rc() { // bootloader's last in-place-apply code, stashed in GPREGRET2 - uint32_t v = 0; - uint8_t en = 0; sd_softdevice_is_enabled(&en); - if (en) sd_power_gpregret_get(1, &v); else v = NRF_POWER->GPREGRET2; - return (uint8_t)v; -} +uint8_t ota_bootloader_last_rc() { return g_bootloader_last_rc; } static bool ota_apply_mota_nrf52_impl(const uint8_t* buf, uint32_t len, const SignerAllowlist& allow, diff --git a/src/helpers/ota/OtaApply.h b/src/helpers/ota/OtaApply.h index ef523fd75..1846bbfce 100644 --- a/src/helpers/ota/OtaApply.h +++ b/src/helpers/ota/OtaApply.h @@ -99,6 +99,11 @@ bool ota_apply_mota_nrf52(OtaStoreQspiNrf52& store, // into the slot already armed by ota_apply_detools_mota. void ota_reboot_to_apply(); +inline uint8_t ota_nrf52_boot_result_or_zero(uint8_t value) { + return ((value >= 0x90u && value <= 0x9Fu) || + (value >= 0xB0u && value <= 0xBCu)) ? value : 0u; +} + // DIAGNOSTIC (nRF52): the bootloader stashes its last in-place-apply bail/progress code in GPREGRET2 (see // ota_delta.c). Read it back so `ota status` can show why an apply didn't take. 0 / other platforms = n/a. uint8_t ota_bootloader_last_rc(); diff --git a/test/test_ota/test_ota_core.cpp b/test/test_ota/test_ota_core.cpp index be1be00f2..e9a9df956 100644 --- a/test/test_ota/test_ota_core.cpp +++ b/test/test_ota/test_ota_core.cpp @@ -23,6 +23,21 @@ extern "C" { using namespace mesh::ota; +TEST(OtaBootResult, AcceptsOnlyOtafixApplyDiagnostics) { + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x90), 0x90); + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x9F), 0x9F); + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xB0), 0xB0); + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xB8), 0xB8); + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xBC), 0xBC); + + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x00), 0x00); + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x4C), 0x00); // shutdown reason + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x51), 0x00); // QSPI handoff + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xD4), 0x00); // legacy handoff + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xED), 0x00); // expanded handoff + EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xBD), 0x00); +} + class FakeMotaSeederStream : public Stream { public: using Stream::write;