Skip to content

Where a boot came from is the supervisor's to read, not the kernel's - #1210

Merged
Japabu merged 4 commits into
mainfrom
bootnames
Nov 11, 2026
Merged

Japabu merged 4 commits into
mainfrom
bootnames

Conversation

@Japabu

@Japabu Japabu commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The kernel held what the loader named of where the system booted from, and interpreted part of it:

  • four partition GUIDs by role (kernel/src/loaded.rs), answered as Loaded records in the device inventory;
  • the two boot sources, answered whole by SYS_BOOT_SOURCES;
  • a UEFI device-path parse of each source (toyos-devicepath), linked into the kernel only to refuse the loader's own bytes;
  • KernelArgs::boot_partition_start_lba and boot_partition_blocks, read by one log line.

The manual gives partition identity to userland and has the kernel parse nothing one program writes for another. The inventory in issues/the-kernel-is-small-interrupts-post-and-threads-wait.md (#1208) lists this as a removal that waits on no other change; I confirmed its description against the code at 9349705ce before designing.

What changes

The loader writes one record of what it named; the kernel carries the record's bytes to the supervisor and reads none of them; the supervisor parses it.

  • The record is a new no_std crate, lib/bootnames (toyos-bootnames): the ROOT, slot-volume and log partitions, the boot partition where firmware named one, and for the loader and the slot volume the firmware device path with the PCI function it runs through. Names::encode is the loader's side and Names::parse the supervisor's. Absence is a tag, so no zero GUID or presence flag stands for it; the three partitions every boot has are not optional in the type.
  • The loader (loader/src/bootsource.rs) builds the record before it exits boot services and puts it in LoaderData pages of its own, as it does a section of the slot image.
  • The kernel takes it as a fourth BootImage beside the supervisor's, the file server's and ROOT's (KernelArgs::boot_names): the same checks, the same read-only SharedMemObject, endowed to the supervisor under boot:names. No new syscall and no new object kind.
  • The supervisor maps it once at start, parses it and keeps the typed names. A record that does not parse panics the supervisor: the loader wrote it, so one this supervisor cannot read is another build's loader, and no partition it names can be trusted. Its first line now says the four partitions; the three source lines read as before.

What goes: kernel/src/loaded.rs; SYS_BOOT_SOURCES (137) with its ABI and SDK wrappers; the inventory's Loaded record and Role; the ABI's BootSources, BootSource, Controller and Unwritten; KernelArgs' four boot-partition fields, its log, ROOT and slot-volume GUIDs and boot_sources; the kernel's dependency on toyos-devicepath; the two kernel log lines that printed the boot and log partitions.

git ls-files 'kernel/src/*.rs': 215 files and 55,378 lines before, 214 and 55,303 after (75 fewer), and the kernel no longer links the 324-line device-path parser. KernelArgs is 440 bytes, from 1,560.

Decisions a reviewer should weigh

  1. KernelArgs::layout moves from offset 164 to 112, accepted by the orchestrator over 52 reserved bytes in front of it: no loader is deployed, and updates fix forward. What is guaranteed, at compile time: all sixteen fields before the word and the word itself are pinned by offset_of!, and LAYOUT's low byte is not zero. What a mismatched pair does, read off the code and run on no machine:
    • A loader older than this, a slot built from this. The loader verifies and loads the slot as before and hands its 1,560-byte struct. Before the comparison the kernel reads only offsets below 112, none of which moved: its memory and stack in the entry (and boot_pml4_addr on aarch64), the scanout in panic_console::arm, rsdp_addr for the UART. At 112 the old loader wrote the low 32 bits of the first block of the partition firmware loaded it from, or zero where firmware named none. A partition on a 1 MiB boundary starts at a multiple of 256 blocks at 512-byte and at 4 KiB blocks, so its low byte is zero and the word's is not: the kernel panics boot: the loader wrote KernelArgs layout … and this kernel reads layout 0x545901b8 on the panel and the UART, before it reads any field that moved. Not guaranteed: a partition that starts off a 1 MiB boundary, at exactly block 1,415,119,288 modulo 2^32, equals the word; that loader is not refused and the kernel reads moved fields as garbage. The comment at LAYOUT and the loader track say so.
    • A loader built from this, a slot older than it. The old kernel copies 1,560 bytes from the loader's 440-byte struct, the rest being the loader's stack above it, inside the boot map. It reads the word at 164, now the high half of loader_handoff_counter. That equals the old 0x54590618 only for a counter past 0x5459 << 48 ticks, decades at any counter rate, so it panics with the same line naming the two words.
    • Either way the machine does not boot until loader and slot are one build's, as with every KernelArgs change since the word exists: update does not replace the loader on the ESP. The record format's MAGIC is a second loader-to-slot version beside LAYOUT; a mismatch in it alone is refused by the supervisor, after the kernel has booted.
  2. A new crate. The record is written by the loader and parsed by the supervisor, so it is a boundary between two programs and belongs to neither; it is not toyos-abi, which is the kernel's boundary, and the kernel does not link it.
  3. dev.loaded.<role> leaves inspect, accepted by the orchestrator as removed with the supervisor's log lines as its replacement and no record owed. It rendered the inventory's Loaded records, which no longer exist; nothing in the tree read those paths but the renderer's own tests.
  4. The block service's --active-unknown goes. The supervisor sent it only when its read of the inventory failed; it now holds the names from start, so nothing can send it.
  5. A malformed record stops the supervisor rather than degrading. Before, the kernel panicked on a source the loader could not have written and the supervisor refused each source separately. Firmware's own input is still refused per source in the loader, by name, and carried as absence.
  6. The manual gains four lines on the boot page saying where a boot came from is not the kernel's to know. I read this as stating what the kernel and devices pages already decide, not as a new decision.

Files shared with open PRs

Removing a syscall cannot avoid sdk/abi/src/syscall.rs and kernel/src/syscall/dispatch.rs, which #1200 and #1204 also change, in other hunks. kernel/src/loader/mod.rs gains one array entry in spawn_supervisor, a few lines above a hunk of #1200's. kernel/src/main.rs changes at the reserved-region list and two log lines; no page-table or mapping code is touched. Nothing of #1202, #1203 or #1131 is touched.

Checks

check revision target result
cargo run -- --build-only tree of cccaf1e x86-64 image, built on macOS arm64 exit 0. log
cargo run -- --ci host tree of cccaf1e macOS arm64 exit 1: 109 of 110 steps green. The red step is the build system, by llvm::tests::the_caller_s_environment_reaches_neither_the_build_nor_the_key alone, where macOS's git shim cannot find Xcode's git under the test's environment (issues/the-llvm-environment-test-breaks-git-on-macos.md); #1208's run reds the same step on a tree with no code change. The new crate's four tests, the supervisor's lifecycle tests, the ABI's and inspect's are among the green. log
guest rows, run 42941582777: the_log_lands_on_the_boot_stick a_host_disk_beside_the_stick_is_left_alone the_data_partition_beside_the_stick_is_designated nvme_disk_boots usbd_drives_the_spare a_clean_stop_proves_its_image console_image_boots a_bent_supervisor a_supervisor_that_fails_early virt_first_entry virt_user_mode virt_smp virt_jobs_at_el2 virt_reboot 3b2d0a9 the guest runner: x86-64 under KVM, aarch64 emulated exit 1: 14 passed, 1 failed, of 15. The red is virt_reboot_refused_without_psci, which the word virt_reboot selected beside the row I meant: STALLED: waiting for the job reboot to end — it went quiet after 18 s. That is the open defect issues/virt-reboot-refused-without-psci-goes-quiet-before-its-job.md, recorded red on main at ec8912181 with the same line; virt_reboot, the same image with PSCI, is green here as it is there. I did not rerun it. log, and five lines of one x86-64 boot's console: the kernel's boot: boot names image …+0xf9 and the supervisor's four

The host and image checks were made on the tree of cccaf1e0f, a commit the rebase replaced. 3b2d0a999 differs from it by the rebase onto #1208 (records only) and edits to one issue record; no source file differs. The two commits after it change comments, two issue records, the manual's wording and compile-time asserts in sdk/abi/src/boot.rs: the only lines that are not comments are thirteen offset_of! asserts and assert!(LAYOUT % 256 != 0), all inside the constant block.

cargo run -- --build-only at the head f7a54f7ae exits 0, which compiles toyos-abi and the kernel with the new asserts. Built in the same worktree at 3b2d0a999 and at the head, 42 of the 45 binaries are byte-identical, the loader among them. The kernel, the supervisor and doom are not: in each, every allocated section but the build-id note is identical and every symbol keeps its address and size, and what differs is the .llvm.<hash> suffix of ThinLTO-promoted symbol names (15, 243 and 26 symbols) and, for the two programs, the build-id. So the staged images run that revision's code and data exactly, and are not its bytes. No guest check was repeated.

The head, 4ef2f55c6, changes two lines after f7a54f7ae. The assert becomes assert!(!LAYOUT.is_multiple_of(256));: the merge group's Linux lint step refused LAYOUT % 256 != 0 as clippy's manual_is_multiple_of, which my compile-only check of that commit did not run. And one of the two blank lines left in kernel/src/syscall/machine.rs goes, review suggestion 5, which moves the line number one panic location in the kernel carries. cargo run -- --ci host lint at the head exits 0 with 5 steps green on macOS arm64 (log); the Linux lint is the merge group's to confirm. The binaries were not compared again at this head.

The aarch64 rows apply because the loader's handoff is shared: four of the five I selected are green, and the fifth name's second match is the recorded defect above.

Negative controls on the parser's tests, each a checked patch applied, run and reverted: accepting trailing bytes reds hostile_bytes_are_refused_by_name; reading the slot and log GUIDs in each other's place reds every_shape_the_loader_writes_reads_back. Patches and runs.

Metal

Both benches booted the images staged at 3b2d0a999, one testcases boot each, run by the orchestrator, and I judged the readbacks from the worktree at f7a54f7ae, one commit before the head, with cargo test --test toyos-build -- --metal --metal-readback <dir> <rows>.

bench rows result
T14 control_regs, nvme_leaves_the_benchs_disk_alone, usbd_drives_the_type_c_controller exit 0: 3 passed, 0 failed. judge log
AMD bench control_regs, nvme_leaves_the_benchs_disk_alone, amdvi_translates_the_boot_controller, amdvi_remaps_the_boot_controller, amdvi_usbd_drives_its_own_entry exit 0: 5 passed, 0 failed. judge log

On each bench the supervisor's four lines agree with the loader's own (T14, AMD bench): the two device paths are the loader's Boot source: paths character for character, the controller is the one the loader placed (04:00.0 and 02:00.0), the boot partition and the log are the GUIDs of the loader's Boot partition: and Log partition: signatures, and the slot volume is the GUID in the slot path's HD(8,…) node. ROOT's GUID has no loader line to be read against. The kernel's line is boot: boot names image …+0x105 on both.

The images are the staged revision's: the supervisor says build 3b2d0a999… in both readbacks, and the kernel's symbol names and the supervisor's build-id in them are that revision's, not the head's, as the comparison above says. The staging requests and logs are beside them.

These are installed-disk boots, not USB boots. toyos-metal writes the image to the install area of the bench's internal NVMe, after the bench's own four partitions, and boots it once through BootNext; no bench row boots a stick (issues/no-bench-row-drives-usb-storage-since-the-benches-boot-off-nvme.md). So what the two boots exercise of this change, on real firmware:

  • the loader reading its own device path and the slot volume's off an NVMe namespace (PciRoot/Pci…/NVMe/HD), and GetLocation() placing the controller;
  • the record through the real handoff, and the supervisor's four lines;
  • LOG and BOOT granted by the loader's GUIDs on a disk that also carries another system's ESP and partitions, and the running ROOT and slot volume named to the block service (nvme_leaves_the_benchs_disk_alone);
  • @boot naming the NVMe controller, so the spare xHCI rows find a controller that is not it.

What no metal boot exercises: a source whose path runs through a USB node, with @boot an xHCI and LOG on a stick. A guest row holds that under QEMU's firmware: the_log_lands_on_the_boot_stick boots off usb-storage on an xHCI and is green in the run above; its console is not in the evidence, since a green row keeps none. The path's text form with a USB node is a unit test's. A machine whose firmware names no boot partition, or no device path, is reached by unit tests alone.

Not done

  • No boot of a mismatched loader and slot was made; the two cases above are read off the code.

🤖 Generated with Claude Code

The kernel held what the loader named of where the system booted from:
four partition GUIDs by role, answered as `Loaded` records in the device
inventory; the two boot sources, answered whole by `SYS_BOOT_SOURCES`;
and a UEFI device-path parse of each source, linked only to refuse the
loader's own bytes. The manual gives partition identity to userland and
has the kernel parse nothing one program writes for another.

The loader now writes one record of all of it (`toyos-bootnames`): the
ROOT, slot volume and log partitions, the boot partition where firmware
named one, and each source's device path with the PCI function it runs
through. It hands the record to the kernel as a fourth boot image beside
the file server's and ROOT's, and the kernel keeps its pages read-only
and endows the supervisor with them under `boot:names`, as it does ROOT,
reading none of it. The supervisor parses the record once at start and
panics on one it cannot read: the loader wrote it, so a record that does
not parse is another build's loader. Absence is a tag in the record, so
no zero GUID or presence flag stands for it.

What goes:
- `kernel/src/loaded.rs`, `SYS_BOOT_SOURCES`, the inventory's `Loaded`
  record and `Role`, and the kernel's link to `toyos-devicepath`;
- `KernelArgs`' boot-partition fields (two of them read by one log
  line), its log, ROOT and slot-volume GUIDs and `boot_sources`, and the
  ABI's `BootSources`, `BootSource`, `Controller` and `Unwritten`;
