pureboot: artifact roles, wrong-chip refusal, and misplaced-loader re-homing

The README's deployment section now says what each build artifact is for:
the .hex is the programmer artifact (self-addressed into the top slot),
the .bin the self-update image — bare slot bytes a programmer would put
at address 0, where a boot-sectioned mega cannot even heal itself (SPM
only runs from the boot section) but a patched-vector chip runs the
position-independent copy and re-homes a build through the ordinary
--update-loader flow: the staging install and the word-0 redirect both
execute outside page 0's slot, so the running-slot guard never blocks it.
pbrehome.py is the acceptance test (misplaced at 0, guard intact,
re-home, app flash over the stale copy, banner); the staging slot is the
one position that cannot re-home itself, documented. The preflight's
wrong-chip refusal and loader_image's handling of padded images (peeled
to the slot content by the embedded base) are documented and the padded
case pinned in the planner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 21:18:10 +02:00
parent 9cbe2d682d
commit 5f9e736d66
4 changed files with 133 additions and 0 deletions

View File

@@ -111,6 +111,24 @@ write `0xff` (per page for flash, per byte for EEPROM).
## Deployment
The build leaves three artifacts per chip. The ELF is a container for the
tests and objcopy — never flashed. The **.hex is the programmer artifact**:
it carries its own addresses and lands the loader in its top slot,
touching nothing else. The **.bin is the self-update image** — the slot's
bare bytes with no addressing, which a programmer would put at address 0.
On a boot-sectioned mega a copy at 0 is dead weight (SPM only executes
from the boot section, so it cannot even heal itself — reflash the .hex);
on the patched-vector chips it *runs* (the image is position-independent
and reset enters word 0), reports its canonical geometry, and the ordinary
`--update-loader` flow re-homes a build into the top slot — the staging
install and the word-0 redirect both execute from copies outside page 0's
slot, so the running-slot guard never blocks the flow (`pureboot.rehome`
is the acceptance test). Flashing the application afterwards overwrites
the stale copy, vector surgery included. The one position that cannot
re-home itself is the staging slot: installing the staging copy would hit
the running copy's own guard and the update stops at its verify — flash
the .hex instead.
**Boot-sectioned megas**: program the loader at `flash slot` with an
external programmer. Every such mega has a BOOTSZ step whose boot section
is exactly the loader slot — 512 B, the second-smallest step on the 8 KiB
@@ -161,6 +179,14 @@ loader itself as its own staging loader. The image is the loader's own 512
bytes as a raw binary, or the Intel HEX the build emits beside it, which
links the loader at its base inside an otherwise blank flash image:
The preflight refuses an image built for another chip: the info block
embedded in every pureboot binary (signature, page size, loader base,
EEPROM size, flags) must match the device's own, and the error names both.
Die revisions share their base signature and geometry, so their images are
interchangeable — as the silicon is. `loader_image()` also accepts a
padded image (a raw .bin padded from 0, or a whole-flash read-back with
the loader resident) and peels it to the slot content by the embedded base.
1. The staging slot `[baseslot, base)` is saved to a host-side state file
(on the 1 KB tiny13s that is the whole application, vectors included).
2. The resident installs the identical update image there. On the