Add MeshTower SD bootloader updates over LoRa
This commit is contained in:
1 parent
9e4d246ac6
commit
3d78d9c846
26 files changed
+763
-114
No files matched your search
@@ -166,7 +166,7 @@ fix, no WiFi connection, an inactive bridge, or an nRF52 bootloader without
|
||||
| LoRa OTA | [`ota get`; `ota pull`; `ota download`](ota_protocol.md#11-cli-surface-otaclicpp) | LoRa OTA build; internal-store nRF52 installs in-place deltas, while matched SD/QSPI nRF52 also accepts full images | No | No | Yes |
|
||||
| LoRa OTA | [`ota install`; `ota apply`; `ota applydelta`](ota_protocol.md#11-cli-surface-otaclicpp) | Compatible bootloader and completed update | No | No | Yes |
|
||||
| LoRa OTA | [`ota rescue install <base_hash16>`](ota_protocol.md#12-apply-bootloader-contract) | Legacy internal-flash nRF52 LoRa OTA build with failed app-side EndF validation; shared bootloader-update builds require valid EndF | No | No | Feature |
|
||||
| LoRa OTA | [`ota bootloader`; `ota bootloader install <MID8> <HASH16>`](ota_nrf52_bootloader_update.md) | Allowlisted internal-flash or XIAO-QSPI nRF52 repeater with ABI-3 exact-board OTAFIX | No | No | Feature |
|
||||
| LoRa OTA | [`ota bootloader`; `ota bootloader install <MID8> <HASH16>`](ota_nrf52_bootloader_update.md) | Allowlisted internal-flash, XIAO-QSPI, or exact MeshTower V2 microSD nRF52 repeater with ABI-3 exact-board OTAFIX | No | No | Feature |
|
||||
| LoRa OTA | [`ota cancel`; `ota drop`; `ota stop`](ota_protocol.md#11-cli-surface-otaclicpp) | LoRa OTA build | No | No | Yes |
|
||||
| LoRa OTA | [`ota announce`; `ota adv`](ota_protocol.md#11-cli-surface-otaclicpp) | LoRa OTA build | No | No | Yes |
|
||||
| LoRa OTA | [`ota self`; `ota id`](ota_protocol.md#11-cli-surface-otaclicpp) | Firmware with EndF trailer | No | No | Yes |
|
||||
|
||||
@@ -17,8 +17,8 @@ The script cannot install the destination's first OTA-capable firmware. It is
|
||||
also deliberately application-only: it rejects both v3 bootloader containers
|
||||
and a v2 container carrying the bootloader flag, and it never sends the
|
||||
privileged install command. Do the first nRF52 bootloader installation over USB.
|
||||
An already-capable, explicitly supported internal-flash or XIAO QSPI repeater
|
||||
may later use the separate manual
|
||||
An already-capable, explicitly supported internal-flash, XIAO QSPI, or exact
|
||||
MeshTower V2 microSD repeater may later use the separate manual
|
||||
[`ota bootloader install`](ota_nrf52_bootloader_update.md) workflow.
|
||||
|
||||
## Required topology
|
||||
|
||||
+3
-2
@@ -335,8 +335,9 @@ or `delta` for any supported nRF52 target. Do not select `[unsupported]` (for ex
|
||||
full image on an internal-staging nRF52).
|
||||
Rows labelled `bootloader` are also outside this ordinary application flow:
|
||||
they are never automatic and `ota install` refuses them. Only an
|
||||
already-capable, allowlisted internal-flash or XIAO-QSPI nRF52 target can use
|
||||
the separate [explicit bootloader workflow](ota_nrf52_bootloader_update.md).
|
||||
already-capable, allowlisted internal-flash, XIAO-QSPI, or exact MeshTower V2
|
||||
microSD nRF52 target can use the separate
|
||||
[explicit bootloader workflow](ota_nrf52_bootloader_update.md).
|
||||
Use the row's stable eight-hex manifest ID rather than its changing list position:
|
||||
|
||||
```text
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
The `Heltec_tower_v2_sdcard_repeater_lora_ota_no_external_sensors` target uses the MeshTower V2 onboard
|
||||
microSD socket as persistent storage for its own LoRa OTA downloads. It accepts
|
||||
both full `.mota` images and in-place delta `.mota` images. After verification,
|
||||
the matching SD-aware OTAFIX bootloader reads the staged file from the card and
|
||||
programs the nRF52840 application region.
|
||||
full application images, in-place application deltas, and explicitly selected
|
||||
signed v3 bootloader packages. After verification, the matching SD-aware OTAFIX
|
||||
bootloader reads the staged file from the card and programs the selected
|
||||
nRF52840 application or bootloader region.
|
||||
|
||||
The pin assignment follows the
|
||||
[Heltec MeshTower V2 partial reference circuit](https://resource.heltec.cn/download/MeshTower-V2/schematic/MeshTower_V2_Partial_Reference_Circuit.pdf):
|
||||
@@ -70,6 +71,65 @@ bootloader capability marker does not advertise SD staging and the selected
|
||||
codec. Existing `Heltec_tower_v2_repeater` firmware continues to use the
|
||||
internal-flash delta path and is unchanged.
|
||||
|
||||
## Signed bootloader update
|
||||
|
||||
Only this exact SD build exposes the privileged LoRa bootloader-update command.
|
||||
It requires an already installed exact-board ABI-3 OTAFIX bootloader whose one
|
||||
unambiguous capability marker is exactly `0x09` (`SD|BOOT_UPDATE`) and whose
|
||||
codec mask is `0x0005` (`FULL|INPLACE`). Requiring both application codecs
|
||||
prevents a bootloader self-update from disabling either normal SD application
|
||||
path. It cannot bootstrap a stock or older bootloader; provision that
|
||||
prerequisite once over USB/BLE DFU or SWD.
|
||||
|
||||
The signed v3 container is exactly 41,330 bytes and carries a 40 KiB candidate
|
||||
for the installed `239A0071 / TOWER_V2_OTA` identity (boot target `1150F50E`).
|
||||
It uses the same contiguous `/meshcore-ota.mota` file and sector-1 handoff as an
|
||||
application update. GPREGRET `0x6B` plus the distinct SD source marker `0x53`
|
||||
selects the bootloader path; LoRa transport remains payload type `0x0C`.
|
||||
|
||||
The application linker still ends at `0xED000`, but OTAFIX needs
|
||||
`0xE0000..0xEA000` as temporary scratch while replacing itself. Before a boot
|
||||
package can be downloaded or approved, MeshCore requires a hash-valid live
|
||||
`EndF` proving the complete running image ends by `0xE0000`; OTAFIX repeats
|
||||
that no-overlap check before its first erase. An application extending above
|
||||
that boundary can still receive normal FULL or delta application updates from
|
||||
SD, but its bootloader must be updated locally.
|
||||
|
||||
MeshCore also checks the live boot settings before touching that scratch page.
|
||||
Erased settings and a valid-app record with CRC disabled are allowed. When a
|
||||
nonzero bank CRC is active, its recorded `bank_0_size` must cover the complete
|
||||
EndF-inclusive running image and must end by `0xE0000`; an undersized or
|
||||
oversized record refuses the operation and requires local DFU/SWD.
|
||||
|
||||
Because the card is removable, approval is bound to the exact bytes that were
|
||||
authenticated. MeshCore verifies the signature and signer allowlist over one
|
||||
exact local manifest copy, requires the streamed manifest on SD to remain
|
||||
byte-identical, and verifies the complete payload against that manifest. It
|
||||
then writes `APRV` and syncs the card. Before publishing the raw-sector
|
||||
handoff, it writes and readback-verifies a temporary 64-byte `MOTASDBL` token
|
||||
at `0xE0000` containing the exact container length and that signed manifest's
|
||||
`image_hash`. OTAFIX requires the parsed SD manifest, streamed payload, and
|
||||
final scratch image to match the same token. Replacing or changing the card
|
||||
after verification can therefore only make the boot update fail; it cannot
|
||||
authorize different bootloader bytes. The token page becomes the first
|
||||
scratch page during a successful update and is not permanently reserved.
|
||||
|
||||
Bootloader packages are never autofetched or autoinstalled. Select and confirm
|
||||
one exact package manually:
|
||||
|
||||
```text
|
||||
ota ls
|
||||
ota pull <MID8> flash
|
||||
# wait for ota status to report the bootloader download ready
|
||||
ota bootloader
|
||||
ota bootloader install <MID8> <HASH16>
|
||||
```
|
||||
|
||||
Copy the MID and first 16 image-hash hex digits from `ota bootloader`. Ordinary
|
||||
`ota install` rejects this package, and the bootloader command rejects an
|
||||
application package. Keep the SD card inserted through the reboot. A later
|
||||
`ota status` value of `blup:C8` reports a successful bootloader replacement.
|
||||
|
||||
## SD card CLI
|
||||
|
||||
The SD-backed target provides these CLI commands:
|
||||
@@ -100,7 +160,7 @@ reset when the device reboots. The `get sdcard` age queries report how long ago
|
||||
each operation completed. `get sdcard free` reports used and free filesystem
|
||||
space in human-readable binary units.
|
||||
|
||||
`get sdcard ls` and `get sdcard dir` recursively list files on the card, four
|
||||
`get sdcard ls` and `get sdcard dir` recursively list files on the card, two
|
||||
files per page. A bare command shows page 1; append a positive page number to
|
||||
move through the remaining results. Each row includes the path and a compact
|
||||
file size. The header reports the selected page, total pages, and total files.
|
||||
|
||||
@@ -12,6 +12,33 @@ over USB/BLE 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` limit | shared internal slot, exact start `0xE2000` | the same eleven-page slot; no second 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` |
|
||||
|
||||
The exact SD target is
|
||||
`Heltec_tower_v2_sdcard_repeater_lora_ota_no_external_sensors`. Its normal
|
||||
application FULL and delta updates continue to use the SD file without the
|
||||
bootloader-update scratch restriction. For a bootloader package only, both
|
||||
MeshCore and OTAFIX require a hash-valid live `EndF` proving the complete
|
||||
running image ends at or below `0xE0000`. If boot settings carry a nonzero app
|
||||
bank CRC, the recorded bank size must also cover that full EndF-inclusive
|
||||
image and stop by `0xE0000`; erased or explicitly CRC-disabled settings remain
|
||||
valid. OTAFIX then copies the verified 40
|
||||
KiB payload from SD into `0xE0000..0xEA000` and uses the MBR copy operation to
|
||||
replace `0xF4000..0xFE000`. The application linker remains at `0xED000`; a
|
||||
future application extending above `0xE0000` can still use application mOTA
|
||||
but must update its bootloader through local DFU/SWD.
|
||||
|
||||
The removable SD authorization is fail-closed. MeshCore directly authenticates
|
||||
one exact manifest, requires the streamed manifest on SD to remain
|
||||
byte-identical, and verifies its complete payload. After writing and syncing
|
||||
`APRV`, but before publishing the raw-sector handoff, it writes and
|
||||
readback-verifies a 64-byte `MOTASDBL` token at `0xE0000`. The token contains
|
||||
the exact container length and the authenticated signed manifest's
|
||||
`image_hash`. OTAFIX requires that same hash in the parsed manifest, the
|
||||
streamed raw payload, and the final scratch image. A card swap or mutation
|
||||
after application verification therefore causes refusal rather than
|
||||
authorizing different bytes; the token page is consumed as scratch during a
|
||||
successful update and is not a permanent reservation.
|
||||
|
||||
The internal path does not change the application linker or permanently set
|
||||
aside separate app-OTA, boot-package, and scratch regions. The ordinary
|
||||
@@ -86,9 +113,10 @@ ordinary full-sensor target when that module is required.
|
||||
Boards with onboard external flash are not silently redirected to internal
|
||||
staging. Mesh Solar, Nano G2 Ultra, T-Impulse Plus, ThinkNode M8, T-Echo
|
||||
Lite/Card, MeshTracker X1, and Wio WM1110 have board-specific QSPI hardware and
|
||||
need a separately matched QSPI path where available. Full Companions,
|
||||
need a separately matched QSPI path where available. Full Companions, other
|
||||
SD/QSPI/ExtraFS roles, Ethernet roles, source-only roles, and unqualified
|
||||
full-sensor roles are excluded.
|
||||
full-sensor roles are excluded from the internal-flash inventory. The exact
|
||||
MeshTower V2 SD role above is separately qualified for its SD path.
|
||||
|
||||
This internal layout is limited to nRF52840 devices with 1 MiB internal flash,
|
||||
the exact S140 v6/v7 map, and the 40 KiB boot region at
|
||||
@@ -104,8 +132,9 @@ Check the installed identity and capability marker:
|
||||
ota bootloader
|
||||
```
|
||||
|
||||
The response must show a CRC-valid exact identity, ABI 3 or newer, full codec,
|
||||
and exact flags `0x0A` for internal shared storage or `0x0E` for XIAO QSPI. A
|
||||
The response must show a CRC-valid exact identity, ABI 3 or newer, both FULL
|
||||
and INPLACE application codecs (`codecs=0x5`), and exact flags `0x09` for
|
||||
MeshTower V2 SD, `0x0A` for internal shared storage, or `0x0E` for XIAO QSPI. A
|
||||
bootloader row is visible in `ota ls`, but it is never autofetched or
|
||||
autoinstalled. Fetch its exact MID, then explicitly arm it:
|
||||
|
||||
@@ -126,25 +155,31 @@ bootloader package is not automatically resumed after an application reboot.
|
||||
Before writing `APRV`, the application authenticates and authorizes the
|
||||
package: exact v3 geometry, trusted Ed25519 signer, signed/embedded identity,
|
||||
one unambiguous capability marker, embedded CRC, sane vectors, complete
|
||||
Merkle/payload/image hashes, safe live placement, and the typed MID/hash
|
||||
confirmation. OTAFIX consumes that application-written authorization and then
|
||||
Merkle/payload/image hashes, storage-specific safe live placement, and the
|
||||
typed MID/hash confirmation. On SD, `APRV` and the internal signed-image-hash
|
||||
token are synced and read back before the handoff becomes visible. OTAFIX consumes that application-written
|
||||
authorization and then
|
||||
independently rechecks the safety/integrity subset: strict v3 structure,
|
||||
canonical identity/capabilities, vectors, full payload SHA, embedded manifest
|
||||
CRC, live `EndF` no-overlap geometry, page readback, and final copy hash. It
|
||||
does not re-run Ed25519, the signer allowlist, Merkle leaves/root, or the typed
|
||||
operator confirmation. Success is reported as `blup:C8`.
|
||||
CRC, the applicable live `EndF`/bank-settings no-overlap geometry, the SD token
|
||||
binding where applicable, scratch readback, and final
|
||||
copy hash. It does not re-run Ed25519, the signer allowlist, Merkle leaves/root,
|
||||
or the typed operator confirmation. Success is reported as `blup:C8`.
|
||||
|
||||
## Failure behavior
|
||||
|
||||
The feature fails closed when the MCU/map is wrong, current valid `EndF`
|
||||
headroom is insufficient, external/ExtraFS storage owns the target, the exact
|
||||
installed capability marker is absent or ambiguous, identity cannot be derived
|
||||
unambiguously, or any package check fails. Before compaction, the application
|
||||
and bootloader are unchanged. OTAFIX consumes the trigger/approval before the
|
||||
first in-place compaction erase, so an interrupted conversion cannot retry a
|
||||
partly converted package; it boots the untouched application and old
|
||||
bootloader instead. Use USB/BLE DFU or SWD for initial provisioning and local
|
||||
recovery.
|
||||
The feature fails closed when the MCU/map is wrong, required valid `EndF`
|
||||
headroom is insufficient, an unsupported external/ExtraFS role owns the target,
|
||||
the exact installed capability marker is absent or ambiguous, identity cannot be derived
|
||||
unambiguously, or any package check fails. Before the storage-specific
|
||||
scratch/copy step, the application and bootloader are unchanged. OTAFIX
|
||||
consumes the trigger and, for internal/QSPI storage, clears approval before its
|
||||
first scratch erase, so an interrupted operation cannot retry a partly
|
||||
consumed package. The SD backend cannot rewrite raw card sectors: its `APRV`
|
||||
and checksummed handoff may persist, but the one-shot GPREGRET trigger is
|
||||
consumed before validation and they remain inert unless the running app again
|
||||
authenticates and explicitly re-arms that package. Use USB/BLE DFU or SWD for
|
||||
initial provisioning and local recovery.
|
||||
|
||||
For XIAO and ordinary external-QSPI details, see
|
||||
[nRF52 repeater LoRa OTA with external QSPI](ota_nrf52_qspi.md).
|
||||
+16
-3
@@ -271,8 +271,9 @@ A v3 bootloader package has a deliberately narrow, non-extensible profile:
|
||||
- signed `hw_id` exactly `XIAO_BL_28860044`/`XIAO_BL_28860045` for deployed XIAO, or the zero-padded
|
||||
32-byte `NRF_BL_<BOARD_ID>_<DEVICE_NAME>` for a generic target;
|
||||
- a sane nRF52840 vector table, exactly one CRC-valid embedded manifest v1 with the exact board/name pair,
|
||||
and exactly one `MOTABLDR` marker advertising ABI >= 3, full codec, boot-update continuity, and the exact
|
||||
storage flags for the application layout (`0x0E` XIAO QSPI or `0x0A` shared internal staging).
|
||||
and exactly one `MOTABLDR` marker advertising ABI >= 3, both application codecs (`FULL|INPLACE`, mask
|
||||
`0x0005`), boot-update continuity, and the exact storage flags for the application layout (`0x09`
|
||||
MeshTower V2 SD, `0x0E` XIAO QSPI, or `0x0A` shared internal staging).
|
||||
|
||||
The incoming embedded identity must exactly match the installed CRC-valid bootloader identity. Both scans
|
||||
consider every aligned structurally valid candidate so magic bytes in a literal pool cannot shadow the real
|
||||
@@ -842,7 +843,19 @@ ota dev ... bring-up helpers (stage/recv/serve/verify)
|
||||
- **MeshTower V2 SD nRF52:** the application stores a contiguous `/meshcore-ota.mota` on microSD and
|
||||
publishes its raw sector range in a checksummed handoff record outside the MBR partition. The matching
|
||||
bootloader reads the card without mounting FAT, supports either a full image or an in-place delta,
|
||||
verifies the staged/full result hash, and never writes through `0xED000` where InternalFS begins.
|
||||
verifies the staged/full result hash, and never writes through `0xED000` where InternalFS begins. The
|
||||
exact SD repeater also accepts a manually selected, signed v3 bootloader package when installed and
|
||||
candidate markers are exactly `0x09` (`SD|BOOT_UPDATE`). MeshCore streams the same strict identity,
|
||||
CRC, vector, signature, MID/hash-confirmation, and complete-image checks from the SD file. GPREGRET
|
||||
`0x6B` plus GPREGRET2 `0x53` selects this privileged path. Both MeshCore and OTAFIX require a hash-valid
|
||||
live `EndF` ending by `0xE0000`; when a nonzero boot-settings bank CRC is active, its recorded size must
|
||||
also cover that complete live image and stop by `0xE0000`. MeshCore authenticates one exact manifest,
|
||||
verifies the streamed SD copy is byte-identical, writes and syncs `APRV`, then writes a readback-checked
|
||||
`MOTASDBL` token at `0xE0000` containing the exact total and signed `image_hash` before publishing the
|
||||
raw-sector handoff. OTAFIX binds the parsed manifest, streamed payload, and final scratch image to that
|
||||
token, so a removable-media change can only fail closed. OTAFIX then uses `0xE0000..0xEA000` as temporary scratch; the normal
|
||||
application linker remains at `0xED000` and ordinary application updates do not inherit this scratch
|
||||
headroom restriction.
|
||||
- **Matched external-QSPI nRF52 repeaters:** the application reserves the board's dedicated QSPI NOR as a
|
||||
raw store beginning at offset zero. It obtains a 1-16 MiB capacity from JEDEC, checkpoints payload before
|
||||
leaf metadata, and verifies each erased/programmed page. GPREGRET2 `0x51` selects QSPI only when the
|
||||
|
||||
+14
-3
@@ -219,7 +219,9 @@ After it reboots, run `ota status` to confirm the new version.
|
||||
### Updating an nRF52 bootloader (advanced, explicit only)
|
||||
|
||||
This is available on specially marked no-external-flash nRF52840 lean
|
||||
repeater/bridge builds and on the legacy XIAO nRF52840/Sense raw-QSPI builds,
|
||||
repeater/bridge builds, on the legacy XIAO nRF52840/Sense raw-QSPI builds, and
|
||||
on the exact `Heltec_tower_v2_sdcard_repeater_lora_ota_no_external_sensors`
|
||||
microSD build,
|
||||
after a one-time exact-board ABI-3 OTAFIX installation over USB/BLE DFU or
|
||||
SWD. It is not the normal firmware update path. Check support first:
|
||||
|
||||
@@ -228,8 +230,8 @@ ota bootloader
|
||||
```
|
||||
|
||||
The reply must show a CRC-valid installed identity plus ABI 3 and the exact
|
||||
storage/boot-update capability bits for that build (`0x0A` for the shared
|
||||
internal store or `0x0E` for XIAO raw QSPI). A bootloader package appears as
|
||||
storage/boot-update capability bits for that build (`0x09` for MeshTower V2
|
||||
microSD, `0x0A` for the shared internal store, or `0x0E` for XIAO raw QSPI). A bootloader package appears as
|
||||
`bootloader` in `ota ls`. It is never downloaded or installed automatically,
|
||||
even if both OTA automation settings are enabled. Use its stable ID explicitly:
|
||||
|
||||
@@ -254,6 +256,15 @@ On an internal-flash target, the package shares the ordinary store below
|
||||
bank. A valid live `EndF` must prove the current image ends at or below that
|
||||
address before any page is erased. If `EndF` is missing/corrupt or the app is
|
||||
too large, the pull is refused and local DFU/SWD is required.
|
||||
On the MeshTower V2 SD target, the application linker remains at `0xED000`, but
|
||||
bootloader replacement needs temporary scratch beginning at `0xE0000`. A
|
||||
hash-valid live `EndF` must therefore prove the complete current image ends by
|
||||
`0xE0000`. A CRC-bound boot-settings bank must cover that complete image while
|
||||
also ending by `0xE0000`. MeshCore binds approval to the exact authenticated
|
||||
signed image hash in an internal token before handing the removable SD card to
|
||||
OTAFIX, so a later card change fails instead of authorizing different bytes.
|
||||
Larger applications can continue to use normal application mOTA;
|
||||
only bootloader self-update is refused.
|
||||
See [the nRF52 bootloader-update guide](ota_nrf52_bootloader_update.md) for the
|
||||
complete target inventory, storage layouts, and safety contract.
|
||||
|
||||
|
||||
Reference in new issue
Block a user