ww_dfu: transcripts and chunky¶
How the exchanges on the scenario pages are recorded and checked, and the chunky records
ww_dfu carries as raw bytes.
About the transcripts and their tests¶
Every transcript on the scenario pages (A/B, one slot) is recorded by its test in tests/dfu_scenarios (src/scenarios.rs) and checked
against the file included there: a test fails when the exchange changes, until just dfu-transcripts writes the new
one and the change is looked at. So the bytes here are the bytes the current code sends.
- Two views. The readable one decodes each
ww_linkmessage with the API model of the firmware that runs: method, arguments, result, sink writes (⇐) and stream items (⇒), credit and sideband events, with each message's size and first bytes. The wire view shows every unit of the medium: USB packets, UDP datagrams or WebSocket frames. Both are taken on the device side. - Left out: link setup (
Nop,GetDeviceInfo,LinkSetup,LinkReady, the WebSocket upgrade), which is the same on every connection and carries crate versions that change with every release, and pings. WebSocket frames from the host are masked with a random key; they are shown unmasked with the key zeroed. - The device is a simulation with real firmware logic on a simulated flash: two banks with NOR semantics (programming
only clears bits), 8 chunks of 64 bytes, erase blocks of 128 bytes, a plan area, an apply journal, and power cuts
injected at a given step. It runs as a task next to the host on one single-threaded runtime, as firmware runs on one
core, so every run of a scenario interleaves host and device the same way (over the simulated USB the clock is paused
as well, timeouts don't depend on the machine's load). A reset restarts the device task on the same link endpoint,
running the bootloader logic first; the host reconnects as it would to a real device. The firmwares are generated
ww_dfuservers (tests/dfu_app_api,tests/dfu_lean_app_api,tests/dfu_updater_api). - The plan format is a small stand-in for chunky's (a 35-byte header with a toy signature; steps
01 block,02 dst src hash,03 dst hash len data), so the step bytes in the transcripts are readable. Hashes are SHA-256 truncated to 8 bytes, as in chunky's format 2. The real records are in The other side: chunky. - The links are the real ones from
ww_deviceup: WebSocket and UDP on localhost throughwire_weaver_client'swsandudptransports; USB is simulated bywire_weaver_client::in_process::usb_device, which runs the real USB framing on both ends with full-speed (64 B) or high-speed (512 B) packets, but not USB itself (enumeration, data toggles, NAKs). RTT is not used: with a debug probe attached a device is flashed directly, without a bootloader. - Changing a scenario's link is one line,
const LINK: Link = ...in its test (UART and CAN will joinLink).
The other side: chunky¶
ww_dfu is the transport half of an update. What it carries, and the code that checks and applies it on the device,
is chunky (chunky_mcu_updater, public).
SPEC.mdis the protocol in octets (a draft): the records, the receiver's checks in order, delivery modes, resume and segmentation, with annotated vectors. Its §10 describes this carrier.chunky_format/src/lib.rsdefines the records. They areshrink_wraptypes with a fixed little endian layout, and the same bytes are read by Rust (with or withoutBufReader) and by a generated C reader:- Descriptor (
CKY2): a 64-byte body (product, version, security counter, image length, geometry, SHA-256 of the image) and a 64-byte seal over a domain string and exactly the body's bytes. The scheme is Ed25519, or SHA-256 only for an unsigned build, which a signed device refuses. - Plan (
CKP2): a 145-byte header (geometry, step count, body length, the descriptor), then the steps. A step is ahead_tailenum read throughStep::view: ERASE is 3 bytes, COPY 13, WRITE 13 plus one chunk. - Manifest (
CKM2): a 13-byte header and one entry per chunk, a hash (8 bytes of SHA-256), blank or unreadable.
- Descriptor (
chunky/src/apply.rsis the apply engine: it runs an untrusted plan against the DFU slot overembedded_storage, one step per call, checking the descriptor first, then every COPY source and WRITE hash, then the whole image.chunky_wwis the real device side of this API (scenario2C-APP-STEP, no_std):ChunkyNodeanswersUpdateNode,BootControl,InventoryandStagewith the apply engine and a boot backend (embassy-boot swap, hardware bank swap), so a firmware's generated handlers only forward. Its one error without a variant here isError::Custom(1)(chunky_ww::CUSTOM_NOT_BLANK): the chunk a COPY or WRITE goes to is not blank, nothing was written, and the host reads the manifest again and plans again (SPEC §10).chunky_ww_hostis the host side: node, manifest, plan, steps pipelined up toStageLimits::in_flight(PreparedCall::send), timeouts from the flash layout, reboot into the trial, the device's confirm or revert.ww dfu flashis built on it, and its end-to-end tests run it againstchunky_wwon chunky's simulated flash over the simulated USB ofin_process.
The raw octets are these records: Stage::begin and StageBulk::begin_bulk take the 145 header bytes, Stage::write
carries one step's data, and the StageBulk::plan sink carries the body as segments. What a node answers with
Error::BadSignature, BelowFloor, WrongGeometry or VerifyFailed is what chunky decides; WrongGeometry is
what chunky and the spec call GeometryMismatch.
Status. Chunky does not implement the ww_dfu traits yet (chunky NET-1 is open): it is the device core and the
record format that ww_dfu carries, and the server side that exposes them as UpdateNode, Stage and the rest is
to come. Until then the exchanges on the scenario pages are recorded with the stand-in plan above.