- `dev.loaded.<role>` in inspect, which rendered those records; the
  supervisor's first line now says the four partitions;
- the supervisor's read of the inventory for them, and with it the
  block service's `--active-unknown`, which only a failed read sent.

`KernelArgs::layout` moves from offset 164 to 112: the fields that left
were before it. The kernel still reads the word before any field after
it; a loader older than this wrote the low half of its ESP's first block
there, and the size folded into `LAYOUT` changes as well.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The loader track promised that every field before `KernelArgs::layout`
keeps its offset in every layout, and the field's own contract repeated
it. The word moved once, with the fields that left for the supervisor's
record. Both now say what holds: the word follows only the fields the
kernel reads before it compares it, and what a loader and a kernel on
either side of the move read of each other.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Review r1 of #1210 at 1198991e5

Base fb88cec2a; merges cleanly onto main at ea7a3897b. I read both commits, the whole diff, each changed file, the callers, the evidence at 7cc5b852, and the two overlapping branches. I ran no build or test.

Architecture

The shape is right and I found no simpler one.

  • Carrier. The record rides the existing boot-image mechanism: one more BootImage field, the same LoaderData, page and overlap checks, the same SharedMemObject::boot, one more entry in the supervisor's endowment array. The kernel reads addr and len and nothing else. No syscall, object kind or right is added; SYS_BOOT_SOURCES, the Loaded records, loaded.rs and the link to toyos-devicepath go. kernel/src holds no GUID, partition or device path afterwards. What it still reads of the boot is the slot word in the boot parameter, which the manual keeps with it.
  • Crate. Earned. The loader (no_std, UEFI) and the supervisor share no crate this belongs in, the format has one writer and one reader with a real parse boundary, and keeping it out of toyos-abi is correct since the kernel does not read it.
  • One parser. Names::parse has one caller, Boot::take. Every former consumer is served from the typed value: LOG and BOOT role grants (storage::grant), the block service's --active list (the same four GUIDs the Loaded records gave), the updater's slot grant (names.root, names.slot), and @boot. inspect's dev.loaded.<role> is dropped, not re-homed (suggestion 1).
  • Boundary. Firmware's path is walked no further than 512 bytes and held to Path::parse in the loader, per source, and carried as absence; a controller's range is checked before a Function exists, and encode asserts both. The supervisor parses the same bytes with the same check, so nothing firmware says can make the record unreadable: an unreadable record is a loader of another format. Panic is then the right failure (without the names there is no --active protection and no LOG), it is by name and after say_last_words_on_panic, and it is no stricter than before, when the kernel panicked on a source the loader could not have written. One consequence worth knowing: MAGIC is now a second loader-to-slot version beside LAYOUT, and a mismatch in it is refused by the supervisor, after the kernel has booted.
  • Size. 38 files, +666/−753. Kernel 215 files/55,378 lines to 214/55,303, verified at both revisions. The new crate is 318 lines, about 110 of them tests; the ABI loses about 290.
  • Manual. The four lines apply what the kernel page ("parses nothing one program writes for another") and the devices page (a partition GUID is never the kernel's) already decide, and name no code. I see no owner decision changed.

The layout word, old loader against new slot and the reverse

Read off the code at head; no such boot exists in the evidence.

  • Old loader, new slot. Before the comparison the kernel reads only offsets below 112: its memory and stack in the entry (and boot_pml4_addr on aarch64), the scanout in panic_console::arm, rsdp_addr for the UART. At 112 the old loader wrote the low half of its partition's first block, or zero where firmware named none. That is refused by name on panel and UART unless the partition starts at sector 0x545901B8 modulo 2^32, about 675 GiB into a disk of 512-byte sectors. In that one case the kernel reads moved fields as garbage.
  • New loader, old slot. The old kernel reads the high half of loader_handoff_counter at 164. Equalling an old LAYOUT needs a counter past 0x5459 << 48 ticks, decades at any counter rate, so it is refused by name. Its 1,560-byte copy runs past the 440-byte struct into the loader's stack, inside the boot map.
  • So the refusal is clear in every case but one, and that one is stated in the PR body and nowhere in the tree (blocker 2).

BLOCKERs

  1. Evidence guest/run-42941582777-uart-5.log at 7cc5b852 identifies the guest runner's CPU. Under KVM the guest sees the host's CPU, and the kernel's microcode:, fpu: and clock: lines (first at 42, 69 and 81; ten such lines in the file) carry its vendor by name, its CPUID signature, its microcode revision and its TSC rate. Together they name the model the evidence README says is masked and the run log masks as <cpu model>. The PR needs five lines of that console (the kernel's boot names image line and the supervisor's four): publish that excerpt, or mask those lines. Separately, line 2 of both guest run logs carries the runner's registered name unmasked while Machine name is masked; it is a generic name and the same line is in the linked Actions run's own log, but the rule as briefed names runner names, so drop it in the same pass or rule it acceptable. The other files are clean of paths, users, hostnames, firmware and disk identifiers; the bench names in metal/ are the repository's own.

  2. The layout word's rule is stated more strongly than it holds, in the comment and in the record.

    • sdk/abi/src/boot.rs:121-122 — "which no ESP starts at" — nothing makes that true. The removed LAYOUT as i32 assert was what kept the previous sentence true by construction; nothing replaces it, and ToyOS's own bench install puts its ESP after another system's partitions, so "an ESP is near the start of its disk" is not a property to lean on. State the condition, or enforce what can be enforced: the low bits of LAYOUT are the struct's size, so a const assert that it is no multiple of 256 would make "no 1 MiB-aligned partition starts there" checked for both sector sizes.
    • issues/the-loader-does-only-what-must-precede-the-handover.md:63-66 — "each pinned by offset_of!" — false for thirteen of the sixteen fields before the word. The asserts pin kernel_memory_addr, kernel_stack_addr, kernel_stack_size and layout; memory_map_*, kernel_memory_size, rsdp_addr, kernel_image_*, the six gop_* fields and boot_pml4_addr are unpinned, and two of them exchanged build with layout still at 112. These are the fields the refusal itself reaches the panel and the UART through. Adding the asserts makes the sentence true at compile time and is cheaper than rewording it.
    • the same record, :67-70 — "each refuses the other by the word" — omits the exception above.
  3. issues/the-kernel-is-small-interrupts-post-and-threads-wait.md:452-453 — the "Stale text" list still says loaded.rs's header "interprets no path" above a function that parses one. The file is deleted by this PR, so the entry points at nothing; delete it.

None of the three changes a token of built code except the optional asserts, so no check needs repeating beyond a compile of toyos-abi.

SUGGESTIONs

  1. lib/inspect/src/dev.rs — dev.loaded.<role> is a user-visible answer removed with no record. The fact's owner is now the supervisor, which serves no inspect subtree, so re-homing it is separate work and I do not ask for it here. The removal is declared in the PR body; the orchestrator should make sure the owner accepts the log line as the replacement, or file it.
  2. lib/bootnames/src/lib.rs:48 — "The device [Self::boot] is on" — the path is that of the handle firmware loaded the loader from, and it can be present where boot is None.
  3. system/supervisor/src/main.rs:3793 — "the supervisor reads neither" sits on fileserver alone and speaks of two fields.
  4. docs/src/architecture/boot.md:22 — "the partitions it read, by role" — the log partition is named, not read.
  5. kernel/src/syscall/machine.rs:359-360 — two blank lines where sys_boot_sources was.

Evidence, as read

  • Host (host/ci-host.log, tree of cccaf1e0f): 1 of 110 steps red, the build system, by one test, llvm::tests::the_caller_s_environment_reaches_neither_the_build_nor_the_key. The keyed_and_built failure printed inside it is that test's own child, failing at the macOS git shim under SDKROOT=/no/such.sdk, as issues/the-llvm-environment-test-breaks-git-on-macos.md records. Every other FAILED in the log is inside a [ci] control step (the loom negative controls), which the summary counts green. The new crate's four tests, the lifecycle's and the block service's ran green. git diff cccaf1e0f 3b2d0a999 is five issue files; 3b2d0a999..1198991e5 is one doc comment and one record. Reusing the run is sound.
  • Guest (run 42941582777, head 3b2d0a999 confirmed from the run): 14 of 15. The red row ends exactly as its record says, after Boot: complete. On that boot the kernel logged the names image and spawned the supervisor; virt_reboot, the same image with PSCI, is green in the same run, so the record parses under that firmware; A log read answers an unpublished ring at its first number, as the console does #1207 places the cause in the log reader's cursor for a ring no CPU published. Independent of this change. The row therefore says nothing about this branch either way.
  • Controls: both are one-line parser mutations, each built and each reds the named test with the expected difference.
  • Reached by no boot: the updater's slot grant (only images/system.toml sets slots), a one-line change I checked by reading the argument order at the loader's call against the console, where the supervisor's slot-volume and boot GUIDs equal the loader's own HD(...) nodes; and any mismatched loader and slot (WRITE_NO_LAYOUT_PARAM has no reader in the tree).

Metal precondition

Each bench's one testcases boot, every staged member green:

  • T14: nvme_leaves_the_benchs_disk_alone, usbd_drives_the_type_c_controller, control_regs.
  • AMD bench: nvme_leaves_the_benchs_disk_alone, amdvi_translates_the_boot_controller, amdvi_remaps_the_boot_controller, amdvi_usbd_drives_its_own_entry, control_regs.

nvme_leaves_the_benchs_disk_alone carries this change on both: it judges what the LOG and BOOT servers mounted against the loader's own console lines, an oracle the record does not pass through, on a disk shared with another system. The @boot readers fail if the supervisor's line is absent or names the wrong controller. Read the supervisor's four lines against the loader's Boot source: lines on both.

These are installed-disk boots, as the amended body says: toyos-metal writes the image to the internal NVMe's install area and boots it once through BootNext (tests/metal/src/lib.rs, internal.rs). Loader and slot come from one image. What metal still does not exercise: a source through a USB node (the QEMU row the_log_lands_on_the_boot_stick only), firmware that names no boot partition or no path (unit tests only), and a slot written by update under a loader already on the ESP, which is both the slot grant and the only way a mismatch arises.

Overlap

For the orchestrator, on the description

  • [requests and staging logs](/metal) is a dead relative link; it should be the evidence URL at 7cc5b852…/metal.
  • The rest I checked holds: the counts and sizes, the run's head, the exit codes, and the staging runs' exit 2, which is by design (Verdict::Staged).

SEND BACK

…xception is said

Every field before `KernelArgs::layout` now has its `offset_of!` assert,
where three of sixteen did, so the record's "each pinned" is true at
compile time. `LAYOUT`'s low byte is asserted non-zero: a loader older
than the word's place wrote its partition's first block there, and a
partition on a 1 MiB boundary starts at a multiple of 256 blocks at
either block size, so such a loader is always refused. The comment and
the loader track say that, and that a partition off such a boundary can
still equal the word.

The kernel-is-small record's stale-text entry for `loaded.rs` goes with
the file. Three comments and the manual's line say what they mean: the
loader's source is the device firmware loaded it from, both of the
supervisor's image handles are handed on unread, and the log partition
is named, not read.

No built code changes: the asserts are constant, and the two comments
in files that carry panic locations keep their line counts.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Review r2 of #1210 at f7a54f7ae

Scope: 1198991e5..f7a54f7ae (one commit) against round 1's three blockers, and the evidence branch pr-1210 at b8e5794f. I ran no build or test.

The change list is complete. Six files. The only non-comment lines since the staged revision 3b2d0a999 are fourteen in sdk/abi/src/boot.rs's constant block: thirteen offset_of! asserts and assert!(LAYOUT % 256 != 0). The rest is one comment at LAYOUT, one doc comment each in lib/bootnames/src/lib.rs and system/supervisor/src/main.rs, two issue records and one manual line. No manifest, lockfile or build file differs. The architecture is as round 1 left it.

Blocker 2 (the layout word): resolved

  • The guarantee is now true by construction. At the base, offset 112 was boot_partition_start_lba: u64, which the old loader set to the partition's first block or 0 (loader/src/main.rs:536-540 at fb88cec2a). A 1 MiB boundary is block 2048·k at 512 bytes and 256·k at 4 KiB, so the low byte the old loader left at 112 is zero in both, and in the no-partition case. LAYOUT is 0x545901B8; its low byte is the struct size's and the assert keeps it non-zero for any later size. The kernel compares by inequality (kernel/src/main.rs:216), so that loader is refused.
  • The exception is exact. Unrefused needs the low half of the first block to equal 0x545901B8 = 1,415,119,288, which forces the start off a boundary. The comment at LAYOUT, the loader track record and the PR body each say this, and none claims more.
  • The prefix is pinned. Sixteen fields precede the word and all sixteen are now asserted, at the offsets repr(C) gives them, which are also the base's. "A field leaves, joins or moves in that prefix only by failing to build" holds.
  • The record's sentence on the reverse case (an older kernel reading the handoff counter's high half at 164) matches the layout.

