Add retained-RAM staging for nRF52 mOTA
This commit is contained in:
1 parent
23bf83277c
commit
49dcba5c6e
25 files changed
+2071
-83
No files matched your search
@@ -12,7 +12,7 @@ provisioned locally before either application or bootloader OTA.
|
||||
| Layout | Application limit | Staged boot package | Work area | Handoff / capability |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| 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` |
|
||||
| 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` |
|
||||
|
||||
The exact SD target is
|
||||
@@ -53,10 +53,13 @@ ownership. Upgrade preview.12 through USB/BLE DFU or SWD. Both fmt2 application
|
||||
and fmt3 bootloader OTA then require the installed BLM2 metadata to match the
|
||||
live SoftDevice/application layout; neither MeshCore nor OTAFIX uses sector 1.
|
||||
|
||||
The internal path does not change the application linker or permanently set
|
||||
aside separate app-OTA, boot-package, and scratch regions. The ordinary
|
||||
bottom-aligned internal store holds one container at a time: either an
|
||||
application delta or a bootloader package.
|
||||
The internal path does not change the application's `0xED000` flash limit or
|
||||
permanently set aside separate app-OTA, boot-package, and flash-scratch
|
||||
regions. Qualified applications do use a dedicated linker that reserves the
|
||||
top 64 KiB of SRAM for hybrid application-delta staging and a retained
|
||||
authorization record. That SRAM is never part of a bootloader-update package.
|
||||
The ordinary bottom-aligned internal flash store holds one container prefix at
|
||||
a time: either an application delta or the complete bootloader package.
|
||||
|
||||
The exact bootloader container is 41,330 bytes: 365 bytes of signed mOTA
|
||||
metadata, a 40 KiB payload, and the five-byte trailer. Below the normal
|
||||
@@ -68,10 +71,13 @@ erasing and compacts the payload forward in place to the page-aligned raw range
|
||||
MBR to copy that image over `0xF4000..0xFE000`.
|
||||
|
||||
An ordinary application delta can be smaller or larger than this eleven-page
|
||||
shape. It bottom-aligns dynamically below `0xED000` and may begin below
|
||||
`0xE2000`; its detools workspace must stop at its actual container start, while
|
||||
the reconstructed application must stop below `0xED000`. The two package kinds
|
||||
are mutually exclusive because they use the same store.
|
||||
shape. On the qualified profile, a delta larger than one page uses the minimum
|
||||
page-aligned flash prefix ending at `0xED000` and keeps up to 64 KiB of its
|
||||
logical tail in reset-retained SRAM; its detools workspace must stop at the
|
||||
actual flash-prefix start. A one-page-or-smaller delta stays wholly in flash.
|
||||
In either case the reconstructed application must stop below `0xED000`. The
|
||||
two package kinds are mutually exclusive because they use the same flash
|
||||
store, and a bootloader package never uses the volatile SRAM suffix.
|
||||
|
||||
For internal-self-update builds, an absent or corrupt live `EndF` disables
|
||||
**all** internal staging before the first erase. The older 608 KiB rescue
|
||||
|
||||
+32
-16
@@ -394,7 +394,9 @@ There is no separate availability structure. **Block `i` is present <=> `leaves[
|
||||
|
||||
**Commit order per block (crash-safe):** (1) verify proof, (2) write block payload to its offset, (3) write
|
||||
`leaves[i]` **last**. A power loss before step 3 leaves the slot erased -> the block is simply re-fetched
|
||||
(idempotent). On boot a node rebuilds an in-RAM present-bitmap by scanning `leaves[]`.
|
||||
(idempotent). On boot a node rebuilds an in-RAM present-bitmap by scanning `leaves[]` for a persistent,
|
||||
reopenable store. A hybrid nRF52 transfer is the deliberate exception: its SRAM-backed payload suffix is
|
||||
volatile, so the application refuses to adopt that staged header instead of rebuilding partial progress.
|
||||
|
||||
**Resume (`OtaManager::resumeStaged` + `OtaStore::checkpoint`/`reopen`):** an interrupted fetch resumes from
|
||||
the staged container after a reboot - re-parse the stored manifest, recompute geometry, count present
|
||||
@@ -407,11 +409,14 @@ package and keeps target `0` as a MID-only wildcard. Stores keep `leaves[]` in R
|
||||
auto-GC, preserving resumable progress. The debug/operator equivalent is `ota dev resume <MID8>`; after a
|
||||
reboot the MID is mandatory, while a no-argument form may only reuse a still-active session MID. It never
|
||||
uses the `nullptr` automatic-adoption path, so a malformed MID or no active MID fails closed.
|
||||
Hybrid nRF52 staging cannot enter this resume path after an application restart, even if its flash prefix
|
||||
still contains metadata; the complete logical container must be fetched again.
|
||||
|
||||
**Flash-store note (RX-safe writes):** a flash page-erase halts the CPU (~85 ms on nRF52) and starves LoRa
|
||||
RX, so the flash stores (`OtaStoreFlashNrf52`/`OtaStoreFlashEsp32`) **coalesce writes to the erase unit**
|
||||
(4 KB page / sector) and commit each once off the per-packet path - RAM stays O(one page), not O(image). A
|
||||
small delta that fits page 0 does zero flash I/O until COMPLETE.
|
||||
(4 KB page / sector) and commit each once off the per-packet path. Ordinary stores keep RAM at O(one page),
|
||||
not O(image); a qualified hybrid nRF52 profile additionally reserves one fixed 64 KiB staging arena. A small
|
||||
delta that fits page 0 does zero flash I/O until COMPLETE.
|
||||
|
||||
---
|
||||
|
||||
@@ -932,16 +937,26 @@ retains its `.part` file; it does not erase the unrelated manual-install store.
|
||||
- **nRF52 internal staging ceiling:** an internal-store application derives the ceiling from facts available
|
||||
in every build, not a board-name list. A companion that actually links the internal ExtraFS datastore stays
|
||||
below `0xD4000`; a default linker region or a role that does not mount ExtraFS can reclaim the unused
|
||||
100 KiB through `0xED000`. An internal bootloader-self-update target keeps that normal linker and ceiling;
|
||||
it does not reserve a second boot-package or scratch region. The application uses a larger-than-legacy
|
||||
window only when the
|
||||
installed bootloader advertises the GPREGRET2 ceiling-handoff capability. The bootloader treats every
|
||||
unknown/legacy handoff value as
|
||||
`0xD4000`, and accepts a container only at the bottom-aligned position for the selected ceiling.
|
||||
100 KiB through `0xED000`. A qualified internal bootloader-self-update target keeps that normal flash
|
||||
ceiling and does not reserve a second boot-package or flash-scratch region, but it uses a dedicated
|
||||
application linker that reserves the top 64 KiB of SRAM (`0x20030000..0x20040000`) plus a 72-byte
|
||||
retained authorization record after the existing persistent clock bytes. Application deltas larger than
|
||||
one flash page use a deterministic page-aligned flash prefix ending at `0xED000` and place up to 64 KiB
|
||||
of the logical container tail in that SRAM. Packages of one page or less, and bootloader-update packages,
|
||||
remain wholly in flash. Hybrid staging is enabled only when the installed bootloader contains exactly one
|
||||
valid `MOTARAMA` capability marker; otherwise a larger hybrid-profile fetch fails before erase. Immediately
|
||||
before a software reset the application publishes a valid-last `MOTAHYB1` record binding the split geometry
|
||||
and normalized container hash. The bootloader consumes it once and rejects power-loss, stale-reset, corrupt,
|
||||
or mismatched-RAM cases before its first application write. A hybrid transfer cannot resume after an
|
||||
application restart because its suffix is deliberately volatile. The bootloader treats every unknown/legacy
|
||||
GPREGRET2 handoff value as `0xD4000`, and accepts a flash-only container only at the bottom-aligned position
|
||||
for the selected ceiling.
|
||||
- **nRF52 dynamic apply window:** the post-build hook records the resolved app base, linked app end,
|
||||
internal-ExtraFS/SD/QSPI storage flags, and desired staging ceiling immediately before `EndF`. `motatool` reads that authenticated
|
||||
firmware record and chooses `memory_size` from the actual patch size and bottom-aligned stage address;
|
||||
firmware without the record retains the conservative `0x98000` default. Before writing `APRV`, an
|
||||
internal-ExtraFS/SD/QSPI/hybrid-RAM storage flags, and desired staging ceiling immediately before `EndF`.
|
||||
`motatool` reads that authenticated firmware record and chooses `memory_size` from the actual patch size
|
||||
and bottom-aligned stage address. For a hybrid base it charges only the deterministic flash prefix against
|
||||
that workspace; the retained-RAM suffix is still part of the same hashed logical container.
|
||||
Firmware without the record retains the conservative `0x98000` default. Before writing `APRV`, an
|
||||
internal-store app validates the staged-address bound; an external SD/QSPI app validates the full detools
|
||||
geometry against the application workspace. The bootloader independently parses and validates the same
|
||||
geometry before its first application write. Expanded auto-sized packages require a bootloader
|
||||
@@ -1013,10 +1028,11 @@ retains its `.part` file; it does not erase the unrelated manual-install store.
|
||||
It holds either an ordinary app delta or the exact 41,330-byte v3 container, never both. The boot package
|
||||
bottom-aligns at `0xE2000`; a hash-valid live `EndF` must prove the complete running image ends at or below
|
||||
that address before the first erase. OTAFIX reads each source window before erasing and compacts the
|
||||
payload forward in the same eleven pages to raw `0xE2000..0xEC000`; no separate scratch bank or special
|
||||
application linker exists. GPREGRET `0x6B` plus GPREGRET2 `0xED` selects boot update, while ordinary app
|
||||
apply uses GPREGRET `0x6A` plus the same storage source. Exact installed/candidate capability flags are
|
||||
`0x0A` (`STAGE_CEILING|BOOT_UPDATE`). Ordinary deltas remain dynamically sized, may start below
|
||||
payload forward in the same eleven pages to raw `0xE2000..0xEC000`; no separate flash scratch bank exists.
|
||||
The qualified application linker reserves a 64 KiB SRAM arena only for hybrid application deltas; the
|
||||
bootloader package remains wholly in flash. GPREGRET `0x6B` plus GPREGRET2 `0xED` selects boot update,
|
||||
while ordinary app apply uses GPREGRET `0x6A` plus the same storage source. Exact installed/candidate
|
||||
capability flags are `0x0A` (`STAGE_CEILING|BOOT_UPDATE`). Ordinary deltas remain dynamically sized, may start below
|
||||
`0xE2000`, and reconstruct only below the normal `0xED000` app ceiling. The same signature, explicit
|
||||
confirmation, exact identity, vector, CRC, and single-marker rules as the XIAO path apply. Bootloader FULL
|
||||
admission is isolated from ordinary application FULL policy, and privileged partials are never resumed
|
||||
|
||||
Reference in new issue
Block a user