Support 2 KiB DEFLATE OTA transport
This commit is contained in:
1 parent
066f1abca6
commit
e2da2701da
32 files changed
+3877
-357
No files matched your search
@@ -200,6 +200,12 @@ reported bootloader ABI and codec mask against the selected package. If the
|
||||
version command is unavailable on older firmware, the script warns and falls
|
||||
back to the legacy `ota self` platform marker. If an nRF52 bootloader lacks
|
||||
the required capabilities, install the exact-board OTAFIX bootloader first.
|
||||
Current firmware also reports `maxblk:2048` near the front of both OTA replies.
|
||||
The runner treats a missing marker as the deployed 1 KiB receive limit, rejects
|
||||
a ready 2 KiB package for such a target before transfer, and passes the live
|
||||
limit to `motatool --block-size` when it builds from raw firmware. Thus updating
|
||||
an older target from a release ZIP produces a compatible 1 KiB package while a
|
||||
current target retains the 2 KiB application default.
|
||||
`ota stats` is only an optional EndF version probe. It uses one bounded retry
|
||||
cycle; unsupported firmware or a lost reply falls back directly to the
|
||||
required `ver` command instead of entering an operator continuation loop.
|
||||
@@ -581,7 +587,8 @@ the destination.
|
||||
capabilities, and save the controller's normal radio tuple.
|
||||
3. Select or build one compatible **v2 application** mOTA and verify all block
|
||||
hashes, Merkle root, full-image hash where applicable, identity fields,
|
||||
signature, codec, base, and the firmware's 1024-byte maximum block size.
|
||||
signature, codec, base, and the live firmware's reported maximum block size
|
||||
(1 KiB for legacy replies without `maxblk`, currently 2 KiB otherwise).
|
||||
Version-3 bootloader packages are refused before any target state changes.
|
||||
4. Read every participant's version, save the destination's RXPS state,
|
||||
select the qualified destination policy, and show the confirmation prompt.
|
||||
|
||||
+48
-32
@@ -71,10 +71,18 @@ the authoritative reference for byte-level details.
|
||||
| `CODEC_FULL` / `_SEQUENTIAL` / `_INPLACE` | `0` / `1` / `2` | Section 5 |
|
||||
| `PAYLOAD_TYPE_OTA` | `0x0C` | MeshCore packet type (`src/Packet.h`) |
|
||||
| `MAX_PACKET_PAYLOAD` | `184` | usable bytes per packet (`src/MeshCore.h`) |
|
||||
| Default block size | `1024` | `block_size_log2 = 0x0A` |
|
||||
| Default application block size | `2048` | `block_size_log2 = 0x0B`; deployed 1 KiB application packages remain accepted |
|
||||
| Bootloader-package block size | `1024` | fixed `block_size_log2 = 0x0A` for format 3 |
|
||||
| OTA discovery TX priority | `250` | background (`OTA_TX_PRIORITY`, `src/Mesh.h`) |
|
||||
| OTA active-transfer TX priority | `0` | primary (`OTA_TRANSFER_TX_PRIORITY`, `src/Mesh.h`) |
|
||||
|
||||
Current application firmware publishes its receive/reassembly ceiling as the compact `maxblk:2048` field
|
||||
near the front of both `ota status` and `ota self`. Keeping the field ahead of variable diagnostics ensures
|
||||
it survives the 160-byte remote-admin reply limit. Host updaters must treat an absent field as the deployed
|
||||
legacy capability (`1024`), reject a ready package whose manifest block size exceeds that capability, and use
|
||||
the reported size when turning a raw firmware image into a new application package. This capability does not
|
||||
change the format-3 bootloader package's fixed 1 KiB geometry.
|
||||
|
||||
---
|
||||
|
||||
## 2. Firmware image & the `EndF` trailer
|
||||
@@ -238,7 +246,7 @@ off size field notes
|
||||
7 4 fw_version MAJOR<<24 | MINOR<<16 | PATCH<<8 | pre (comparable uint32)
|
||||
11 4 image_size size of the reconstructed image (BODY||EndF)
|
||||
15 4 payload_size PAYLOAD bytes in this container
|
||||
19 1 block_size_log2 e.g. 0x0A = 1024
|
||||
19 1 block_size_log2 e.g. 0x0B = 2048 (new application default); 0x0A = deployed 1024
|
||||
20 4 merkle_root sha2-256:4 over PAYLOAD blocks (Section 6) - also the manifest_id
|
||||
24 32 image_hash sha2-256:32 of the reconstructed image - SECURITY anchor
|
||||
56 1 codec_id 0=full/raw, 1=detools-sequential, 2=detools-in-place
|
||||
@@ -347,8 +355,9 @@ deltas use `--compression crle`. **Do not reimplement the codec** - use the vend
|
||||
Verifies each PAYLOAD block against the signed `merkle_root` **before** the whole payload exists, so
|
||||
corruption/forgery is localized to a block. Implemented in `MerkleTree.cpp`.
|
||||
|
||||
- **Blocks:** PAYLOAD splits into `BC = ceil(payload_size / B)` blocks, `B = 2^block_size_log2` (default
|
||||
1024). The last block is its real length (**no zero padding**).
|
||||
- **Blocks:** PAYLOAD splits into `BC = ceil(payload_size / B)` blocks, `B = 2^block_size_log2` (new
|
||||
application default 2048; deployed 1024-byte application packages remain valid). The last block is its
|
||||
real length (**no zero padding**). Format-3 bootloader packages remain fixed at 1024 bytes.
|
||||
- **Leaf:** `leaves[i] = sha2-256:4( block_i_bytes )`.
|
||||
- **Internal node:** `node = sha2-256:4( left || right )` (4+4 input bytes).
|
||||
- **Odd level:** an odd count promotes the **last node unchanged** to the next level (no duplication).
|
||||
@@ -545,12 +554,17 @@ OTA_GET_MANIFEST: manifest_id[4] want_mask(uint16) # bit k = send manifest f
|
||||
OTA_MANIFEST: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up to OTA_MF_FRAG=176 B/frag
|
||||
OTA_REQ: manifest_id[4] { block_idx(uint16) want_mask(uint16) }[1..4]
|
||||
# legacy: bit k requests its 160-byte fragment
|
||||
# v2: bit15=marker, bit14=allow transport DEFLATE, bits0..12=fragment bitmap
|
||||
# v2: bit15=marker, bit14=allow transport DEFLATE,
|
||||
# bit13=2 KiB descriptor, bits0..12=fragment bitmap;
|
||||
# bit12 must be zero when bit13 is set (0xFFFF stays legacy)
|
||||
OTA_DATA legacy: manifest_id[4] block_idx(uint16) frag_off(uint16) data[<=160]
|
||||
OTA_DATA v2: manifest_id[4] block_idx(uint16) descriptor(uint16)
|
||||
stream_id[4] data[<=171]
|
||||
# descriptor: bit15=marker, bit14=DEFLATE, bits13..10=fragment,
|
||||
# bits9..0=complete encoded length minus one
|
||||
# deployed 1 KiB descriptor: bit15=marker, bit14=DEFLATE,
|
||||
# bits13..10=fragment, bits9..0=complete encoded length minus one
|
||||
# negotiated 2 KiB descriptor: bit15=marker, bits14..11=fragment,
|
||||
# bits10..0=complete encoded length minus one; DEFLATE iff that
|
||||
# length is strictly less than the manifest-derived raw block length
|
||||
OTA_REQ_PROOF: manifest_id[4] block_idx(uint16)
|
||||
OTA_PROOF: manifest_id[4] block_idx(uint16) n_proof(1) proof[] # n_proof x 4 bytes
|
||||
OTA_GET_LEAVES: manifest_id[4] want_mask(uint16) # bit k = send leaves fragment k; 0xFFFF = all
|
||||
@@ -571,21 +585,24 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
is answering `OTA_GET_LEAVES` with leaves the node already holds, so any node's firmware can be captured.
|
||||
|
||||
- **Negotiated 171-byte fragments:** the deployed profile remains unchanged: `frag_off` is a byte offset and
|
||||
`data[]` carries at most 160 bytes. A new fetcher marks an `OTA_REQ` row as v2 and includes all seven legacy
|
||||
bits on its first request. Old sources ignore/mask the high flags and can therefore return a complete legacy
|
||||
block immediately; receipt of an untagged `OTA_DATA` switches that fetch session to the old geometry. If no
|
||||
v2 data appears by the first adaptive deadline, the fetcher retries with an ordinary legacy mask. New
|
||||
sources answer v2 with a packed descriptor, a repeated 4-byte representation ID, and exactly 171 data bytes
|
||||
except the final fragment. A raw 1 KiB block therefore falls from seven data packets to six. Message type
|
||||
IDs do not change, so multi-hop relays continue to forward request/data/proof packets opaquely with the same
|
||||
priority.
|
||||
`data[]` carries at most 160 bytes. A new fetcher marks an `OTA_REQ` row as v2. For a deployed 1 KiB block,
|
||||
its first request also includes every fragment bit needed by the legacy representation, so an old source can
|
||||
answer it completely. The extended 2 KiB profile reserves bit 12 as zero; this both fits its twelve v2
|
||||
fragments and ensures the deployed `0xFFFF` legacy all-fragments request is never misclassified. Receipt of
|
||||
untagged `OTA_DATA` switches the fetch session to the old geometry. If no v2 data appears by the first
|
||||
adaptive deadline, the fetcher retries with an ordinary legacy mask. New sources answer v2 with a packed descriptor, a repeated 4-byte representation
|
||||
ID, and exactly 171 data bytes except the final fragment. A raw 1 KiB block falls from seven data packets to
|
||||
six; a raw 2 KiB block falls from thirteen to twelve. Message type IDs do not change, so multi-hop relays
|
||||
continue to forward request/data/proof packets opaquely with the same priority.
|
||||
- **Transport-only DEFLATE:** a fetcher sets the v2 DEFLATE-permission bit only when its application includes
|
||||
and configures the decoder. A source may encode each logical block as an independent raw RFC 1951 stream
|
||||
with full stored, fixed-Huffman, and dynamic-Huffman support (`BTYPE=0/1/2`). Compression is used only when
|
||||
strictly smaller, otherwise the source returns raw v2 data. The
|
||||
receiver inflates to the manifest-derived block length, then performs the unchanged Merkle proof and writes
|
||||
the original bytes to staging. Thus existing `.mota` containers, signatures, resume markers, and legacy
|
||||
bootloader apply remain unchanged. The 4-byte `stream_id` is SHA-256:4 of the complete raw or compressed
|
||||
the original bytes to staging. Thus staging and bootloader apply remain uncompressed. Deployed 1 KiB
|
||||
`.mota` containers remain compatible; new 2 KiB containers require a receiver advertising the extended
|
||||
descriptor. Format-3 bootloader packages retain their strict 1 KiB geometry. The 4-byte `stream_id` is
|
||||
SHA-256:4 of the complete raw or compressed
|
||||
representation and appears in every fragment. A receiver locks `{encoding, encoded length, stream_id}` for
|
||||
a block, preventing fragments from independently encoded seeders from being mixed. After one sparse retry,
|
||||
a second stalled interval clears the full in-flight window and switches the session to legacy geometry, so
|
||||
@@ -594,12 +611,11 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
bounds output to the exact logical block length and rejects truncation, malformed streams, and trailing
|
||||
whole bytes. Builds without OTA do not link the decoder. This application capability is independent of the
|
||||
bootloader because transport data is inflated before the unchanged staged container is written.
|
||||
- **Adaptive flight size is not signed block size.** The container continues to use 1 KiB Merkle leaves and
|
||||
each slot is one existing 1 KiB block. A clean link changes how many of those blocks one `OTA_REQ` names:
|
||||
1, then 2, then 3, then the compiled cap. The manifest stores `block_size_log2`, so 3 KiB is not a valid
|
||||
logical geometry; enabling 2 KiB would require larger device reassembly buffers and new-package/bootloader
|
||||
validation while saving only one request/proof pair per 2 KiB. It does not address premature retries, which
|
||||
were the dominant packet multiplier.
|
||||
- **Adaptive flight size is not signed block size.** Each slot holds one manifest-defined block: 1 KiB for
|
||||
deployed application containers or 2 KiB for new ones. A clean link changes how many complete blocks one
|
||||
`OTA_REQ` names: 1, then 2, then 3, then the compiled cap. It never changes block geometry. Two KiB halves
|
||||
the leaf, block-request, and proof count for a payload while giving each independent DEFLATE stream a larger
|
||||
history window.
|
||||
- **Append-only request-window compatibility:** the first four-byte request row is exactly the original
|
||||
`block_idx + want_mask` body. Old sources decode that row and ignore appended bytes. New sources queue all
|
||||
rows. If a new fetcher meets an old source, the unserved tail rows eventually time out and are recovered as
|
||||
@@ -614,8 +630,8 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
half-duplex radios: re-requesting a whole multi-fragment burst let the periodic retry (a transmit) collide
|
||||
with the tail of the in-flight burst and drop the same fragment forever - a hang. Requesting only the hole
|
||||
removes the burst, so there is nothing to collide with. The block/manifest mask matches the 16-bit
|
||||
reassembly bitmap (<=16 fragments/block; legacy 1 KB blocks = 7, raw v2 = 6). `OTA_PROOF` is a single packet
|
||||
and needs no mask.
|
||||
reassembly bitmap (<=16 fragments/block; 1 KiB is 7 legacy or 6 raw-v2 fragments; 2 KiB is 13 legacy or 12
|
||||
raw-v2 fragments). `OTA_PROOF` is a single packet and needs no mask.
|
||||
- **Data and proof remain separate packets, without a normal extra round trip.** A server retains requested
|
||||
blocks in a bounded descriptor queue, admits at most one response per main-loop pass, and sends one
|
||||
`OTA_PROOF` after each block's requested fragments. Before admitting that proactive proof, the source leaves
|
||||
@@ -631,14 +647,14 @@ OTA_LEAVES: manifest_id[4] frag_idx(1) frag_total(1) bytes[] # up
|
||||
|
||||
| message | fixed overhead | payload/packet |
|
||||
|---|---|---|
|
||||
| `OTA_DATA` legacy | 9 B (type+mid4+idx2+off2) | `OTA_FRAG_DATA = 160` -> 7 frags per 1 KB block |
|
||||
| `OTA_DATA` v2 | 13 B (legacy header + stream ID) | `OTA_FRAG_DATA_V2 = 171` -> 6 raw frags per 1 KB block; fewer when compressed |
|
||||
| `OTA_DATA` legacy | 9 B (type+mid4+idx2+off2) | `OTA_FRAG_DATA = 160` -> 7 raw frags/1 KiB or 13/2 KiB |
|
||||
| `OTA_DATA` v2 | 13 B (legacy header + stream ID) | `OTA_FRAG_DATA_V2 = 171` -> 6 raw frags/1 KiB or 12/2 KiB; fewer when compressed |
|
||||
| `OTA_MANIFEST` | 7 B | `OTA_MF_FRAG = 176` -> signed manifest ~ 2 frags |
|
||||
| `OTA_HAVE` | 12 B | 10 rows x 16 B per fragment |
|
||||
| `OTA_PROOF` | 8 B | up to ~44 sibling digests (>> any real tree) |
|
||||
|
||||
A served mota supports up to `OTA_MAX_BLOCK/4` leaves in the default 4 KB proof scratch (<=1024 blocks ~ 1 MB
|
||||
payload); larger self-images pass a bigger scratch buffer.
|
||||
A served mota supports up to 1024 leaves in the default 4 KiB proof scratch (about 2 MiB of payload at the
|
||||
new 2 KiB default, or 1 MiB for a deployed 1 KiB container); larger self-images pass a bigger scratch buffer.
|
||||
|
||||
### 8.6 Temporary-radio and transfer boundary
|
||||
|
||||
@@ -754,7 +770,7 @@ class MotaSource {
|
||||
};
|
||||
```
|
||||
|
||||
To serve an external mota the node reads its manifest-minus-leaves + `leaves[]` into RAM (<=4 KB for <=1024
|
||||
To serve an external mota the node reads its manifest-minus-leaves + `leaves[]` into RAM (<=4 KiB for <=1024
|
||||
blocks) and streams payload blocks from the source on demand; proofs are generated from the read leaves.
|
||||
|
||||
### 10.2 The `mota-seeder` transport (`MotaSeederProto.h`)
|
||||
@@ -784,8 +800,8 @@ status: 0 = OK, non-zero = error (out of range / past EOF).
|
||||
```
|
||||
|
||||
`SerialMotaSource` splits logical reads into replies of at most 192 payload
|
||||
bytes. A manifest's leaf table can exceed 256 bytes and payload blocks are
|
||||
normally 1 KiB; requesting either in one transaction can overrun common USB
|
||||
bytes. A manifest's leaf table can exceed 256 bytes and new payload blocks are
|
||||
normally 2 KiB; requesting either in one transaction can overrun common USB
|
||||
CDC/UART receive rings even though the host successfully wrote the complete
|
||||
reply. Chunking is internal to the transport and does not change `OP_READ` or
|
||||
the `MotaSource` random-access contract.
|
||||
|
||||
@@ -235,12 +235,12 @@ trusted - every kept block is checked against the target's own fingerprints, so
|
||||
just means those blocks are fetched over the radio (correct result, only slower).
|
||||
|
||||
The node fetches from one source as **primary traffic**, with bounded adaptive request flights. Every session
|
||||
probes with one 1 KiB block, then clean flights grow `1 -> 2 -> 3 -> 4` blocks on RAK3401 OTA builds. All blocks
|
||||
probes with one manifest block, then clean flights grow `1 -> 2 -> 3 -> 4` blocks on RAK3401 OTA builds. New
|
||||
application packages use 2 KiB blocks; deployed 1 KiB packages remain supported. All blocks
|
||||
in a flight share one request packet, and the receiver stays silent until the source/relays finish returning
|
||||
them. A recovery halves the next flight. Retry timing follows the active SF/BW airtime, duty budget, and path
|
||||
length, so faster settings recover sooner without a fixed one-second request colliding with a valid multi-hop
|
||||
response. The signed block size itself remains 1 KiB. Mesh repeaters carry the packets only while their
|
||||
temporary-radio windows are active.
|
||||
response. Mesh repeaters carry the packets only while their temporary-radio windows are active.
|
||||
Check progress with `ota status`.
|
||||
|
||||
If a `folder` pull loses its link mid-transfer, `ota status` shows **paused** - the host keeps the
|
||||
|
||||
+84
-39
@@ -3,25 +3,25 @@
|
||||
> Status: **published prerelease for controlled, recoverable lab use.** On
|
||||
> 29-Aug-2026 steps 1-9 completed on the target RAK3401 and are byte-for-byte
|
||||
> identical to the corresponding transitions in the physically passed
|
||||
> `fd98bc90` chain. The replacement step 10 installs the current `b40d2e6c`
|
||||
> `fd98bc90` chain. The replacement step 10 installs the current `3f6eddd5`
|
||||
> endpoint. That replacement package and the hardened current runner have
|
||||
> passed offline reconstruction plus both required bootloader simulators, but
|
||||
> passed offline reconstruction plus the exact deployed bootloader simulator, but
|
||||
> have not had a new physical end-to-end run. Use the lab gate and keep local
|
||||
> DFU/SWD recovery available. Multi-hop and alternate-bandwidth estimates
|
||||
> remain planning data, not physical qualification.
|
||||
|
||||
The prerelease is
|
||||
[`rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c`](https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c).
|
||||
[`rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.6-3f6eddd5`](https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.6-3f6eddd5).
|
||||
Use this asset:
|
||||
|
||||
```text
|
||||
RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
|
||||
RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
```
|
||||
|
||||
- prerelease ZIP SHA-256: `8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4`
|
||||
- inner `SHA256SUMS.txt` SHA-256: `29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a`
|
||||
- endpoint image SHA-256: `a7761da8af1f2447d7f01d72e2b1456b99c0e874a165d6483f48ab1f58e8d905`
|
||||
- endpoint EndF body hash: `1FA6AAA3C28D8BD8`
|
||||
- prerelease ZIP SHA-256: `135783dd8777490db2422a7543f7d80e3cf03f621d5ac362da930abbc1850dc4`
|
||||
- inner `SHA256SUMS.txt` SHA-256: `93835ed497c579be0a2322292e7f15ab095e7c8ab7892833fe7df3130dbdaba4`
|
||||
- endpoint image SHA-256: `cd1e9b819f9e918f9f2ceaf37b8a74bf242e2be78f319af2cf02a42d7729d0f4`
|
||||
- endpoint EndF body hash: `9BD7CF682EE065AE`
|
||||
|
||||
`--verify-only` remains the safe default. Live preflight and installation of
|
||||
this prerelease require `--accept-test-candidate`. That switch bypasses only
|
||||
@@ -37,11 +37,20 @@ target:
|
||||
- hardware: `RAK_3401`
|
||||
- role: `RAK_3401_repeater_lora_ota_no_external_sensors`
|
||||
- start: `v1.16.7.0-c1caa5ad`, EndF `71F4026CBE4B8B74`
|
||||
- endpoint source: MeshCore `b40d2e6cb2a185db740175e8b161ea71736d19d9`
|
||||
- endpoint label: `v1.17.1.5-halo-keymind-cascade-dev-b40d2e6c`
|
||||
- endpoint packed version: `0x01110105`
|
||||
- endpoint source: MeshCore `3f6eddd5c20f1fa3df6ba1cc208efbe3e2fffb43`
|
||||
- endpoint label: `v1.17.1.6-halo-keymind-cascade-dev-3f6eddd5`
|
||||
- endpoint packed version: `0x01110106`
|
||||
- deployment target key: `63d8df6387eaffd2e25db7d2a8ad967a65202182a48d681d7e7a9260f917280d`
|
||||
|
||||
The exact starting application is the
|
||||
[`lora-ota-v1.16.07-halo-keymind-cascade-dev-3a6af3bf`](https://github.com/mikecarper/MeshCore/releases/tag/lora-ota-v1.16.07-halo-keymind-cascade-dev-3a6af3bf)
|
||||
RAK3401 asset. Its outer ZIP SHA-256 is
|
||||
`58ead2ea18efafda186bc9d983fe08cdf65f69b0e5a3a1c72c18fe35948a254c`;
|
||||
its 566,912-byte `firmware.bin` SHA-256 is
|
||||
`4301fc63ebd661c70f9bc40e7eca44d3cb8a358cb3c0075e700d9676a57478cb`.
|
||||
The endpoint build ZIP SHA-256 is
|
||||
`a9fca9be145a96feb650b57c43b5ed173d74d2f3194da12e31b1c54352635b56`.
|
||||
|
||||
### Retained and omitted hardware support
|
||||
|
||||
Despite the legacy `no_external_sensors` target name, the endpoint retains the
|
||||
@@ -62,7 +71,7 @@ identity without helping this route.
|
||||
Do not use the chain on another target ID, hardware family, starting image, or
|
||||
firmware body hash. The runner checks all four.
|
||||
|
||||
## Why Preview 5 and the old staging ceiling work
|
||||
## Why the deployed OTAFIX2.4 and old staging ceiling work
|
||||
|
||||
The deployed bootloader is not changed and no package uses the newer expanded
|
||||
`0xED000` ceiling. Each package remains bottom-aligned below the old
|
||||
@@ -85,19 +94,30 @@ workspace.
|
||||
Every package in this candidate is an ordinary application container using
|
||||
format ABI 2 and in-place codec 2. The historical bridge applications scan the
|
||||
bootloader capability marker byte-by-byte, so they recognize the released
|
||||
Preview 5 RAK4631 marker at absolute address `0xFCDEA` even though it is two
|
||||
bytes off a word boundary. The `b40d2e6c` endpoint retains that exact Preview
|
||||
5 application-update fallback. Its separately compiled internal
|
||||
bootloader-update feature remains unavailable on Preview 5 because privileged
|
||||
self-update requires a current ABI 3 marker with the exact storage profile.
|
||||
`0.9.2-OTAFIX2.4` marker at absolute address `0xFCD16`. That immutable marker
|
||||
advertises ABI 2, codec mask `0x0004`, and internal storage profile 0. The
|
||||
`3f6eddd5` endpoint retains the matching legacy application-update fallback.
|
||||
Its separately compiled internal bootloader-update feature remains unavailable
|
||||
because privileged self-update requires a current ABI 3 marker with the exact
|
||||
storage profile.
|
||||
Installing this chain therefore does not replace the bootloader, and the final
|
||||
application can still accept a future valid ABI-2/codec-2 application update.
|
||||
|
||||
The schema-2 exhaustive search checked 17,371 candidate geometries and found
|
||||
121 feasible forward edges in the declared 33-image inventory. Fourteen
|
||||
ten-package routes tied for the minimum package count; the selected route has
|
||||
the smallest total container size among them. That minimum applies to the
|
||||
declared inventory, not every conceivable firmware image. The selected route
|
||||
The exact installed bootloader is the
|
||||
[`0.9.2-OTAFIX2.4` RAK4631 asset](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/download/0.9.2-OTAFIX2.4/wiscore_rak4631_board_bootloader-0.9.2-OTAFIX2.4_s140_6.1.1.zip).
|
||||
Its outer ZIP SHA-256 is
|
||||
`5e29e7a8982cc2ed5f8868435556eb09387250fe5b9a7e8443bac325247dfed1`;
|
||||
its 191,040-byte `sd_bl.bin` SHA-256 is
|
||||
`a97f834388db0f1da6c29f6d8f458ff6d3e6a26034709044566384838919bb23`.
|
||||
The exact simulator was compiled from release commit
|
||||
`d73de8372e89b8ef352747c8bc7a1aaeab80fbfe`; its executable SHA-256 is
|
||||
`9dacf24b1023fe2f4c620419417649ccc9538dd4a611b47bc19b7fedb97ceba6`.
|
||||
|
||||
The schema-2 exhaustive search checked 1,799 candidate geometries and found 12
|
||||
feasible forward edges in the declared exact 12-node inventory. It found one
|
||||
shortest ten-package route and selected the smallest total container bytes as
|
||||
the secondary objective. This minimum applies only to that declared inventory
|
||||
and geometry table, not every conceivable new bridge image. The selected route
|
||||
is:
|
||||
|
||||
| Step | From | To | Workspace | Package | Margin |
|
||||
@@ -111,20 +131,28 @@ is:
|
||||
| 7 | 1.16.9.113 | 1.16.9.117 | `0x8B000` | 139,637 | 0 |
|
||||
| 8 | 1.16.9.117 | 1.16.10.0 | `0x7E000` | 174,202 | 20,480 |
|
||||
| 9 | 1.16.10.0 | 1.17.1.3 | `0x6B000` | 271,569 | 0 |
|
||||
| 10 | 1.17.1.3 | 1.17.1.5 | `0x74000` | 174,744 | 61,440 |
|
||||
| 10 | 1.17.1.3 | 1.17.1.6 | `0x76000` | 197,825 | 28,672 |
|
||||
|
||||
Total mOTA transfer data is 1,384,252 bytes. `ROUTE.json`, `CHAIN.csv`, and
|
||||
Total mOTA transfer data is 1,407,333 bytes. `ROUTE.json`, `CHAIN.csv`, and
|
||||
`validation-results.json` in the bundle pin the exact geometry and image hash
|
||||
for every transition.
|
||||
|
||||
Steps 1-9 are byte-for-byte identical to the transitions that passed on the
|
||||
physical RAK3401. The replacement step 10, from the physically reached
|
||||
`FE65A6135A1E7B3F` v1.17.1.3 body to `1FA6AAA3C28D8BD8`, has offline
|
||||
`FE65A6135A1E7B3F` v1.17.1.3 body to `9BD7CF682EE065AE`, has offline
|
||||
qualification only. All ten packages passed independent container checks,
|
||||
zero-filled and erased-workspace reconstruction, the OTAFIX 2.4.1 Preview 5
|
||||
simulator, and the current OTAFIX simulator. Those results prove the package
|
||||
bytes and apply geometry; they are not a physical claim for the replacement
|
||||
step 10 or the hardened runner.
|
||||
zero-filled and erased-workspace reconstruction, and the exact deployed
|
||||
`0.9.2-OTAFIX2.4` simulator. Those results prove the package bytes and apply
|
||||
geometry; they are not a physical claim for the replacement step 10 or the
|
||||
hardened runner.
|
||||
|
||||
The 171-byte transport DEFLATE support lands only in the final `3f6eddd5`
|
||||
application. The starting receiver and every receiver that accepts steps 1-10
|
||||
lack that inflate path, so all ten legacy-chain transfers remain raw and the
|
||||
1,407,333-byte total above is not automatically reduced over LoRa. A later
|
||||
update can negotiate transport compression after this endpoint is running;
|
||||
the application inflates before staging, and the immutable bootloader still
|
||||
applies an ordinary ABI-2/codec-2 container.
|
||||
|
||||
## Host requirements
|
||||
|
||||
@@ -137,7 +165,7 @@ No password or device is needed:
|
||||
|
||||
```bash
|
||||
python3 tools/lora_ota/rak3401_mota_chain.py \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip \
|
||||
--work-dir ./rak3401-mota-chain-work \
|
||||
--motatool /path/to/motatool \
|
||||
--verify-only
|
||||
@@ -166,7 +194,7 @@ Restore the test start locally with
|
||||
export MESHCORE_ADMIN_PASSWORD='password'
|
||||
|
||||
python3 tools/lora_ota/rak3401_mota_chain.py \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip \
|
||||
--bundle /path/to/RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip \
|
||||
--work-dir ./rak3401-mota-chain-work \
|
||||
--controller-serial /dev/ttyACM0 \
|
||||
--source-tcp 192.168.1.51:5001 \
|
||||
@@ -188,6 +216,23 @@ zero and restores it at the endpoint. Use that option only where the selected
|
||||
frequency and local duty-cycle rules permit a full transmit budget; omit it
|
||||
otherwise.
|
||||
|
||||
## Superseded `b40d2e6c` prerelease
|
||||
|
||||
The immediately preceding prerelease remains pinned for offline provenance:
|
||||
|
||||
- release: [`rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c`](https://github.com/mikecarper/MeshCore/releases/tag/rak3401-mota-v1.16.07-c1caa5ad-to-v1.17.1.5-b40d2e6c)
|
||||
- asset: `RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip`
|
||||
- outer ZIP SHA-256: `8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4`
|
||||
- inner `SHA256SUMS.txt` SHA-256: `29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a`
|
||||
- endpoint image SHA-256: `a7761da8af1f2447d7f01d72e2b1456b99c0e874a165d6483f48ab1f58e8d905`
|
||||
- endpoint EndF body hash: `1FA6AAA3C28D8BD8`
|
||||
|
||||
Its first nine packages are byte-for-byte identical to the current release and
|
||||
the physical `fd98bc90` evidence. Its different step 10 had offline
|
||||
qualification only. The runner recognizes this exact archive separately and
|
||||
keeps live use gated; it is not interchangeable with the current `3f6eddd5`
|
||||
asset.
|
||||
|
||||
## Legacy `fd98bc90` physical and SWD qualification
|
||||
|
||||
The earlier local candidate remains the exact physical evidence source for
|
||||
@@ -207,9 +252,9 @@ deployed `0.9.2-OTAFIX2.4` bootloader and a Heltec V4 source at 909.950 MHz /
|
||||
500 kHz / SF5 / CR5. Every intermediate EndF hash matched. Independent SWD
|
||||
readback then matched the endpoint application, original bootloader,
|
||||
SoftDevice/MBR, and UICR byte-for-byte. Steps 1-9 are also the exact first nine
|
||||
packages in the `b40d2e6c` prerelease. The two candidates have different step
|
||||
10 packages and endpoints, so the physical result and step-10 timing must not
|
||||
be attributed to `b40d2e6c`.
|
||||
packages in the `b40d2e6c` and `3f6eddd5` prereleases. All three candidates
|
||||
have different step 10 packages or endpoints, so the physical result and
|
||||
step-10 timing must not be attributed to either later prerelease.
|
||||
|
||||
### Measured direct-link timing for `fd98bc90`
|
||||
|
||||
@@ -291,17 +336,17 @@ never skip a step even when a later package appears in `ota ls`.
|
||||
### 1. Verify and extract the asset
|
||||
|
||||
```bash
|
||||
sha256sum RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
|
||||
unzip RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c.zip
|
||||
cd RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.5-b40d2e6c
|
||||
sha256sum RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
unzip RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5.zip
|
||||
cd RAK3401-update-chain-v1.16.7-c1caa5ad-to-v1.17.1.6-3f6eddd5
|
||||
sha256sum -c SHA256SUMS.txt
|
||||
for package in motas/*.mota; do motatool verify "$package" || exit 1; done
|
||||
```
|
||||
|
||||
The outer hash must be
|
||||
`8e7f0c2565f37d4372fa51dc3ba72b591e7f38b74be09303e48641623f99bce4`,
|
||||
`135783dd8777490db2422a7543f7d80e3cf03f621d5ac362da930abbc1850dc4`,
|
||||
and the SHA-256 of the extracted `SHA256SUMS.txt` must be
|
||||
`29a02a6c4c83eafa80168a80562b0ded5d8432e6cc02cfaca289cb2ed426681a`.
|
||||
`93835ed497c579be0a2322292e7f15ab095e7c8ab7892833fe7df3130dbdaba4`.
|
||||
Do not continue after any checksum or verification error.
|
||||
|
||||
### 2. Record and prepare the destination
|
||||
|
||||
Reference in new issue
Block a user