Blocker 1 (evidence): resolved at b8e5794f, with one thing left that a push cannot do

  • pr-1210 is at b8e5794f, and all seven evidence links in the PR body point at that commit; each target exists.
  • The full console is gone. guest/run-42941582777-console-excerpt.log is five lines: the kernel's boot names image line and the supervisor's four. They carry QEMU's PCI address, the test image's partition GUIDs and the guest's clock.
  • Both guest run logs mask the runner name, group and machine name, and the instrument lines read <cpu model>.
  • I searched every file for CPU vendor strings, CPUID signatures, microcode revisions, counter rates, firmware and board vendors, hostnames, home paths, user names, IP and MAC addresses and disk identifiers. Nothing identifies a machine. What matches is the repository's own: test and crate names, the two microcode file names ROOT ships, the bench names in metal/ (settled in round 1), and the aarch64 row's console, which is an emulated CPU.
  • For the orchestrator and the owner: the branch was rewritten so the commit round 1 read is on no ref, but GitHub still serves that commit and its full console log by hash to anyone, and round 1's comment names the hash prefix and the file. The evidence repository is public. Only a purge by GitHub support, or removing the repository, takes it down. This is outside the branch and landing does not change it. The rewrite also departs from the evidence README's "every branch is append-only"; an appended deletion would have left the file one click back, so I do not hold it against the author.

