From ea40663f0967862143ccaa25ffd360d7aeed6ade Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sun, 27 Sep 2026 15:07:29 -0700 Subject: [PATCH] Revert adaptive RAK app-owned bootloader recovery after ACL test --- docs/ota_nrf52_bootloader_update.md | 46 +----- platformio.ini | 1 - src/helpers/ota/OtaApply.cpp | 183 --------------------- src/helpers/ota/OtaApply.h | 15 -- src/helpers/ota/OtaBlInfo.h | 3 - src/helpers/ota/OtaBootloaderUpdate.h | 27 +-- src/helpers/ota/OtaCli.cpp | 40 +---- src/helpers/ota/OtaContext.h | 21 --- src/helpers/ota/OtaFlashLayout_nrf52.h | 1 - src/helpers/ota/OtaStoreFlashNrf52.cpp | 34 +--- src/helpers/ota/OtaStoreQspiNrf52.cpp | 28 +--- test/test_ota/test_ota_core.cpp | 34 ---- tools/lora_ota/lora_ota.py | 18 +- tools/lora_ota/test_lora_ota_bootloader.py | 32 +--- tools/mota/README.md | 11 +- tools/mota/motalib.py | 36 +--- tools/mota/test_mota.py | 40 +---- 17 files changed, 49 insertions(+), 521 deletions(-) diff --git a/docs/ota_nrf52_bootloader_update.md b/docs/ota_nrf52_bootloader_update.md index a6f3865d8..718a30e60 100644 --- a/docs/ota_nrf52_bootloader_update.md +++ b/docs/ota_nrf52_bootloader_update.md @@ -16,14 +16,14 @@ be replaced with the exact ABI-3 self-update-capable OTAFIX build over USB/BLE DFU or SWD. MeshTower SD also requires the BLM2 retained-auth version to be provisioned locally before either application or bootloader OTA. -The adaptive RAK3401 and RAK4631 loaders are an exception: their 2.4.8 -application updater has ABI 2 and capability flags `0x16`, with no bootloader -self-update code. The two `*_repeater_unified_lora_ota` MeshCore builds now -include an application-owned recovery installer. Install that application -through its normal exact-base application mOTA path first. It can then fetch -an exact-board signed format-3 bootloader package from internal flash or a -matched external NOR and invoke Nordic's MBR itself. A node that cannot run -MeshCore and receive LoRa cannot use this path; use local UF2/DFU or SWD. +The adaptive RAK3401 and RAK4631 OTAFIX 2.4.8 profiles support application +full and delta updates, but not bootloader self-update. Their running +bootloader protects `0xF4000..0x100000` with the nRF52840 ACL before starting +MeshCore. This includes the MBR parameter page at `0xFE000`, so an application +cannot initiate `COPY_BL` even after verifying a signed bootloader package. +Hardware testing on a RAK3401 confirmed the MBR SVC enters a bus fault and +leaves the installed bootloader unchanged. Migrate these profiles with their +exact-board UF2 or local DFU package; do not offer a bootloader mOTA for them. ## Storage layouts @@ -32,36 +32,6 @@ MeshCore and receive LoRa cannot use this path; use local UF2/DFU or SWD. | XIAO-module raw QSPI | below `0xE0000` | external QSPI offset 0 | dedicated internal `0xE0000..0xEA000` scratch | source `0x51`, flags `0x0E` | | Qualified internal-flash target | normal `0xED000` flash limit; top 64 KiB SRAM reserved for application-delta staging | shared internal slot, exact start `0xE2000` | the same eleven-page slot; no second flash reservation | source `0xED`, flags `0x0A` | | MeshTower V2 microSD | normal `0xED000` limit | contiguous `/meshcore-ota.mota` | dynamic internal `0xE0000..0xEA000` scratch; live image must end by `0xE0000` | source `0x53`, flags `0x09` | -| Adaptive RAK3401/RAK4631 recovery application | normal `0xED000` flash limit; live EndF and bank CRC must stop by `0xE2000` | internal `0xE2000` slot or exact matched NOR | internal `0xE2000..0xEC000` raw copy | application calls MBR; installed loader flags `0x16` | - -For adaptive RAK recovery, `ota bootloader status` reports the installed -`3401_AUTO_DFU` or `4631_AUTO_DFU` identity. The host must build a signed -format-3 package from a *newer* exact-board OTAFIX image with the same `0x16` -capability marker and BLM2 continuity metadata. The package is full, so it -does not depend on the installed bootloader bytes. Its target IDs are -`D04AB3AB` and `FEEAFD1B`, respectively. The application requires a trusted -signer, matching board name/ID and SoftDevice layout, complete Merkle and -SHA-256 verification, explicit MID and image-hash confirmation, and a -hash-valid live EndF plus safe bank settings before writing scratch. It -copies each page with readback, hashes the final source again after the LoRa -reply drains, then calls `SD_MBR_COMMAND_COPY_BL`. The old bootloader remains -untouched until the MBR call. An installed 2.4.8 BLM2 loader rejects an equal -or older version; a 2.4.8 image is therefore not a recovery upgrade for it. -If scratch verification or the MBR call fails, the application resets without -the bootloader-update trigger and reports `blup:CE` on its next OTA status. -Build a candidate only from the matching **newer** OTAFIX bootloader HEX: - -```bash -motatool build-bootloader --fw \ - --board wiscore_rak3401_auto --sign \ - --out -# For RAK4631 use --board wiscore_rak4631_auto and its own exact-board HEX. -``` - -Verify the signed file with the corresponding public key, serve it over LoRa, -pull its MID manually, and run the same `ota bootloader install ` -confirmation shown below. A temporary or untrusted test signing key cannot -install on a node whose allowlist does not contain its public key. The exact SD target is `Heltec_tower_v2_sdcard_repeater_lora_ota_no_external_sensors`. Its normal diff --git a/platformio.ini b/platformio.ini index d1e35253b..6f6ceca47 100644 --- a/platformio.ini +++ b/platformio.ini @@ -271,7 +271,6 @@ build_flags = [nrf52_wisblock_auto_ota] build_flags = -D OTA_RAK_AUTO_STORE=1 - -D OTA_RAK_BOOTLOADER_RECOVERY=1 -D OTA_QSPI_STORE=1 -D OTA_FLASH_STORE=1 -D OTA_QSPI_SHARED_WISBLOCK_SPI=1 diff --git a/src/helpers/ota/OtaApply.cpp b/src/helpers/ota/OtaApply.cpp index fba5b500a..abf73f9f2 100644 --- a/src/helpers/ota/OtaApply.cpp +++ b/src/helpers/ota/OtaApply.cpp @@ -18,7 +18,6 @@ #include "OtaSelf.h" // SelfFwInfo / ota_self_firmware (running-image base_hash gate) #endif #elif defined(NRF52_PLATFORM) - #include #include "OtaVerify.h" #include "OtaSelf.h" #include "OtaFlashLayout_nrf52.h" @@ -27,9 +26,7 @@ #include "nrf.h" #include "nrf_soc.h" #include "nrf_sdm.h" - #include "nrf_mbr.h" #include "../nrf52/SoftDeviceState.h" - #include #if defined(OTA_SD_STORE) #include "OtaStoreSdNrf52.h" #endif @@ -471,31 +468,6 @@ static OtaStoreFlashNrf52* g_nrf52_apply_store = nullptr; #if defined(OTA_QSPI_STORE) static uint8_t g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_QSPI; #endif -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) -// Set only after the exact-board signed image has been copied to the internal -// source and independently hashed there. Mesh drains the LoRa reply first. -static bool g_rak_recovery_ready = false; -static uint8_t g_rak_recovery_hash[32] = {0}; - -static bool ota_rak_recovery_source_hash_matches() { - SHA256 sha; - const uint8_t* image = (const uint8_t*)(uintptr_t)MOTA_NRF52_SHARED_BOOT_STAGE_START; - for (uint32_t off = 0; off < OTA_BOOT_IMAGE_SIZE; off += 512u) - sha.update(image + off, 512u); - uint8_t hash[32]; - sha.finalize(hash, sizeof(hash)); - return memcmp(hash, g_rak_recovery_hash, sizeof(hash)) == 0; -} - -static bool ota_rak_mbr_addresses_ready() { - const uint32_t mbr_boot = *(const volatile uint32_t*)(uintptr_t)MBR_BOOTLOADER_ADDR; - const uint32_t mbr_params = *(const volatile uint32_t*)(uintptr_t)MBR_PARAM_PAGE_ADDR; - const uint32_t boot = mbr_boot == UINT32_MAX ? NRF_UICR->NRFFW[0] : mbr_boot; - const uint32_t params = mbr_params == UINT32_MAX ? NRF_UICR->NRFFW[1] : mbr_params; - return boot == OTA_BOOT_IMAGE_START && - params == OTA_BOOT_IMAGE_START + OTA_BOOT_IMAGE_SIZE; -} -#endif static void ota_nrf52_set_reset_handoff(uint8_t request, uint8_t source) { uint8_t sd_en = 0; @@ -568,40 +540,6 @@ void ota_reboot_to_apply() { // public: set the apply magic + } void ota_reboot_to_bootloader_update() { -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - if (g_rak_recovery_ready) { - // COPY_BL succeeds by resetting directly into the new bootloader. Never - // hand 0x6B to an older adaptive loader: it cannot consume this package. - if (!ota_rak_mbr_addresses_ready() || - !ota_rak_recovery_source_hash_matches()) { - g_rak_recovery_ready = false; - ota_nrf52_set_reset_handoff(0u, GPREGRET2_RAK_RECOVERY_FAILED); - NVIC_SystemReset(); - return; - } - uint8_t sd_enabled = 0; - if (mesh_nrf52::softdeviceIsEnabled(sd_enabled) != NRF_SUCCESS || - (sd_enabled && sd_softdevice_disable() != NRF_SUCCESS)) { - g_rak_recovery_ready = false; - ota_nrf52_set_reset_handoff(0u, GPREGRET2_RAK_RECOVERY_FAILED); - NVIC_SystemReset(); - return; - } - sd_mbr_command_t command = {}; - command.command = SD_MBR_COMMAND_COPY_BL; - command.params.copy_bl.bl_src = - (uint32_t*)(uintptr_t)MOTA_NRF52_SHARED_BOOT_STAGE_START; - command.params.copy_bl.bl_len = OTA_BOOT_IMAGE_SIZE / sizeof(uint32_t); - NRF_POWER->GPREGRET = 0u; - NRF_POWER->GPREGRET2 = 0u; - (void)sd_mbr_command(&command); // success never returns - g_rak_recovery_ready = false; - NRF_POWER->GPREGRET = 0u; - NRF_POWER->GPREGRET2 = GPREGRET2_RAK_RECOVERY_FAILED; - NVIC_SystemReset(); - return; - } -#endif uint8_t source = GPREGRET2_OTA_STAGE_QSPI; #if defined(OTA_INTERNAL_BOOTLOADER_UPDATE) source = GPREGRET2_OTA_STAGE_EXPANDED; @@ -1076,127 +1014,6 @@ static const char* ota_bootloader_continuity_error(OtaBootloaderContinuityGate g } } -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) -bool ota_prepare_rak_bootloader_recovery(OtaStore& store, bool internal, - const SignerAllowlist& allow, - const OtaBootloaderIdentity& installed, - const uint8_t actual_mid[4], - const uint8_t operator_mid[4], - const uint8_t operator_hash8[8], - ApplyState& st, char* msg) { - st = ApplyState(); - g_rak_recovery_ready = false; - const uint32_t total = store.staged_size(); - uint8_t header[8], manifest[MOTA_MFL]; - MotaManifest m; - if (total != MOTA_NRF52_BOOT_CONTAINER_SIZE || - !store.read(0, header, sizeof(header)) || - memcmp(header, MOTA_MAGIC, sizeof(MOTA_MAGIC)) != 0 || - rd_u32le(header + 4) != total || - !store.read(8, manifest, sizeof(manifest)) || - !mota_parse_manifest(manifest, sizeof(manifest), m)) { - strcpy(msg, "recovery bootloader container is invalid"); return false; - } - if (ota_bootloader_confirmation_gate(m, installed, actual_mid, operator_mid, - operator_hash8) != OTA_BOOT_CONFIRM_OK) { - strcpy(msg, "recovery bootloader identity/MID/hash confirmation failed"); return false; - } - const OtaBlCaps current = ota_bootloader_app_caps(); - const uint8_t rak_flags = OTA_BL_PROFILE_RAK_AUTO; - if (!current.present || current.storage_flags != rak_flags || - current.apply_abi < MOTA_APP_FORMAT_VER || - (current.codec_mask & OTA_BL_REQUIRED_APP_CODEC_MASK) != - OTA_BL_REQUIRED_APP_CODEC_MASK) { - strcpy(msg, "installed bootloader is not the adaptive RAK OTAFIX profile"); return false; - } - SelfFwInfo fi; - const uint32_t app_base = mota_nrf52_app_base(); - if (!ota_self_firmware(fi) || - !ota_bootloader_scratch_headroom_valid( - fi.valid, app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) || - !ota_bootloader_live_bank_preserves_scratch( - app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) || - ota_nrf52_effective_stage_ceiling() != MOTA_NRF52_APP_END || - !ota_rak_mbr_addresses_ready()) { - strcpy(msg, "running app/settings or MBR do not preserve bootloader scratch"); return false; - } - if (internal) { - OtaStoreFlashNrf52& flash = static_cast(store); - if (flash.write_start() != MOTA_NRF52_SHARED_BOOT_STAGE_START || - !flash.data() || flash.is_hybrid()) { - strcpy(msg, "internal recovery container placement is invalid"); return false; - } - } - const uint32_t payload_off = 8u + MOTA_MFL + (uint32_t)m.block_count * 4u; - if (payload_off != 365u || payload_off + OTA_BOOT_IMAGE_SIZE + 5u != total) { - strcpy(msg, "recovery payload layout is invalid"); return false; - } - - mesh::Identity signer(m.signer_pubkey); - if (!signer.verify(m.signature, m.manifest_start, (int)m.signed_len) || - !allow.contains(m.signer_pubkey)) { - strcpy(msg, "recovery bootloader signer is not trusted"); return false; - } - const VerifyResult verified = ota_verify(store, allow, manifest); - st.manifest_ok = verified.parsed; - st.sig_ok = verified.sig_ok; - st.trusted = verified.trusted; - st.image_size = m.image_size; - memcpy(st.image_hash, m.image_hash, sizeof(st.image_hash)); - if (!verified.auto_appliable() || !verified.container_hash_ok) { - strcpy(msg, "recovery bootloader package failed full verification"); return false; - } - uint8_t vectors[8]; - OtaBootloaderIdentity candidate; - OtaBootloaderCapsMarker candidate_caps; - if (!store.read(payload_off, vectors, sizeof(vectors)) || - !ota_bootloader_vector_sane(vectors) || - !ota_bootloader_external_image_metadata( - store, payload_off, rak_flags, candidate, candidate_caps, false) || - !ota_bootloader_identity_matches(installed, candidate) || - candidate_caps.apply_abi < current.apply_abi || - (candidate_caps.codec_mask & current.codec_mask) != current.codec_mask) { - strcpy(msg, "recovery candidate identity/capabilities/CRC mismatch"); return false; - } - const OtaBootloaderContinuityGate continuity = ota_bootloader_continuity_gate( - installed, candidate, m.fw_version, OTA_BOOT_CONTINUITY_FAMILY_S140, - ota_runtime_softdevice_fwid(), app_base, OTA_BOOT_CONTINUITY_LAYOUT_ABI); - if (continuity != OTA_BOOT_CONTINUITY_OK) { - strncpy(msg, ota_bootloader_continuity_error(continuity), 96); - msg[95] = 0; return false; - } - - uint8_t* page = static_cast(malloc(MOTA_NRF52_FLASH_PAGE)); - if (!page) { strcpy(msg, "not enough RAM for recovery copy"); return false; } - bool copied = true; - for (uint32_t off = 0; off < OTA_BOOT_IMAGE_SIZE; off += MOTA_NRF52_FLASH_PAGE) { - const uint32_t dest = MOTA_NRF52_SHARED_BOOT_STAGE_START + off; - // Internal packages overlap the destination by 365 bytes. Read the next - // complete source page before erasing the lower destination page. - if (!store.read(payload_off + off, page, MOTA_NRF52_FLASH_PAGE) || - flash_nrf5x_write(dest, page, MOTA_NRF52_FLASH_PAGE) < 0) { - copied = false; break; - } - flash_nrf5x_flush(); - if (memcmp((const void*)(uintptr_t)dest, page, MOTA_NRF52_FLASH_PAGE) != 0) { - copied = false; break; - } - } - free(page); - if (!copied) { - strcpy(msg, "recovery scratch copy/readback failed; bootloader unchanged"); return false; - } - memcpy(g_rak_recovery_hash, m.image_hash, sizeof(g_rak_recovery_hash)); - if (!ota_rak_recovery_source_hash_matches()) { - strcpy(msg, "recovery scratch hash mismatch; bootloader unchanged"); return false; - } - st.slot_ok = true; - g_rak_recovery_ready = true; - strcpy(msg, "signed bootloader copied and verified; MBR install follows this reply"); - return true; -} -#endif - #if defined(OTA_SD_BOOTLOADER_UPDATE) static bool external_bootloader_approve(OtaStoreSdNrf52& store, const uint8_t image_hash[32], diff --git a/src/helpers/ota/OtaApply.h b/src/helpers/ota/OtaApply.h index 37bb83086..6da291377 100644 --- a/src/helpers/ota/OtaApply.h +++ b/src/helpers/ota/OtaApply.h @@ -15,8 +15,6 @@ namespace mesh { namespace ota { -class OtaStore; - struct ApplyState { bool manifest_ok = false; bool sig_ok = false; @@ -133,19 +131,6 @@ bool ota_prepare_bootloader_update_nrf52(OtaStoreFlashNrf52& store, ApplyState& st, char* msg); #endif -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) -// Application-owned recovery for adaptive RAK loaders lacking BOOT_UPDATE. -// Copies a verified full bootloader from either active store into internal -// scratch; deferred commit invokes Nordic's MBR after the LoRa reply drains. -bool ota_prepare_rak_bootloader_recovery(OtaStore& store, bool internal, - const SignerAllowlist& allow, - const OtaBootloaderIdentity& installed, - const uint8_t actual_mid[4], - const uint8_t operator_mid[4], - const uint8_t operator_hash8[8], - ApplyState& st, char* msg); -#endif - // Commit the (already approved/armed) update and reboot into it - does NOT return. Call this only after // a successful ota_apply_* AND after the confirmation reply has been delivered, so the operator knows // the apply started (over LoRa the device then goes silent while the bootloader applies). nRF52: set diff --git a/src/helpers/ota/OtaBlInfo.h b/src/helpers/ota/OtaBlInfo.h index c3192595f..80199cdf6 100644 --- a/src/helpers/ota/OtaBlInfo.h +++ b/src/helpers/ota/OtaBlInfo.h @@ -93,9 +93,6 @@ static const uint8_t OTA_BL_PROFILE_INTERNAL_BOOT_UPDATE = static const uint8_t OTA_BL_PROFILE_QSPI_BOOT_UPDATE = OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_QSPI | OTA_BL_STORAGE_BOOT_UPDATE; -static const uint8_t OTA_BL_PROFILE_RAK_AUTO = - OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_QSPI | - OTA_BL_STORAGE_HEADER_W25; // A successor must retain both application update paths used by qualified // external stores: bit 0 CODEC_FULL and bit 2 CODEC_DETOOLS_INPLACE. static const uint16_t OTA_BL_REQUIRED_APP_CODEC_MASK = 0x0005u; diff --git a/src/helpers/ota/OtaBootloaderUpdate.h b/src/helpers/ota/OtaBootloaderUpdate.h index b0d26a0d2..a5adfccb0 100644 --- a/src/helpers/ota/OtaBootloaderUpdate.h +++ b/src/helpers/ota/OtaBootloaderUpdate.h @@ -32,17 +32,6 @@ #endif #endif -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - #if !defined(NRF52_PLATFORM) || !defined(OTA_RAK_AUTO_STORE) || \ - !defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || !defined(OTA_FLASH_STORE) || \ - !defined(OTA_QSPI_STORE) - #error "RAK application recovery requires the adaptive nRF52 shared-slot profile" - #endif - #if !defined(RAK_3401) && !defined(RAK_4631) - #error "RAK application recovery is restricted to the two qualified RAK profiles" - #endif -#endif - #if defined(OTA_SD_BOOTLOADER_UPDATE) #if !defined(NRF52_PLATFORM) || !defined(OTA_SD_STORE) #error "OTA_SD_BOOTLOADER_UPDATE requires nRF52 SD staging" @@ -523,19 +512,17 @@ inline bool ota_bootloader_caps_marker_parse(const uint8_t raw[16], inline bool ota_bootloader_caps_from_image(const uint8_t* image, size_t image_size, uint8_t exact_storage_flags, - OtaBootloaderCapsMarker& out, - bool require_boot_update = true) { + OtaBootloaderCapsMarker& out) { out = OtaBootloaderCapsMarker(); if (!image || exact_storage_flags == 0) return false; uint8_t valid_count = 0; for (size_t off = 0; off + 16u <= image_size; off += 4u) { OtaBootloaderCapsMarker parsed; if (!ota_bootloader_caps_marker_parse(image + off, parsed) || - parsed.apply_abi < (require_boot_update ? MOTA_BOOT_FORMAT_VER : MOTA_APP_FORMAT_VER) || + parsed.apply_abi < MOTA_BOOT_FORMAT_VER || (parsed.codec_mask & OTA_BL_REQUIRED_APP_CODEC_MASK) != OTA_BL_REQUIRED_APP_CODEC_MASK || - ((parsed.storage_flags & OTA_BL_STORAGE_BOOT_UPDATE) != 0u) != - require_boot_update) continue; + (parsed.storage_flags & OTA_BL_STORAGE_BOOT_UPDATE) == 0) continue; if (++valid_count != 1u) return false; // privileged marker identity must be unambiguous if (parsed.storage_flags != exact_storage_flags) continue; out = parsed; @@ -569,8 +556,7 @@ inline bool ota_bootloader_external_crc_ok( template inline bool ota_bootloader_external_image_metadata( Store& store, uint32_t payload_off, uint8_t exact_storage_flags, - OtaBootloaderIdentity& candidate, OtaBootloaderCapsMarker& caps, - bool require_boot_update = true) { + OtaBootloaderIdentity& candidate, OtaBootloaderCapsMarker& caps) { candidate = OtaBootloaderIdentity(); caps = OtaBootloaderCapsMarker(); if (exact_storage_flags == 0u) return false; @@ -590,11 +576,10 @@ inline bool ota_bootloader_external_image_metadata( if ((absolute & 3u) == 0u && local + 16u <= len) { OtaBootloaderCapsMarker parsed; if (ota_bootloader_caps_marker_parse(buf + local, parsed) && - parsed.apply_abi >= (require_boot_update ? MOTA_BOOT_FORMAT_VER : MOTA_APP_FORMAT_VER) && + parsed.apply_abi >= MOTA_BOOT_FORMAT_VER && (parsed.codec_mask & OTA_BL_REQUIRED_APP_CODEC_MASK) == OTA_BL_REQUIRED_APP_CODEC_MASK && - ((parsed.storage_flags & OTA_BL_STORAGE_BOOT_UPDATE) != 0u) == - require_boot_update) { + (parsed.storage_flags & OTA_BL_STORAGE_BOOT_UPDATE) != 0u) { if (++valid_caps != 1u) return false; if (parsed.storage_flags == exact_storage_flags) caps = parsed; } diff --git a/src/helpers/ota/OtaCli.cpp b/src/helpers/ota/OtaCli.cpp index e7d4bdd94..9d4d5f61c 100644 --- a/src/helpers/ota/OtaCli.cpp +++ b/src/helpers/ota/OtaCli.cpp @@ -400,17 +400,11 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board const OtaBootloaderIdentity& bid = c.bootloaderIdentity(); installable = h->flags == (MFLAG_FULL | MFLAG_SIGNED | MFLAG_BOOTLOADER) && h->codec == CODEC_FULL && bid.present && bid.crc_ok && - h->target_id == ota_bootloader_target_id(bid); -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - installable = installable && - list_bl.storage_flags == OTA_BL_PROFILE_RAK_AUTO && - (c.fetch_store.usesExternal() || c.fetch_store.usesInternal()); -#else - installable = installable && ota_bootloader_self_update_caps_valid(list_bl_update); + h->target_id == ota_bootloader_target_id(bid) && + ota_bootloader_self_update_caps_valid(list_bl_update); #if defined(OTA_SD_BOOTLOADER_UPDATE) installable = installable && list_sd_headroom; #endif -#endif #endif fit = installable ? "yours" : "bootloader unsupported"; } else if (myt && h->target_id == myt) { @@ -547,11 +541,7 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board (defined(OTA_QSPI_BOOTLOADER_UPDATE) || defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || \ defined(OTA_SD_BOOTLOADER_UPDATE)) const OtaBootloaderIdentity& bid = c.bootloaderIdentity(); -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - const OtaBlCaps& bl = c.bootloaderAppCaps(); -#else const OtaBlCaps& bl = c.bootloaderUpdateCaps(); -#endif if (selflags != (MFLAG_FULL | MFLAG_SIGNED | MFLAG_BOOTLOADER) || selcodec != CODEC_FULL) { strcpy(reply, "ERR malformed bootloader catalog row; capture it to folder for inspection"); @@ -561,17 +551,10 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board strcpy(reply, "ERR bootloader package does not match this installed bootloader identity"); return true; } -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - if (bl.storage_flags != OTA_BL_PROFILE_RAK_AUTO) { - strcpy(reply, "ERR installed bootloader is not the adaptive RAK profile"); - return true; - } -#else if (!ota_bootloader_self_update_caps_valid(bl)) { strcpy(reply, "ERR installed bootloader lacks safe LoRa bootloader-update support"); return true; } -#endif #if defined(OTA_SD_BOOTLOADER_UPDATE) SelfFwInfo sd_self; if (!ota_self_firmware(sd_self) || @@ -596,21 +579,13 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board return true; } #if defined(NRF52_PLATFORM) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - const OtaBlCaps& bl = c.bootloaderAppCaps(); -#else const OtaBlCaps& bl = selboot ? c.bootloaderUpdateCaps() : c.bootloaderAppCaps(); -#endif if (!bl.present) { strcpy(reply, "ERR bootloader has no mOTA apply support; update it over USB first"); return true; } -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - const uint8_t need_abi = MOTA_APP_FORMAT_VER; -#else const uint8_t need_abi = selboot ? MOTA_BOOT_FORMAT_VER : MOTA_APP_FORMAT_VER; -#endif if (bl.apply_abi < need_abi || selcodec >= 16 || !(bl.codec_mask & (1u << selcodec))) { snprintf(reply, 160, "ERR bootloader cannot apply mOTA ABI %u codec %u (has abi=%u codecs=0x%x)", need_abi, (unsigned)selcodec, bl.apply_abi, bl.codec_mask); @@ -883,27 +858,16 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board (defined(OTA_QSPI_BOOTLOADER_UPDATE) || defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || \ defined(OTA_SD_BOOTLOADER_UPDATE)) const OtaBootloaderIdentity& bid = c.bootloaderIdentity(); -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - const OtaBlCaps& bl = c.bootloaderAppCaps(); -#else const OtaBlCaps& bl = c.bootloaderUpdateCaps(); -#endif if (*rest == 0 || strcmp(rest, "status") == 0) { if (!bid.present || !bid.crc_ok) { strcpy(reply, "Bootloader update unavailable: installed embedded manifest/CRC is invalid"); return true; } -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - if (bl.storage_flags != OTA_BL_PROFILE_RAK_AUTO) { - strcpy(reply, "Bootloader recovery unavailable: installed loader is not adaptive RAK OTAFIX"); - return true; - } -#else if (!ota_bootloader_self_update_caps_valid(bl)) { strcpy(reply, "Bootloader update unavailable: installed bootloader supports application OTA only"); return true; } -#endif MotaManifest staged; char midhx[9] = "-", hashhx[17] = "-"; const bool ready = c.manager.fetchState() == OtaManager::COMPLETE && diff --git a/src/helpers/ota/OtaContext.h b/src/helpers/ota/OtaContext.h index dccbcd65e..c0cf8a048 100644 --- a/src/helpers/ota/OtaContext.h +++ b/src/helpers/ota/OtaContext.h @@ -359,19 +359,6 @@ struct OtaContext { } const OtaBootloaderIdentity& installed = bootloaderIdentity(); #if defined(OTA_RAK_AUTO_STORE) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - if (!fetch_store.usesInternal() && !fetch_store.usesExternal()) { - strncpy(msg, fetch_store.selectionReason(), 96); - msg[95] = 0; return false; - } - const bool internal = fetch_store.usesInternal(); - OtaStore& recovery_store = internal - ? static_cast(fetch_store.internalStore()) - : static_cast(fetch_store.externalStore()); - bool ok = ota_prepare_rak_bootloader_recovery( - recovery_store, internal, allow, installed, - manager.fetchManifestId(), operator_mid, operator_hash8, apply_st, msg); -#else if (!fetch_store.usesInternal()) { strncpy(msg, "bootloader LoRa update needs the internal OTAFIX profile", 96); msg[95] = 0; return false; @@ -379,7 +366,6 @@ struct OtaContext { bool ok = ota_prepare_bootloader_update_nrf52( fetch_store.internalStore(), allow, installed, manager.fetchManifestId(), operator_mid, operator_hash8, apply_st, msg); -#endif #else bool ok = ota_prepare_bootloader_update_nrf52( fetch_store, allow, installed, manager.fetchManifestId(), operator_mid, @@ -718,15 +704,8 @@ struct OtaContext { (defined(OTA_QSPI_BOOTLOADER_UPDATE) || defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || \ defined(OTA_SD_BOOTLOADER_UPDATE)) #if defined(OTA_RAK_AUTO_STORE) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - manager.set_accept_bootloader( - (fetch_store.usesInternal() || fetch_store.usesExternal()) && - bootloaderIdentity().crc_ok && - bootloaderAppCaps().storage_flags == OTA_BL_PROFILE_RAK_AUTO); -#else manager.set_accept_bootloader(fetch_store.usesInternal() && ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps())); -#endif #else manager.set_accept_bootloader(true); #endif diff --git a/src/helpers/ota/OtaFlashLayout_nrf52.h b/src/helpers/ota/OtaFlashLayout_nrf52.h index 8d1b2dd7b..a167bfd51 100644 --- a/src/helpers/ota/OtaFlashLayout_nrf52.h +++ b/src/helpers/ota/OtaFlashLayout_nrf52.h @@ -52,7 +52,6 @@ static const uint32_t MOTA_NRF52_FLASH_PAGE = 4096u; static const uint32_t MOTA_NRF52_CONTAINER_MIN_SIZE = 8u + 197u + 5u; static const uint8_t GPREGRET_OTA_APPLY = 0x6Au; // distinct from DFU magics 0x57/0x4E/0xA8 static const uint8_t GPREGRET_OTA_BOOTLOADER_UPDATE = 0x6Bu; -static const uint8_t GPREGRET2_RAK_RECOVERY_FAILED = 0xCEu; static const uint8_t GPREGRET2_OTA_STAGE_LEGACY = 0xD4u; static const uint8_t GPREGRET2_OTA_STAGE_EXPANDED = 0xEDu; static const uint8_t GPREGRET2_OTA_STAGE_QSPI = 0x51u; diff --git a/src/helpers/ota/OtaStoreFlashNrf52.cpp b/src/helpers/ota/OtaStoreFlashNrf52.cpp index 9fda8236e..7d32dac5d 100644 --- a/src/helpers/ota/OtaStoreFlashNrf52.cpp +++ b/src/helpers/ota/OtaStoreFlashNrf52.cpp @@ -4,7 +4,6 @@ #include "OtaSelf.h" #include "OtaBlInfo.h" -#include "OtaBootloaderUpdate.h" #include "MotaContainer.h" #include "OtaDebug.h" #include "OtaByteIO.h" // align_down / rd_u32le (flash-page geometry + header read) @@ -155,11 +154,7 @@ bool OtaStoreFlashNrf52::plan_layout(bool is_full, uint32_t image_size, #if defined(OTA_INTERNAL_BOOTLOADER_UPDATE) if (!is_full || image_size != 40u * 1024u || payload_size != 40u * 1024u || payload_off != 365u || total64 != MOTA_NRF52_BOOT_CONTAINER_SIZE || - !(ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps()) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - || ota_bootloader_app_caps().storage_flags == OTA_BL_PROFILE_RAK_AUTO -#endif - )) + !ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps())) return false; // This is the release-blocking no-EndF gate: package kind is known before @@ -170,12 +165,7 @@ bool OtaStoreFlashNrf52::plan_layout(bool is_full, uint32_t image_size, const uint32_t app_base = mota_nrf52_app_base(); if (!ota_self_firmware(fi) || !fi.valid || fi.image_len > UINT32_MAX - app_base || !mota_nrf52_shared_boot_stage_plan( - (uint32_t)total64, app_base, true, app_base + fi.image_len, start) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - || !ota_bootloader_live_bank_preserves_scratch( - app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) -#endif - ) + (uint32_t)total64, app_base, true, app_base + fi.image_len, start)) return false; _planned_bootloader = true; _planned_total = (uint32_t)total64; @@ -355,12 +345,7 @@ bool OtaStoreFlashNrf52::begin(uint32_t total_size) { !ota_self_firmware(fi) || !fi.valid || fi.image_len > UINT32_MAX - app_base || !mota_nrf52_shared_boot_stage_plan( total_size, app_base, true, app_base + fi.image_len, checked_start) || - checked_start != planned_start -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - || !ota_bootloader_live_bank_preserves_scratch( - app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) -#endif - ) + checked_start != planned_start) return false; app_end = app_base + fi.image_len; #else @@ -612,19 +597,10 @@ bool OtaStoreFlashNrf52::reopenFor(const uint8_t* want_mid, uint32_t expected_ta if (bootloader) { #if defined(OTA_INTERNAL_BOOTLOADER_UPDATE) SelfFwInfo fi; - if (!(ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps()) -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - || ota_bootloader_app_caps().storage_flags == OTA_BL_PROFILE_RAK_AUTO -#endif - ) || + if (!ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps()) || !ota_self_firmware(fi) || !fi.valid || fi.image_len > UINT32_MAX - app_base || !mota_nrf52_shared_boot_stage_plan( - total, app_base, true, app_base + fi.image_len, want) || want != start -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - || !ota_bootloader_live_bank_preserves_scratch( - app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) -#endif - ) + total, app_base, true, app_base + fi.image_len, want) || want != start) continue; #else continue; diff --git a/src/helpers/ota/OtaStoreQspiNrf52.cpp b/src/helpers/ota/OtaStoreQspiNrf52.cpp index 89de11c0c..05d53204e 100644 --- a/src/helpers/ota/OtaStoreQspiNrf52.cpp +++ b/src/helpers/ota/OtaStoreQspiNrf52.cpp @@ -4,10 +4,6 @@ #include "OtaByteIO.h" #include "OtaFlashLayout_nrf52.h" -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - #include "OtaBootloaderUpdate.h" - #include "OtaSelf.h" -#endif #include "hal/nrf_gpio.h" #include "hal/nrf_qspi.h" #include "nrf.h" @@ -889,9 +885,8 @@ uint32_t OtaStoreQspiNrf52::capacity() const { return result; } -bool OtaStoreQspiNrf52::plan_layout(bool is_full, uint32_t image_size, - uint32_t payload_off, uint32_t payload_size, - bool is_bootloader) { +bool OtaStoreQspiNrf52::plan_layout(bool, uint32_t image_size, uint32_t, + uint32_t payload_size, bool) { // This is the start of a new manifest admission attempt. Clear diagnostics // left by the normal empty-store reopen probe, then latch the first error // from this attempt until the operator reads it or starts another attempt. @@ -900,25 +895,6 @@ bool OtaStoreQspiNrf52::plan_layout(bool is_full, uint32_t image_size, _stage = OtaQspiStage::IDLE; const uint32_t app_base = mota_nrf52_app_base(); const uint32_t app_ceiling = mota_nrf52_application_ceiling(); -#if defined(OTA_RAK_BOOTLOADER_RECOVERY) - if (is_bootloader) { - SelfFwInfo fi; - if (!is_full || image_size != OTA_BOOT_IMAGE_SIZE || - payload_size != OTA_BOOT_IMAGE_SIZE || payload_off != 365u || - ota_nrf52_effective_stage_ceiling() != MOTA_NRF52_APP_END || - !ota_self_firmware(fi) || - !ota_bootloader_scratch_headroom_valid( - fi.valid, app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START) || - !ota_bootloader_live_bank_preserves_scratch( - app_base, fi.image_len, MOTA_NRF52_SHARED_BOOT_STAGE_START)) { - fail("recovery scratch is unavailable"); - releaseFlash(); - return false; - } - } -#else - (void)is_full; (void)payload_off; (void)is_bootloader; -#endif if (image_size == 0 || payload_size == 0 || app_base >= app_ceiling || image_size > app_ceiling - app_base) { fail("image exceeds nRF52 application region"); diff --git a/test/test_ota/test_ota_core.cpp b/test/test_ota/test_ota_core.cpp index 5c200485a..b9acac5bf 100644 --- a/test/test_ota/test_ota_core.cpp +++ b/test/test_ota/test_ota_core.cpp @@ -278,26 +278,6 @@ TEST(OtaBootPackage, CandidateContinuityEnvelopeHasOneCanonicalFinalOffset) { store, 0, OTA_BL_PROFILE_SD_BOOT_UPDATE, identity, caps)); EXPECT_EQ(identity.manifest_offset, OTA_BOOT_CANDIDATE_MANIFEST_OFFSET); - // Adaptive RAK recovery is authenticated by the application, so its - // candidate retains ABI 2 and the exact 0x16 application capability marker. - auto rak_recovery = canonical; - rak_recovery[0x400u + 8u] = MOTA_APP_FORMAT_VER; - rak_recovery[0x400u + 12u] = OTA_BL_STORAGE_STAGE_CEILING | - OTA_BL_STORAGE_QSPI | - OTA_BL_STORAGE_HEADER_W25; - wr_u32le(rak_recovery.data() + OTA_BOOT_CANDIDATE_MANIFEST_OFFSET + 40u, - 0u); - wr_u32le(rak_recovery.data() + OTA_BOOT_CANDIDATE_MANIFEST_OFFSET + 40u, - ota_boot_image_crc32(rak_recovery.data(), rak_recovery.size(), - OTA_BOOT_CANDIDATE_MANIFEST_OFFSET + 40u)); - ASSERT_TRUE(store.begin((uint32_t)rak_recovery.size())); - ASSERT_TRUE(store.write(0, rak_recovery.data(), (uint32_t)rak_recovery.size())); - EXPECT_TRUE(ota_bootloader_external_image_metadata( - store, 0, rak_recovery[0x400u + 12u], identity, caps, false)); - EXPECT_EQ(caps.apply_abi, MOTA_APP_FORMAT_VER); - EXPECT_FALSE(ota_bootloader_external_image_metadata( - store, 0, rak_recovery[0x400u + 12u], identity, caps)); - // A second CRC-valid base identity still counts when its adjacent BLM2 // extension is only half present. These coupled values are the fixed point // for this deterministic image. Counting continuity first would wrongly @@ -830,20 +810,6 @@ TEST(OtaBootPackage, LegacyAndCurrentBootloadersHaveSeparateCapabilityViews) { EXPECT_TRUE(app.present); EXPECT_FALSE(update.present); - // Adaptive RAK ABI-2 advertises application OTA and the recovery storage - // layout, while its bootloader still cannot self-update. - const uint8_t adaptive_marker[16] = { - 'M','O','T','A','B','L','D','R', 2,0, 5,0, - OTA_BL_PROFILE_RAK_AUTO, 0,0,0}; - memset(image, 0xFF, sizeof(image)); - memcpy(image + 4, adaptive_marker, sizeof(adaptive_marker)); - app = ota_bl_app_caps_scan(image, sizeof(image)); - update = ota_bl_update_caps_scan_aligned( - image, sizeof(image), OTA_BL_PROFILE_RAK_AUTO); - ASSERT_TRUE(app.present); - EXPECT_EQ(app.storage_flags, OTA_BL_PROFILE_RAK_AUTO); - EXPECT_FALSE(update.present); - // A valid marker for another storage profile is visible diagnostically but // cannot authorize an internal-flash bootloader replacement. memset(image, 0xFF, sizeof(image)); diff --git a/tools/lora_ota/lora_ota.py b/tools/lora_ota/lora_ota.py index f8169870e..eef34b747 100755 --- a/tools/lora_ota/lora_ota.py +++ b/tools/lora_ota/lora_ota.py @@ -149,7 +149,7 @@ MIN_MESHCLI_VERSION = (1, 6, 0) # Tested format-3 inspect/verify/serve support. Keep automatic repairs pinned; # neither a package nor a moving branch may choose code to install on the host. MOTATOOL_REPAIR_REPOSITORY = "https://github.com/mikecarper/motatool.git" -MOTATOOL_REPAIR_REVISION = "beff192aec341077f6d9d4b4938e47323775b9eb" +MOTATOOL_REPAIR_REVISION = "8c38369e7d35ad50cf74261869676d52dd24adf7" MOTATOOL_REPAIR_TIMEOUT_SECONDS = 60 * 60 # The pinned lockfile, not motatool's older package-level rust-version: zeroize # needs edition 2024 (1.85), and idna_adapter/ICU 2.2 require Rust 1.86. @@ -1085,15 +1085,11 @@ def compatible_mota(info: MotaInfo, target: TargetInfo) -> tuple[bool, str]: f"bootloader target {info.target_id:08X} hw={info.hw_id}, need " f"{target.boot_target_id:08X} hw={target.boot_hw_id}" ) - recovery = (target.boot_storage == 0x16 and - info.bootloader_storage == 0x16) - if target.bootloader_abi is None or target.bootloader_abi < (2 if recovery else 3): - return False, "bootloader install requires a capable installed OTAFIX profile" + if target.bootloader_abi is None or target.bootloader_abi < 3: + return False, "bootloader self-update requires installed ABI 3 or newer" if target.bootloader_codecs != 0x5: return False, "bootloader self-update requires FULL|INPLACE codecs 0x5" - expected_storage = (0x16 if recovery else - 0x09 if target.nrf_sd else - 0x0E if target.nrf_qspi else 0x0A) + expected_storage = 0x09 if target.nrf_sd else 0x0E if target.nrf_qspi else 0x0A if ( target.boot_storage != expected_storage or info.bootloader_storage != expected_storage @@ -2769,7 +2765,7 @@ def query_bootloader_target( if match is None: raise OtaError( f"destination cannot stage a bootloader update: {reply}; " - "requires an exact-board update or adaptive RAK recovery application" + "requires exact-board ABI-3 self-update bootloader and capable application" ) board, target_id, name, _crc, abi, caps = match.groups() library = bootloader_library() @@ -2783,9 +2779,7 @@ def query_bootloader_target( raise OtaError(f"invalid destination bootloader identity: {exc}") from exc if derived_target != int(target_id, 16): raise OtaError("destination bootloader board/name does not match its target ID") - recovery = int(caps, 16) == 0x16 and (int(board, 16), name) in \ - library.RAK_AUTO_RECOVERY_IDENTITIES - if int(abi) < (2 if recovery else 3) or int(abi) != target.bootloader_abi: + if int(abi) < 3 or int(abi) != target.bootloader_abi: raise OtaError("destination reports inconsistent or unsupported bootloader ABI") return replace( target, boot_target_id=derived_target, boot_hw_id=hw_id, diff --git a/tools/lora_ota/test_lora_ota_bootloader.py b/tools/lora_ota/test_lora_ota_bootloader.py index 36e0048ab..0c5d71e9d 100644 --- a/tools/lora_ota/test_lora_ota_bootloader.py +++ b/tools/lora_ota/test_lora_ota_bootloader.py @@ -21,7 +21,7 @@ import lora_ota as ota from test_lora_ota import firmware, mota_blob, prepare_args, target, VERSION_NEW -def boot_blob(*, storage=0x0A, version=0x0117010D, rak_name="3401_AUTO_DFU"): +def boot_blob(*, storage=0x0A, version=0x0117010D): """Build a signed test-only candidate using the canonical reference builder.""" ml = ota.bootloader_library() board, name, app_base = 0x239A0029, "3401_DFU", ml.NRF52_APP_BASE_S140_V6 @@ -30,12 +30,9 @@ def boot_blob(*, storage=0x0A, version=0x0117010D, rak_name="3401_AUTO_DFU"): name = ml.XIAO_BOOT_DEVICE_NAME.rstrip(b"\0").decode("ascii") elif storage == 0x09: board, name = 0x239A0071, "TOWER_V2_OTA" - elif storage == 0x16: - board, name = 0x239A0029, rak_name image = bytearray(b"\xff" * ml.XIAO_BOOT_IMAGE_SIZE) struct.pack_into("_` ID and collision-checked wire target from the full embedded manifest pair. Parsing and `verify()` repeat those gates; magic @@ -78,8 +76,7 @@ builder accepts only identities in the qualified inventory. That inventory currently covers the shared-internal nRF52840 targets, the two deployed XIAO raw-QSPI identities, and the exact MeshTower V2 identity whose candidate marker may select either its internal (`0x0A`) or microSD (`0x09`) application -layout, plus the two exact adaptive RAK identities (`3401_AUTO_DFU` and -`4631_AUTO_DFU`). The test suite verifies those boot target IDs are unique and disjoint +layout. The test suite verifies those boot target IDs are unique and disjoint from the generated application target table. This library does not authorize a device update. A capable node will only arm diff --git a/tools/mota/motalib.py b/tools/mota/motalib.py index 458a980a1..a80370f45 100644 --- a/tools/mota/motalib.py +++ b/tools/mota/motalib.py @@ -108,15 +108,11 @@ BOOT_STORAGE_SD = 0x01 BOOT_STORAGE_STAGE_CEILING = 0x02 XIAO_BOOT_STORAGE_QSPI = 0x04 XIAO_BOOT_STORAGE_UPDATE = 0x08 -BOOT_STORAGE_KNOWN = 0x1F -BOOT_STORAGE_HEADER_W25 = 0x10 +BOOT_STORAGE_KNOWN = 0x0F BOOT_STORAGE_SD_UPDATE = BOOT_STORAGE_SD | XIAO_BOOT_STORAGE_UPDATE BOOT_STORAGE_QSPI_UPDATE = (BOOT_STORAGE_STAGE_CEILING | XIAO_BOOT_STORAGE_QSPI | XIAO_BOOT_STORAGE_UPDATE) BOOT_STORAGE_INTERNAL_UPDATE = BOOT_STORAGE_STAGE_CEILING | XIAO_BOOT_STORAGE_UPDATE -BOOT_STORAGE_RAK_AUTO_RECOVERY = (BOOT_STORAGE_STAGE_CEILING | - XIAO_BOOT_STORAGE_QSPI | - BOOT_STORAGE_HEADER_W25) BOOT_REQUIRED_APP_CODEC_MASK = ((1 << CODEC_FULL) | (1 << CODEC_DETOOLS_INPLACE)) @@ -142,13 +138,6 @@ INTERNAL_BOOTLOADER_IDENTITIES = ( (0x239A0029, "RTAG_DFU"), ) -# These adaptive loaders do not self-update. Their exact MeshCore application -# authenticates a format-3 package and uses the MBR to replace the bootloader. -RAK_AUTO_RECOVERY_IDENTITIES = ( - (0x239A0029, "3401_AUTO_DFU"), - (0x239A0029, "4631_AUTO_DFU"), -) - # MeshTower V2 has both a lean internal-store application and an exact SD-store # application. They intentionally retain the same embedded bootloader identity # and signed wire target; the candidate capability marker selects the matching @@ -707,7 +696,7 @@ def audit_bootloader_target_inventory(application_target_ids=()) -> dict: identities = ( (XIAO_BOOT_BOARD_ID_BASE, "XIAO_DFU"), (XIAO_BOOT_BOARD_ID_SENSE, "XIAO_DFU"), - ) + INTERNAL_BOOTLOADER_IDENTITIES + RAK_AUTO_RECOVERY_IDENTITIES + ) + INTERNAL_BOOTLOADER_IDENTITIES app_ids = set(application_target_ids) targets = {} for identity in identities: @@ -725,7 +714,6 @@ def audit_bootloader_target_inventory(application_target_ids=()) -> dict: def bootloader_identity_is_buildable(board_id: int, device_name: str) -> bool: return ((board_id, device_name) in INTERNAL_BOOTLOADER_IDENTITIES or - (board_id, device_name) in RAK_AUTO_RECOVERY_IDENTITIES or (board_id, device_name) in ( (XIAO_BOOT_BOARD_ID_BASE, "XIAO_DFU"), (XIAO_BOOT_BOARD_ID_SENSE, "XIAO_DFU"))) @@ -755,8 +743,6 @@ def bootloader_qualified_storage_profiles(board_id: int, device_name: str): return (BOOT_STORAGE_INTERNAL_UPDATE, BOOT_STORAGE_SD_UPDATE) if identity in INTERNAL_BOOTLOADER_IDENTITIES: return (BOOT_STORAGE_INTERNAL_UPDATE,) - if identity in RAK_AUTO_RECOVERY_IDENTITIES: - return (BOOT_STORAGE_RAK_AUTO_RECOVERY,) return None @@ -833,7 +819,7 @@ def parse_xiao_bootloader_identity(image: bytes) -> Optional[XiaoBootloaderIdent def bootloader_caps_storage(image: bytes) -> Optional[int]: - """Return one exact qualified self-update or app-owned recovery marker.""" + """Return the one exact supported self-update storage marker, or None.""" found = None valid_count = 0 for off in range(0, len(image) - 16 + 1, 4): @@ -841,22 +827,16 @@ def bootloader_caps_storage(image: bytes) -> Optional[int]: continue abi, codecs = struct.unpack_from("