The loader-slimming track, and the firmware rule in CLAUDE.md - #582
Conversation
The owner ruled on the loader audit (2026-09-28): slim the loader per the audit; the hardware clock is always UTC; keep "no firmware calls after the handover" and write it down; redo #539 inside this track rather than land it; end at least as secure. - CLAUDE.md gains the one briefed paragraph: no firmware code runs on the CPU after ExitBootServices; every firmware call ToyOS makes is the loader's. - issues/boot-media/the-loader-does-only-what-must-precede-the-handover.md is the track: seven stages in dependency order, each with an exit a test or gate checks, and #539's pieces placed or deleted (--boot-next, the panic handler's fall and its two issues, the bootvars::state line). - The anti-rollback floor stage keeps the audit's shrink in a form that is argued not to weaken anything: one floor per key (Scope::Machine's) for every loader, so the owner's floor is unchanged and a throwaway key's floor stops being resettable by a write of the log partition's GUID; stale-floor deletion goes, since it never touched a loader's own floor. - issues/boot-media/the-machine-updates-itself-without-ubuntu.md loses what the new track owns: stage 2's Ubuntu items and exit, and the A/B-rollback later stage. The NVMe install stays there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j
…led accepted CLAUDE.md's Firmware paragraph scoped to ToyOS's own calls, since the old wording was false of SMM and the xHCI legacy-ownership handshake. Stage 3 of the loader track now records the owner's ruling on the per-key floor's cost as the stage's decision, rather than the track's own argument for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j
Review, round 1, at 97e8fb3CI: BLOCKER
NOTE
REMOVE
SEND BACK |
… rebuilt on the owner's rulings CLAUDE.md: ToyOS calls no UEFI runtime service; every UEFI call is the loader's. PSCI on ARM64 and the xHCI legacy-ownership handshake are not UEFI calls, which closes both CLAUDE.md blockers. The track: - Stage 1 is what #583 lands: UTC, the hex dump, ten comments, the KernelArgs layout identity, IA32_TSC_ADJUST read by the kernel. The loader's TCO arm stays; wall_clock_utc is the test a zone reds. - Stage 2 scopes every volume lookup to the boot disk with exactly one match, and asks firmware once per pass. - Stage 3 compares one floor per key on a signed security version; the accepted cost of refusing older builds is gone with the build time. - Stage 4 is new: current uefi, the loader's own panic handler, the unsound allocations and relocation unsafes gone, typed KernelArgs, one CRC32. - Stage 5 adopts the Android/libabr tries rules as pure host-tested toyos-update decisions: fresh slot A untried with 3 tries, no bootable slot powers off, the good flag set only past a health gate, and controls for a good flag left set and a floor raised to the table's version. - Stage 8 is new: the kernel arms the TCO before mm::init and takes the read-back the TCO issue's exit needs; only then does the loader's arm go. - Stage 9 rewrites the wedged-report issue's exit and gives the kernel harvest's stale-record check. - Every #539 piece is placed or listed as deleted. the-machine-updates-itself-without-ubuntu.md: stage 2 gets its exit back, and names its wait on the track's --boot-first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j
Review, round 2, at dc6aebcCI: Round-1 BLOCKERs
BLOCKER
NOTE
REMOVE
SEND BACK |
…s controls can fail CLAUDE.md's Firmware paragraph now reads as the orchestrator ruled: the kernel calls no UEFI service, and every UEFI call is the loader's, before ExitBootServices. The loader's GetVariable, SetVariable, GetTime and ResetSystem come before the handover, so the old wording was false of it. The track: - Stage 1 matches #583 at 8565cc5: KernelArgs::layout (0x5459_0001), kernel_args_layout_refused with the loader-writes-no-layout actuator, the probe's realtime=, and the kernel's IA32_TSC_ADJUST line. No test fails without that line, and the stage says so. - Stage 3 drops "why nothing is weaker". A security version admits an older build the same key signed at that version. The floor issue records that as the owner's accepted cost. Raising the version is a reviewed PR that edits one constant and names the security fix. The loader deletes the build-time ToyOSImageFloor- variables instead of leaving them behind. - Stage 4's panic handler writes loader.log and powers off, never resets. Its test panics on a floor planted in 9 bytes, a failure the machine causes. KernelArgs' layout word rises to 0x5459_0002, and kernel_args_last_layout_refused fails if it does not. - Stage 5 refuses a signed kernel the loader cannot load inside verify, so the other slot boots instead of the pass bricking the machine. An install's priority rises above the kept slot's. update --good, run by init at the health gate under the slots claim, writes the good flag, and an image without that claim is never good. Each rule gets a named guest control: update_floor_waits_for_good, update_readonly_stick_boots_nothing (red under `let persisted = true;`) and update_unloadable_kernel_boots_the_other_slot. The slot-table oracle is decoded without production code. - Every #539 piece the review listed is placed or deleted. The #539-only issue names and the stack-offset closure (#584's) are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review, round 3, at 0a76929CI: Round-2 BLOCKERs
BLOCKER
NOTE
REMOVE
SEND BACK |
…d "up" is a signal - Stage 4 keeps an `offset_of!` assert for the layout word and every field before it, and the typed `KernelArgs` changes only fields after the word, so moving `layout` fails to build. - `loader_panic_powers_off` looks for the handler's own `loader: panicked at` line, and the refused-floor site stops writing its reason to `loader.log` before it panics. - Stage 5 defines the health gate: `[boot] up` names the services, and each writes one byte on a `ready` pipe init endows, since the tree has no readiness signal. `update_dead_service_is_never_good` fails when init marks good right after spawning. - Stage 3 quotes the line it adds to the review prompt, and lists deleting the floor issue's "newer than the running image" line. - Any failure after `verify` in `start_kernel` that the image causes is a refusal of that slot; the unloadable-kernel test also finds the non-ELF refusal of B; the once variant and its `LAYOUT` bump move to stage 5; `Rig::update` is the path that signs at +1. - Removed: "at least as secure as it starts", and the aarch64 RTC header's `GetTime` clause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review, round 4, at 8e382e0CI: Round-3 BLOCKERs
BLOCKERNone. NOTE
REMOVE
LAND AFTER NAMED CHANGES |
…d-picked Restates the track's LAYOUT/LAST_LAYOUT literals as `0x5459_0000 | size_of::<KernelArgs>()`, so a size change moves the word on its own and `kernel_args_last_layout_refused` catches it; names the remaining gap a same-size reorder or equal-size type swap leaves, closed by pinning every field's offset (not only the prefix's) and `assert!(LAYOUT != LAST_LAYOUT)`; states how stage 5's once variant, which keeps the struct's size, still moves the word and how the kernel refuses an unrecognized discriminant by name instead of reading it out of range; gives `update_dead_service_is_never_good`'s image a live service beside the dead one, so "any named service wrote its byte" is distinguished from "every one did"; has stage 5's PR list which `start_kernel` refusals are the image's; and drops the PR body's Unsure bullet on #583's pushed head, now stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Places the owner's rulings on the loader audit: the loader-slimming track (
issues/boot-media/the-loader-does-only-what-must-precede-the-handover.md), one rule in rootCLAUDE.md, and the accepted cost of a security-version floor. PR #539 is closed in favour of the track; each of its pieces is placed in a stage or listed as deleted.CLAUDE.md, Architecture: Firmware — the kernel calls no UEFI service; every UEFI call ToyOS makes is the loader's, before ExitBootServices. The loader'sGetVariable,SetVariable,GetTimeandResetSystemall come before the handover, so the rule holds of the tree.kernel/src/arch/aarch64/rtc.rs' header loses theGetTimeclause the rule forbids.issues/boot-media/the-anti-rollback-floor-is-a-firmware-variable.md: one bullet records the accepted cost. Once the floor counts a security version, an older build the same key signed at that version boots.issues/boot-media/the-machine-updates-itself-without-ubuntu.md: stage 2 gets an exit and names its wait on the track's--boot-first; the A/B later stage is the track's stage 5.Gates
cargo test -p toyos-build --libcargo run -- --clippy(x86_64 and aarch64 kernel, bootloader, workspace)No QEMU run and no T14 run: the only code line is a comment.
Unsure
update --goodas the writer of the good flag is my choice. Init already mints theslotsclaim, and onlyupdateholds it. An image that grants no program the claim is never good and boots three times.readypipe and[boot] upare my choice for the health gate's signal: the tree has none, and a swap's probation only asks whether a process still runs.mm::init. Whethertoyos-bootmapcan do that cheaply is unmeasured.🤖 Generated with Claude Code