Blocker 3 (stale-text entry): resolved

issues/the-kernel-is-small-interrupts-post-and-threads-wait.md:447-452 no longer lists loaded.rs, and the sentence that remains reads correctly.

The staged images as evidence for the head: sufficient

The boots of the 3b2d0a999 images count for f7a54f7ae. The reason is the source difference, which the byte comparison corroborates.

  • By inspection. The difference is comments and const-block asserts, which emit nothing. Root's policy does not ask for another integration run for that. I checked the asserts by hand: every asserted offset follows from the declaration and 0xB8 is not zero. The comment at LAYOUT shifts the lines below it in boot.rs, but nothing below it can panic, so no panic location moves.
  • What bytes/ shows. The two hash lists name the same 45 files and differ in three; bootloader.efi is identical. sections.txt shows that in the kernel, the supervisor and doom no allocated section differs except the two programs' build-id notes. symbols.txt shows every symbol keeps its section, address and size once the .llvm.<n> suffix is set aside. Both scripts do what the README says.
  • What it does not show, none of which changes the answer:
    • It covers the 45 binaries of --build-only. The test programs inside the testcases images are not among them, and no hash ties the staged image.img to the hashed 3b2d0a999 binaries. The inspection covers those.
    • sections.txt prints "loadable segments DIFFER" for all three, including the kernel, where no allocated section differs. My inference, not shown in the evidence: the kernel links -pie with no linker script, so its first PT_LOAD holds the ELF header, whose section-table offset moved with .strtab's nine bytes. Printing the differing offsets from elfcmp.py would settle it; I do not require it.
    • No log of the head's --build-only is in the evidence. The head's hash list shows the 45 binaries were produced.
  • When the readbacks arrive, expect the staged revision's names, not the head's: the kernel image carries function names from the kernel's .symtab (build/kimage.rs:103-107) and a copy of the supervisor's build-id, so up to 15 backtrace names differ by suffix and the supervisor's build-id is the staged build's.

SUGGESTIONs

  1. sdk/abi/src/boot.rs:189 — "A partition aligned to 1 MiB starts at a multiple of 256 blocks" is true for blocks up to 4 KiB; the qualifier is only in LAYOUT's doc above. Optional.
  2. For the orchestrator, on the description: "the staged images run the head's code and data exactly" is right for code and program data. The symbol names and build-id above are data the system reads and are the staged revision's; the body could say so and mention the "loadable segments DIFFER" line.

Round 1's suggestions 2 to 4 are taken and read correctly. Suggestion 5 is deferred until after the readbacks, for the reason the body gives; that is sound.

Verdict

No blocker remains in the branch. The two metal boots stay the landing precondition as round 1 listed them, each staged member green and the supervisor's four lines read against the loader's Boot source: lines.

LAND

@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator, before queueing at f7a54f7 (the head review r2 gave LAND; unchanged since).

Review r2's landing precondition is met. Both bench boots of the 3b2d0a9 images ran (r2 accepted them as evidence for this head) and the harness judged the readbacks from a tree at f7a54f7:

  • T14: control_regs, nvme_leaves_the_benchs_disk_alone, usbd_drives_the_type_c_controller — 3 passed, 0 failed.
  • AMD bench: control_regs, nvme_leaves_the_benchs_disk_alone, amdvi_translates_the_boot_controller, amdvi_remaps_the_boot_controller, amdvi_usbd_drives_its_own_entry — 5 passed, 0 failed.

On both, the supervisor's device paths, @boot controller, boot, log and slot-volume GUIDs agree with the loader's own lines (quoted, masked, in the evidence at 89d7f9fb). I ran the boots; the implementer judged them. Queueing.

@Japabu
Japabu marked this pull request as ready for review November 11, 2026 03:38
@Japabu
Japabu enabled auto-merge November 11, 2026 03:38
@Japabu
Japabu added this pull request to the merge queue Nov 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 11, 2026
`assert!(LAYOUT % 256 != 0)` is clippy's `manual_is_multiple_of`, which
the host lint denies; `u32::is_multiple_of` is callable in a constant,
so the assert says the same thing with it. The two blank lines left
where `sys_boot_sources` was become one.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@Japabu

Japabu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator: the merge queue removed this PR at f7a54f7 because the Linux lint step was red on clippy's manual_is_multiple_of at the assert added in round 2 (merge-group run 42954147125). I had told the implementer a compile was enough for that line; it was not.

f7a54f7ae..4ef2f55c6 is two lines, which I read:

  • sdk/abi/src/boot.rs: assert!(LAYOUT % 256 != 0) becomes assert!(!LAYOUT.is_multiple_of(256)) — the same predicate, still in the const block, so nothing is emitted and round 2's argument for the guarantee is unchanged.
  • kernel/src/syscall/machine.rs: one blank line removed (round 1's suggestion 5). It moves the line number of panic locations below it in that file and changes no behaviour; the judged bench boots stand for the same reason round 2 gave for the earlier comment shifts.

The implementer's --ci host lint at the head is clean on macOS; the merge group's Linux lint is the confirmation. Re-queueing.

@Japabu
Japabu added this pull request to the merge queue Nov 11, 2026
Merged via the queue into main with commit ad5db5a Nov 11, 2026
5 checks passed
@Japabu
Japabu deleted the bootnames branch November 11, 2026 04:55
Japabu added a commit that referenced this pull request Nov 11, 2026
The rebase's resolution of the inventory left conflict markers in the
commit before this one. The section is main's again, with what #1200 did
marked done as #1210 marked its own, and the track's measurement says the
kernel this branch leaves.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant