Conversation
The interface carried `10.0.2.15/24`, a default route to `10.0.2.2` and a resolver at `10.0.2.3`, written into `main` as literals. That configuration was one network's — QEMU's user-mode backend — and it was right about a machine nobody had asked. The bench's router leases something else, so the T14 could never have had an address at all. netd now runs smoltcp's DHCPv4 client and applies what it is leased: the address and its prefix, the default route, and the resolvers the DNS socket queries, all three replaced together on a lease and dropped together when one is lost. A route left standing over an address that is gone sends frames out with a source nothing will answer, which is why they move as one. Every DISCOVER and REQUEST carries the host-name option (RFC 2132 §3.14) with `toyos-t14` in it: the bench's router is the only DHCP server in reach that records a client's name, and what it records this machine under is what that name then resolves to. `netd: ready, at most N piped connections` moves from before the loop to the first pass on which the address question is settled — a lease landed, or twenty seconds passed with none. Sixteen arms in this suite wait for that line and then connect; a netd that announced itself before it had an address would hand each of them a stack with none. A machine that gets no lease says so in one line and goes on serving, and its clients get their connects refused one at a time, which is what they are already written to survive. Measured against QEMU's user-mode DHCP server, which is an implementation of RFC 2131 this repository did not write and whose lease is known field by field: `lan_dhcp_lease` reads `10.0.2.15/24` from `10.0.2.2`, gateway `10.0.2.2`, resolver `10.0.2.3`, eleven milliseconds after netd came up, and holds the readiness line to arriving after it. The other fifteen network arms — `netd_*`, `https_tls13`, `https_tls13_e1000e`, the five `log_stream` arms and the four `sshd` ones — are green on the same tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`tests/lancase` is the boot that runs netd in front of the ThinkPad's onboard I219 at `00:1f.6`, `8086:15fc`. It is a directory of its own for the reason `tests/e1000case` is: a program that names a card a machine does not have costs an `init:` refusal line on every boot of the config that does, and no machine has both. Nothing on it reaches the internal NVMe — there is no `[disks]` row, and the PCI function netd claims is the only device any program on it names. `lan_hold` is the one job on that boot and it asserts nothing: a metal boot is about a second long, and nothing on a network can be asked of a machine that is up for that long — the I219's link takes seconds to negotiate before a DHCP discover can go out. It holds the machine up for twenty seconds and exits, and its exit record is what says the machine stayed up for the whole window. It is on `RUST_SKIP` because on any other boot it is twenty seconds of nothing. The metal loop now pings the machine's own address across the window between its two operating systems, and writes what it saw into the readback beside `back_secs` and `stick_secs`. Three things make that a fact about the boot: - The window opens when `ssh` stops answering and closes when it answers again, and inside it the only thing that can be running is the image this loop wrote. - A reply counts only after the address has been silent for five seconds. `reboot` takes `sshd` down before the interface, so the machine that is already "down" by this loop's reckoning still answers ICMP for a moment, and a reply counted there is the operating system that is leaving. - The address is resolved before the flash, through the same router's DNS that issues the lease, and the boot's own lease record is held to it afterwards: a run where the leased address and the pinged one differ is a ping something else answered. `ping` is the host's own ICMP client, declared in `src/sourcegate.rs` beside `ssh` and used nowhere else. It is an implementation of ICMP nobody here wrote, which is what makes it an oracle for the stack under test rather than a second opinion from it — and it is the only question this repository can ask a metal boot while that boot is still running, everything else being on a stick read minutes later. The number is taken on every metal boot and claimed by one. Every other boot in the suite runs no netd on that card, so its reading is Ubuntu answering on the way back up — which is the separation `boot.lancase.ping_secs`'s ceiling will be derived from. Until the machine has answered once, that row bounds the loop's own wait and says so; the run that takes the first reading is the run that tightens it, and every other row in the lancase block says the same. `lan_dhcp_lease` is one name with two judges, as `metal_device_probe` is: under QEMU it certifies netd's DHCP client against the user-mode backend's server, and on the T14 it certifies the whole path — the kernel handing netd the I219's function, the link, the lease from the bench's router under this machine's name, and the host's ping answered at the leased address. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`pcidev::bring_up` armed exactly one mechanism — `enable_msix(...).ok_or(Refusal::NoMsix)?` — so a function that publishes no MSI-X capability was refused by name and its holder never ran. Every function this project had handed to a process so far was a virtio one and every one of those has MSI-X, so the refusal had only ever been reached by `virtio_net_no_msix`'s deliberate `vectors=0`. The ThinkPad T14's onboard NIC is not one of those. Measured on the machine, not assumed: `/proc/interrupts` names its interrupt `IR-PCI-MSI-0000:00:1f.6 ... enp0s31f6` and `/sys/bus/pci/devices/0000:00:1f.6/msi_irqs/162` reads `mode=msi`, so Linux drives that function on MSI. Flashed and booted (run 28), this kernel wrote `pcidev: PCI 00:1f.6 NOT HANDED OVER — its MSI-X could not be armed`, netd found no endowment and exited, and the bench's one cable stayed dark. `bring_up` now arms MSI-X and falls back to MSI, and `Bound` holds whichever it got. **The driver above the boundary cannot tell which one it is and does not have to**: both deliver the same vector into the same `Interrupt`, and the claim answers the same handle either way. What differs is where the message lives, and therefore what a hand-over back has to write to silence it — `Armed::silence` masks an MSI-X table entry or clears MSI Enable, and `Armed::undo` puts the capability itself back off for a hand-over that armed a vector and was then refused. **MSI is not the weaker mechanism here, and the security argument is the same one.** MSI-X's table is kept out of what the holder maps because a holder that could rewrite it could point the device's message at any address the LAPIC decodes. An MSI function's message is in its own config space, which `pcidev` keeps: `config_read` is read-only and there is no writing counterpart. So MSI needs no BAR withheld — the same rule reaching a different register file. `Refusal::NoMsix` becomes `NoInterrupt` and says both mechanisms, because that is now what it means. `PciDevice::disable_msi` is new and is `disable_msix`'s counterpart: it sets the per-vector mask where the capability implements one and clears MSI Enable, which every function has. `toyos_pci::msi` grows `disabled` and the `MASKED`/`UNMASKED` mask values, host-tested — `disabled` deliberately does not restore the Multiple Message Enable field an arming zeroed, or a function would come back armed for as many vectors as it can raise. The hand-over record now names the mechanism (`vector 0x28 on MSI-X`): it is the first thing a machine that never heard from its device is asked, and it is not something the driver above the boundary can see. The two checks. - **Negative control.** Run 28 on the T14 is this change reverted whole, on the base the granted claim will be measured against: the same `tests/lancase` image on this machine's own I219 with `bring_up` arming MSI-X alone. It refused the claim by name at 1.349 s, netd exited `code=0` at 2.268 s, and nothing answered on the cable for the twenty seconds the boot stayed up. - **Independent oracles, two.** Linux's own reading of the same function, above — a driver nobody here wrote, saying that function is an MSI part. And the capability's register layout, which is the PCI spec's and which `toyos-pci/src/msi.rs`'s tests encode: the offsets of the address, data and mask registers all move with the 64-bit address bit, and writing a vector at the wrong one of them lands in whatever capability comes next in the list. Beside them, this kernel already produces the shape MSI must reproduce — `PCI 00:1f.3: msi address=0xfee00098 data=0x00000000` for the T14's HDA. Green: `cargo test -p toyos-pci` (41), `cargo test --lib` (296), `cargo run -- --clippy` (all five invocations), and the four guest arms that read this module — `virtio_net_no_msix`, `iommu_virtio_platform`, `pci_function_is_exclusive` and `userdev_dma_fault`. The MSI arm itself is exercised on the T14 alone: no device QEMU models that a process may claim publishes MSI without MSI-X, so there is no guest that can take that branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…'s name Run 28 on the T14 pinged `100.92.92.12` and got an answer 64 seconds into the window, one second before `ssh` came back. That is the machine's Tailscale address: `t14` resolves to it on this host, `Target::address` resolved the name, and only the operating system that is leaving and the one coming back ever hold it. The reply was Ubuntu's, and the five seconds of silence the loop waits for did not catch it because Tailscale comes up late in Ubuntu's boot — a boot whose netd never ran at all would have been reported the same way. The name was the wrong question. What a boot of a metal image can answer on is the address held on the PCI function that image *claims*, so that is what is read: `Target::nic` names `0000:00:1f.6`, and `Driver::wire` asks the machine, before the flash, which interface that function is, what MAC it has, and what address `ip -4 -brief addr show` reports for it. Three reads, none of them a root command and none of them a write. Measured just now over ssh: `enp0s31f6`, `192.168.1.46/24`, gateway `192.168.1.1`, with the Mac on `192.168.1.47` — the same LAN, which the Tailscale address is not. The MAC is carried out beside the address and written into the readback, and `lan::on_metal` holds the boot's own `netd: MAC` record to it. That is what turns "something answered" into "this boot answered": a MAC does not change with the operating system, so a driver reporting this one is the driver holding that address, and a reply from any other interface at it is somebody else's. An interface with no address at all is refused by name rather than read as the next line's — `an_interface_with_no_address_is_refused_by_name` stages the T14's own four-interface listing, Tailscale address included. `Refusal::Unresolved` becomes `Refusal::Wire`, because the thing that can now fail is the machine's account of one function rather than a name lookup. Run 28's other numbers are recorded against their rows: `complete_ms` 1223, `back_secs` 65, `stick_secs` 0. They are facts about a boot whose claim the kernel refused, which makes them facts about the boot and not about the claim. `ping_secs` gets none — no run has pinged the wire's address yet — and the comment above the block says so rather than leaving a reader to assume the row was simply not reached. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
… is reachable
Run 29 on the T14 armed the I219's MSI and then refused the hand-over:
pcidev: 24 functions; a 32-bit window comes from 0x0..0x0, a 64-bit one from 0x603dc00000..0x6040c00000
PCI 00:1f.6: msi address=0xfee000b8 data=0x00000000
pcidev: PCI 00:1f.6 NOT HANDED OVER — this machine has no 2 MiB-aligned address space above what firmware assigned to put a BAR in
The refusal is correct and its wording was not. `window` places a BAR above
everything firmware described; in 64 bits that always exists, and below 4 GiB it
never does, because the platform's fixed MMIO is at `0xFEC00000` and the UEFI map
reaches it. So the message read as "this machine is full" where the truth is
"this module only ever looks above everything, and there is no above down there".
A 32-bit window is a free run *between* things.
Leaving the BAR where firmware put it is not the alternative: on this machine the
I219's `0xbcf00000` and the internal NVMe's `0xbce00000` are in one 2 MiB page,
and 2 MiB is the only page this kernel maps, so handing it over unmoved would put
a disk controller's registers inside netd's mapping.
What this change does is make the machine say what it has left, and name what is
missing.
`toyos_pci::bridge` decodes a Type 1 header's forwarded memory windows — bits
31:20 in the top twelve of each half, a limit that names the last megabyte, and
the base-above-limit encoding for a bridge that forwards nothing, which read
literally is a range that wraps. `PciDevice::forwarded_below_4g` reads them, and
the prefetchable one is dropped where its upper base puts it above 4 GiB.
`survey_low_space` runs on the machine where the low window comes out empty, and
only there: a boot with room says nothing, and thirty lines on every boot is a
log that has one channel off this bench. It merges the firmware map, this bus's
assigned BARs and every forwarded bridge window, prints the free runs of 2 MiB or
more, and ends with the sentence that matters —
pcidev: a run above is a candidate and not a claim — what says whether an address below
4 GiB reaches this bus at all is the host bridge's own aperture, which is ACPI's `_CRS`,
and this kernel runs no AML
**Nothing here hands a run out**, and that is deliberate. An address outside the
host bridge's aperture is not free space, it is unrouted, and a read of it
answers ones — which `Refusal::Dead` cannot tell from a device that is not there.
Linux reads `_CRS` for exactly this and keeps its chipset registers as quirks for
firmware that gets `_CRS` wrong, not as an alternative to it. Guessing a range
here would be guessing whether a process's mapping reaches the bus or the void.
`issues/kernel/a-32-bit-bar-needs-the-host-bridges-aperture.md` carries the three
ways out and their prices.
`Refusal::NoWindow` was three facts under one name and is now three: `NoWindow
{ wide }` for a machine that published no window of that width — with the 32-bit
arm naming the aperture — `WindowFull { wide }` for one this module used up, and
`NoMappableBar` for a function with nothing to map, which is not about address
space at all and sent a reader to the allocator.
The two checks.
- **Negative control.** Run 29 is this change reverted whole, on this image and
this machine: the same boot, the same claim, and a refusal that named no free
run and no missing input. The next run on the T14 is the arm — the survey
either prints runs or prints none, and either is a reading this repository does
not have today.
- **Independent oracle.** The bridge window layout is the PCI-to-PCI Bridge
Architecture Specification's, encoded in `toyos-pci/src/bridge.rs`'s tests: the
address is bits 31:20 of each half and not the field's own offset, the limit
names the last megabyte, and base-above-limit is "forwards nothing" rather than
a wrapped range. Beside it, the T14's own enumeration is the check on the
survey's arithmetic: `04:00.0` at `0xbce00000` and `00:1f.6` at `0xbcf00000`
are behind bridges, so a survey that reported the page holding them free would
be contradicted by the enumeration in the same log.
Green: `cargo test -p toyos-pci` 44, `cargo test --lib` 297,
`cargo run -- --clippy` all five invocations, and the four guest arms that read
`pcidev` — `virtio_net_no_msix`, `iommu_virtio_platform`,
`pci_function_is_exclusive`, `userdev_dma_fault`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
The green arm's claim stops at the 32-bit window, and the branch that says what the machine has left below 4 GiB is stacked on #443. Merged here so the image the T14 runs carries the survey; when both land, merging main is the same commits again.
Run 29 armed the I219's MSI and stopped at the 32-bit BAR; run 30, the negative control with the claim pointed at a card this machine does not have, did the same boot with no netd at all. Both are recorded here. `boot.lancase.complete_ms` 1257 and `back_secs` 61 are the machine's own numbers off boots whose claim was refused — facts about the boot rather than about the claim, so the readings stand. The two `lan.` rows still have none, because netd never came up on either. **Run 30 changed what `ping_secs` is for.** The control pinged 192.168.1.46 once a second for its whole 61-second window and got nothing: this machine's wire answers no earlier than its ssh, and the loop stops pinging when ssh answers. So the number that row will hold is not "earlier than Ubuntu" — it is "at all", because nothing answers at that address unless the boot brings it up. That is a stronger control than the one the row was written for, and the `ceiling_from` says so. It still waits on a green reading before it can be tightened to the span a boot is actually up for. The track file is brought up to what these three runs established: sshd's half landed as #440, the I219 driver and DHCP are built and green under QEMU, and what is left is the laptop — the claim, and the two things that wait on it. Its constraints list carries three measurements instead of two guesses: the I219 is an MSI part (`/proc/interrupts`, `msi_irqs/162`), `toyos-t14` resolves to nothing on this LAN and `t14` resolves to a Tailscale address only Ubuntu holds, and the 32-bit BAR shares its 2 MiB page with the internal NVMe — which is why leaving it where firmware put it is not the way out either. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
Run 30's window was dark and I wrote a judge on that one sample: a reply anywhere in the window is the boot's. Run 31 falsified it. That boot's claim was refused at the 32-bit BAR, netd never held the card, and the loop still reported `192.168.1.46 answered a ping 57 s into the window` — two seconds before the machine's own `sshd` came back. The wire comes up before ssh does, and the loop stops probing when ssh answers, so the reply was inside the window, inside the ceiling, and belonged to the operating system after the boot. A ceiling on `ping_secs` could never have told the two apart, whatever number it held. What tells them apart is time against the boot's own timeline. `logd` writes a wall clock on every record and the loop now writes one beside the reply; both are UTC, the T14's clock being Ubuntu's from the network and this host's NTP's. `bootlog::record_unix_secs` reads the one field in a log a host clock can be held against, and `lan::the_boot_answered` is the judge: - the reply is inside the span this boot's records bracket, ending at `Rebooting.` — anything after that is the next operating system, whatever its timing; and - it is at or after the `netd: DHCP: lease` record, because a machine with no address answers nothing at that address. Both halves are needed. The first alone would accept a reply from before netd had an address; the second alone would accept run 31's, which came thirty-four seconds after the boot had handed the machine back. The bracket is about twenty-three seconds wide on this boot and the two clocks agree to within a second, which is what makes comparing them admissible; a machine whose clocks drifted further would show it as a reply just outside the bracket rather than as a mystery, and the refusal prints both numbers. `ping_secs` stays, priced, as the cost of a reply — it is a number worth watching move and it is not a verdict, and its `ceiling_from` says so now rather than claiming a separation it cannot make. `boot.txt` gains `ping_at`, written only where something answered: a reply has a time or it did not happen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…t does The fallback this branch added chose by what an arming answered, so a function that publishes MSI-X and whose table this kernel could not decode fell through to MSI and was handed over with that table and its PBA inside a BAR `place_bars` did not withhold: `msix_bar` answers `None` on a decode failure, so nothing is kept back, while `enable_msix` answers `None` on the same input. On the base that function was refused and never reached a holder. The choice is now what the function's capability list publishes and never what an arming answered. `toyos_pci::mechanism` is that rule, pure and host-tested: a function publishing MSI-X is armed on MSI-X or refused `MsixUnusable`, MSI is armed only where there is no table in a BAR at all, and neither is `NoInterrupt`. `enable_msix` succeeding implies `Msix::decode` succeeded implies `msix_bar` named the BAR, so the table's BAR is withheld on every path that arms MSI-X. `disable_msi` is the enable bit alone. Its per-vector mask write had no specification behind its order and was the opposite of the one independent implementation this branch cites — Linux's `pci_msi_shutdown` clears MSI Enable and then *unmasks* — and leaving the Mask bit set owes a message on the set-to-clear transition a later arming makes of it with the Pending bit set (PCIe 7.7.1.7). What is left is the one decision `Msi::disabled` makes, which `disabling_clears_the_enable_bit_and_nothing_else` gates on the host; its fixture now carries Multiple Message Enable set, so the partial implementation that cleared that field too is red where it used to pass. `netd`'s `config_space_is_bounded` now attempts a configuration write and refuses a claim that answers one. That refusal — `RegTarget::PciConfig(_) => Err(NotSupported)` — is the whole of why an MSI function's message may stay in configuration space with no BAR withheld for it, and nothing asserted it. Deleted: `Armed`'s three one-caller methods and the `arm` free function, whose bodies are one `match` each at their sites; `msi::MASKED`/`UNMASKED` and the test that restated their declarations; `iommu.rs`'s `MSI_ARMED` `must_not_say`, which no implementation of that module could red — 00:03.0 publishes MSI-X, so that arm never reaches the MSI branch. `issues/kernel/a-claimed-function-must-have-msi-x-and-the-i219-may-not.md` is renamed and cut to the half that still stands: `toyos-i219` refuses a part outside MSI-X mode at `IVAR`, and the T14's `00:1f.6` is measured to be one. The kernel half the slug claimed is refuted by this branch. No citation to either the slug or the path exists anywhere else in the tree (`git grep`). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…re tracked
`toyos_pci::mechanism` was a new pure-crate rule with one caller, a four-row
truth table its two tests transcribed row for row, and a doc that told the
kernel's hand-over story inside a crate that knows nothing of hand-overs. It is
deleted whole: `toyos-pci/src/lib.rs` is byte-identical to origin/main again,
and `bring_up` asks the capability list directly.
The rule that MSI is not a fall-back is a rule about hand-over and is stated
once, in the module whose subject is a function driven by a process
(`kernel/src/pcidev/mod.rs`). The kernel's own xHCI and HDA drivers arm MSI-X
or fall back to MSI, and that is not the same choice: nothing is handed over
there, so no BAR carrying an MSI-X table reaches a holder and there is no rule
to contradict.
Also deleted, because nothing consumed them: the `on {armed}` discriminant on
the hand-over line, which restates what `report_message` printed off the
device's own registers one line earlier; netd's configuration-space write
probe, which ran on the one claim that is armed on MSI-X and never on the one
armed on MSI, and which had to reach past `PciDev` — a typed handle with no
write method at all — into `toyos_abi::syscall` to make the call; and the second
assertion in `disabling_clears_the_enable_bit_and_nothing_else`, which the first
already implies.
Two weaknesses this branch leaves standing are now recorded rather than carried
in a pull request body:
issues/kernel/nothing-reaches-the-msi-arm-of-a-claimed-function.md
issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md
`grep -rn 'disable_msi\b\|enable_msi\b' kernel/` gives five sites: hda.rs:666
and xhci/wait/boot.rs:104 arm and never disarm, and pcidev/mod.rs:495, :520 and
:756 are this branch's, reached by no tier. Each file carries its owner and the
exit condition that closes it.
Prose deleted at the sites the review named, including two pieces of
pre-existing prose in files this branch edits: `pcidev`'s "What is read back,
and what is not" register of tests, which no gate held and which went stale
every time a test moved, and five of the six lines on `BAR_MOVED`/`MSIX_ARMED`.
TWO SENTENCES OF 8dec3ec ARE RETRACTED. History is not rewritten here, so they
are withdrawn by name instead:
"nothing answered on the cable for the twenty seconds the boot stayed up" is
false. /Users/jan/.claude/jobs/2280e09e/tmp/t14-run28/lancase.log:12 reads
"100.92.92.12 answered a ping 64 s into the window, after 5 s of silence — so
something on this cable was up while Ubuntu was not".
"Run 28 on the T14 is this change reverted whole, on the base the granted
claim will be measured against" is false. Run 28 is tip=220305b4
(t14-run28/lancase.log:1) and run 29 is tip=24625c6b
(t14-run29/lancase.log:1), whose parent 938957a run 28 does not carry, so the
pair reverts two changes and is not a negative control.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
… name The judge that says a host-side reply belongs to this boot lived in the integration-test tree, where nothing could host-test it, beside a module that declares itself the home for exactly its shape. It is now `bootlog::host_second_inside_this_boot`: a log, the two ends of the host's own window, the record the observation may not precede, and one second in; a verdict out. Four tests cover both arms, and the fixture is three lines copied out of the stick the T14 wrote rather than three runs' numbers blended together. The one assertion that could not fail — `x + 57 > x + 23` over string constants — is gone with it. The clock skew the bracket rested on is measured instead of asserted. The loop records the host's clock at both ends of the window in which the machine is running neither of its operating systems, and the judge refuses a boot whose own records do not lie inside it before it places anything against them. Nothing claims the two clocks agree to within a second any more; the run's own data bounds their disagreement. The four cable fields in a readback are one `Cable`, and reading one refuses every partial set by name. A file naming seconds and no wall clock used to fall to a `_` arm and be reported as "nothing answered a ping" — the opposite of what the run recorded, and the shape of every readback taken so far. The reading and the probe belong to the boot that asks for them. `Arm::nic` carries the PCI function into the driver's invocation as `--nic`, and only the lancase arm sets it: `metal_device_probe`, `blackbox_unclaimed_page`, `ccorpus` and every other registration no longer take three `ssh` reads and a ping thread for a fact none of their judges reads. A host with no `ping` is now `Refusal::Probe` rather than a dark cable, and `ping_secs` is judged once, by `lan::on_metal`, instead of twice through a string compare that holed `Unfit::Unpriced` for one field name. `measured` on the lancase rows was run 29's boot time and the negative control's `back_secs`. It is run 31's, the last boot of that config: 1258 ms and 59 s. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…ame on a wire that is read `dns::Socket::update_servers` truncates to `DNS_MAX_SERVER_COUNT` and says nothing, and smoltcp's default for it is one — so a lease offering three left netd's own record naming two resolvers the stack did not have. The count is raised in `userland/.cargo/config.toml` to the three a lease can carry (`smoltcp::wire::DHCP_MAX_DNS_SERVER_COUNT`), and `dhcp.rs` holds the two together with a `const` assertion: a build that lowers it again does not compile. Nothing is dropped and nothing has to be reported as dropped. `report_within` is deleted, and so is the `timeout.min` it fed. Its premise was that a machine whose network never answers produces no timer, so the loop's own delay is unbounded. `dhcpv4::Socket::poll_at` returns `PollAt::Time(retry_at)` in every state, and netd's delay comes from `iface.poll_delay`, so a discovering client already wakes the loop every ten seconds. The report now prints the seconds it actually fired at instead of the constant it was written for. The lease path and the lease-lost path are one writer. `Dhcp::write` takes the lease or its absence and replaces the address, the default route and the resolvers together, so the code that drops a lease is the code every boot already runs. Two things the lease boot cannot ask are now asked. `lan_no_lease` boots the same config on an `e1000e` plugged into a hub with nothing else on it — the only machine in this suite where a DHCP client gets no answer — and holds netd to saying it has no address and then announcing itself anyway, which is what keeps every arm waiting on that line from hanging. And `filter-dump` writes the frames the client actually sent, so the host-name option is read as bytes on the wire: a server that ignores it writes nothing about it, and there was no other place the owner's hostname decision could be checked at all. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
Every prose finding in the review is answered by deletion rather than by a rewrite. What went: what an earlier implementation bought, the alternatives that were rejected, the borrow checker's opinion, a measurement with no command behind it, the run numbers and the story of which run falsified which judge, the comments that restate the line beneath them, and the fourteen-line header over one `sleep`. `tests/lancase/system.toml` claimed it changes "the pair of identifiers" against `tests/e1000case`; it changes one. It also carried a boot parameter line no config on this branch sets, copied in from its neighbour. The track file had grown a stage table with a rationale per bullet, which is a plan again. It is a paragraph, and what remains of the growth is two constraints the bench measured: the I219 publishes MSI and no MSI-X, and its 32-bit BAR shares a 2 MiB page with the internal NVMe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…mes it `installing_the_rule_is_not_also_a_boot` enumerates every boot-describing flag and asserts each one is refused beside `--install-sudoers`. `--nic` pushes onto `about_a_boot` like the rest and was missing from that list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`Arm::nic`'s doc says why only the boot that names a function asks the cable, and `invocation` said it again a screen later; `BootOptions::wire_dump`'s doc says what the dump is for, and the argv said it again; and the partial-cable test's own doc says which half would otherwise read as no answer, twice. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…ne's name `toyos-t14` resolves to nothing on the bench LAN. netd asks under the name and the wire is read for the option; whether any server records it is not a claim this repository can make, and the metal registration made it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…iving it
`enable_msix` collapsed four outcomes into one `None`, so `bring_up` had to ask
the capability list again to tell "publishes no table" from "publishes one this
kernel could not arm" — and the refusal it raised then asserted a reason that was
false on three of the paths it fired on. `Refusal::MsixUnusable` said the table
"is in a BAR nothing here can name to withhold", which holds only where
`Msix::decode` failed: after a successful decode `msix_bar` answers `Some(bir)`
and `place_bars` does withhold that BAR. On the `message()` path it was worse
than false — `enable_msi` calls the same `message()` and fails identically, so
the honest refusal there is that neither mechanism could be armed, and the
console had already printed `not armed — {why}` one line above the contradiction.
`enable_msix` now answers `Result<Mmio, Unarmed>` (`drivers/pci.rs:33-44`):
`Absent` is a function publishing no MSI-X, `Unusable` a table this kernel could
not reach, `Blocked` a message the unit refuses — which is the same message MSI
would carry. `bring_up` is one match over it (`pcidev/mod.rs:489-496`) with no
capability walk of its own: `Unusable` refuses `MsixUnusable`, `Blocked` refuses
`NoInterrupt`, `Absent` tries MSI. The `else if publishes(msi::CAP_ID)` arm is
gone with the closure: `enable_msi` already opens by looking the capability up
and answering false without one, so the arm decided nothing and no log line on
any machine moved with it. `virtio_net_no_msix` is the test that reaches the
`Absent` arm and reads that false back.
`MsixUnusable` no longer claims what withholding happened; it says the function
publishes MSI-X, this kernel could not arm it, and MSI is not a fallback for a
function that has a table.
`PciDevice::capability(id)` (`drivers/pci.rs:363-366`) is the one spelling of
"find this function's capability by id". It replaces seven copies of
`capabilities().find(|c| c.id() == …)` — the four in `drivers/pci.rs`, `msix_bar`
and `reset` in `pcidev/mod.rs`, and `power_up` in `drivers/hda.rs`.
Three callers move from `Option` to `Result` and decide nothing new:
`hda::arm_interrupt`, `virtio_sound::arm_interrupt` and `xhci::wait::boot::
arm_interrupt` read `.is_ok()`/`.is_err()` where they read `.is_some()`/
`.is_none()`.
Two numbers in 76fb456's message and body are corrected here, since history is
not rewritten. Its message attributed "five sites" to
`grep -rn 'disable_msi\b\|enable_msi\b' kernel/`; that command prints seven
lines — the two definitions plus five call sites — and the enumeration that
followed it was of the call sites only. Its body called
`tests/common/iommu.rs` byte-identical to `origin/main`, which
`git diff origin/main HEAD --shortstat -- tests/common/iommu.rs` refutes at
`1 insertion(+), 6 deletions(-)`; `toyos-pci/src/lib.rs` and
`userland/netd/src/virtio_net.rs` are byte-identical and `tests/common/iommu.rs`
is not. The body also cited the choice at `mod.rs:487-496`, which was the comment
above it and one line short of the block.
Prose deleted rather than rewritten: `Armed::Msi`'s doc, which restated the
module header's own MSI clause; the unbacked "no device QEMU models…" sentence
and the session-local "run 29" locator in
`issues/kernel/nothing-reaches-the-msi-arm-of-a-claimed-function.md`; the
rebuttal of the dropped `open` probe in
`issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md`;
and, pre-existing in files this branch edits, the "The refusal moved with the
driver" chronology in `tests/common/faults.rs` and `enable_msix`'s two-meaning
`None` clause. That tracker's own claim moved with the code: `enable_msi` from
`bring_up` is now reached by `virtio_net_no_msix`, and what nothing reaches is a
successful arming and everything past it.
Green in this worktree: `cargo test -p toyos-pci` 40 passed exit 0;
`cargo test --lib` 295 passed 1 ignored exit 0;
`cargo test --workspace --exclude toyos-build` 138 suites 1347 passed exit 0;
all five `src/clippy.rs` shapes exit 0, no warning. Every
`cargo test --test toyos-build` is still refused at `src/toolchain.rs:1382`
(exit 101) while `/Users/jan/Dev/jan/toyos-aperture` holds the sysroot;
`--claim-sysroot` was not passed and `main` was not merged in.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… walk reached `bring_up` armed MSI on `Unarmed::Absent`, and `Absent` meant only "the walk yielded no MSI-X capability". The walk is `caps::CapWalk`, which ends at a misaligned pointer, one below the standard header, or one already visited (`toyos-pci/src/caps.rs`). So a function whose MSI capability precedes such a link and whose MSI-X capability follows it read as `Absent`, was armed on MSI, and `msix_bar` — the same blind walk, one line above — named no BAR, so `place_bars` withheld none and the MSI-X table's BAR went to the holder with everything else. A holder that can write that table points the device's message at any address the LAPIC decodes. On origin/main that function was refused `NoMsix` and reached no holder, so the hole is this branch's. An early end is now a distinct answer, and the refusal is by name. - `CapWalk::truncated()` says whether the walk ended at the list's terminator or at a link the spec forbids. The decision stays in the pure crate; the kernel reads it. - `PciDevice::capability(id)` answers `Result<Capability, NoCapability>`: `Absent` is a walk that reached the terminator and found nothing under the id, `Truncated` a walk that stopped early, so what lies past that link was never read. - `enable_msix` carries it out as `Unarmed::NoTable(NoCapability)`, and `bring_up` refuses `Refusal::CapsTruncated` — "its capability list ends at a link the PCI spec forbids, so whether it holds an MSI-X table in a BAR was never read, and MSI is not armed on a guess". Only `NoTable(Absent)` reaches the MSI arm. The five remaining readers of `capability` — `disable_msix`, `enable_msi`, `disable_msi`, `hda::power_up`, `pcidev::reset` and `msix_bar` — take the same answer and decide nothing new; a truncated walk leaves each of them where a missing capability did. `msix_bar` is the one that matters, and what makes it safe is that `bring_up` refuses the function before `place_bars` ever reads what it answered. The host tests that see a partial implementation are `toyos-pci`'s four walk tests, which now assert the flag as well as the step: an implementation that treats a misaligned pointer, a below-header pointer or a cycle as a clean end is red at `a_pointer_that_is_not_dword_aligned_is_refused`, `a_pointer_below_the_standard_header_is_refused` and `a_pointer_to_a_visited_link_ends_the_walk`, and one that reports the terminator as an early end is red at `the_terminator_ends_the_walk` and `a_forward_chain_is_followed_to_its_end`. The module header at `kernel/src/pcidev/mod.rs` stated the bound as an absolute — "a function that publishes MSI-X is armed on MSI-X or refused" — which held only over what the walk reached. It now states it over the walk. Deleted, because nothing reads them: `#[derive(Debug, Clone, Copy, PartialEq, Eq)]` on `Unarmed`, whose five traits nothing in the tree formats, clones, copies or compares. Prose deleted rather than rewritten, at the sites the review named: `Unarmed`'s "Named rather than collapsed into one `None`", which is the argument `Refusal`'s own doc already makes, by reference to a `None` that no longer exists; `capability`'s first sentence, which restated its signature; `bring_up`'s clause naming which test reads which refusal, which the same commit deleted ten lines of elsewhere; and, pre-existing in files this branch edits, the three-implementations investigation story above `tests/common/faults.rs`'s `virtio_net_no_msix` and the reason under `userland/netd/src/i219.rs`'s BAR search, which this branch falsifies — the T14's `00:1f.6` publishes no MSI-X, so nothing is withheld from that claim. Green in this worktree: `cargo test -p toyos-pci` 40 passed exit 0; `cargo test --lib` 295 passed 1 ignored exit 0; `cargo test --workspace --exclude toyos-build` 138 suites 1347 passed 0 failed exit 0; all five `src/clippy.rs` shapes exit 0 with no warning. Every `cargo test --test toyos-build` is still refused at `src/toolchain.rs:1382` (exit 101) while `/Users/jan/Dev/jan/toyos-aperture` holds the sysroot; `--claim-sysroot` was not passed and `main` was not merged in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…rops the lease `SMOLTCP_DNS_MAX_SERVER_COUNT` was a workspace-wide `[env]` entry in `userland/.cargo/config.toml`: without `force` it is silently ignored where the ambient environment already names the variable, and it applied to every userland crate rather than to netd. smoltcp declares the count as a crate feature, so netd asks for `dns-max-server-count-3` on its own dependency line beside `socket-dhcpv4`. `cargo check -p netd` reads `DNS_MAX_SERVER_COUNT = 3` out of the build script's `out/config.rs`, and the `const` assertion still holds. `Dhcp::pass` had a `Change::Lost` arm whose effect nothing could see: replacing its body with `self.leased = false` left every arm on this branch green. There is one call to `write` now, reached by both changes, with the arm deciding only what is written — so the path that drops a lease cannot be removed without removing the path every boot takes to get one. `dhcp::LEASE_BOUND` is `toyos_tco::LEASE_BOUND_MS`, one declaration the harness reads too, and `tests/lancase/system.toml` no longer grants `receives = ["netd"]` to logd and test-runner: this boot sets no `logstream=` parameter and neither program consumes any of that authority. Prose deleted: the chronology in the module header, the rejected clearing function at `write`, the restatement under `Dhcp::pass`, and the six lines in `userland/.cargo/config.toml` that duplicated the doc at the assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…han the error The judge bounded the host↔T14 skew by the down-window's width less the boot's own span. On run 31's geometry that admits tens of seconds, which is enough to place the very reply the judge exists to reject back inside `first..ended`. The loop reads the machine's own clock as a fourth `ssh` read before the flash, with this host's clock taken at both ends of that read and a read straddling more than one second refused by name, and `host_second_inside_this_boot` takes that offset in place of a window. `Watch`, `window_from` and `window_to` are gone with it, and the readback carries `clock_skew` instead. The negative control is run 31's own reply, refused on run 31's own numbers. That run's transcript opens 33 s before the boot's first record, so a reply 57 s into a window that opens no earlier than the run itself came no earlier than one second past this boot's `Rebooting.` The "at least 34 s" the old test and the pull request body both claimed inverted the arithmetic: a window opening earlier than the first record makes that gap smaller, never larger. `Cable::addr` is an `Ipv4Addr`, so a readback whose `ping_addr` is not an address is refused rather than read and judged. The two `cable()` refusal strings that carried fourteen and eighteen literal spaces mid-sentence are line continuations again. `ride_the_reboot` resolves the boot's own silence before this host's missing `ping`, so a machine that never came back is `Silent` and not `Probe`. `Refusal::Wire` and `Refusal::Probe` are pinned as the loop's own failures, exit 2, by the test that pins that classification. `asked_under_its_own_name` scanned the whole pcap, and `filter-dump` records both directions: a server echoing the option back would have read as the question. It walks the records now and counts only IPv4-over-UDP frames leaving the client's own port. The dump is read once the guest is gone and removed there, so no refusal below it leaves a pcap behind, and its one parameter with one value is gone. `toyos-t14` was spelled three times; there is one spelling in the harness now, held to netd's own declaration by reading that source, with the record and the option both built from it. `ping_once` drops the arm for a host this loop does not run on, and with it the `div_ceil` that was the constant 1. Prose deleted: the fixture's citation of a path no `git ls-files` finds, the invariant restated at five sites, the docs restating the constants beneath them, the router-repeats-the-lease premise stated as fact, the review finding's own provenance inside `cable()`, and the Wi-Fi and Tailscale measurements inside a test doc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…und this tree prices `Nic::E1000eNoServer` was the one NIC arm that dropped `forward`, so a boot asking for `ssh_port` on that profile got no `hostfwd` rather than a refusal; and `wire_dump` was attached outside the `shape.nic` match, so a profile with no NIC handed QEMU a `filter-dump` naming a `netdev` that does not exist. Both combinations are refused by name before the boot. `lan_hold` sleeps `toyos_tco::LEASE_BOUND_MS` rather than a number of its own, and `metalprofile`'s own test holds `tests/metal-profile.toml`'s three `lancase` rows to that constant — the coupling that lived in a comment saying the two had to move together. `boot.lancase.ping_secs` carries run 31's reading, `measured = 57`. The relegation `lan_no_lease` owes is in `issues/build/a-timer-anchored-names-tier-is-decided-by-its-price.md`, beside the two names already waiting on the same unresolved rule, rather than in a source comment with no owner and no exit condition. Prose deleted: the block of run history in `tests/metal-profile.toml`, the plan in `ceiling_from`, the narration of `lan::on_metal` at its registration, the `ping` row's second telling of what makes it an oracle, and eleven comment lines over six of code in `lan_hold.rs`. The track file is back to main's 61 lines and under its word count. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…reaches `Refusal::CapsTruncated` and `NoCapability::Truncated` are reached by no test in any tier, for the same reason the MSI arm is: the guest has no function that publishes one. The walk's half of the decision is host-tested in `toyos-pci/src/caps.rs` and a partial implementation of it is red there; the kernel's refusal arm is not, and the file now says so rather than describing a tree one commit older. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…econd The skew the judge reads a host second through is a difference of two floored whole-second clocks taken at either end of an `ssh` round trip, so it carries error the bracket it fed decided inside. Run 31's reply — the one recorded false positive — sat exactly one second past `Rebooting.`, and one count of that error greened it. `bootlog::MARGIN` is the daylight a reply now needs on both sides: three seconds for the skew (a floored second either side of a round trip held to one), one for the floored readings the comparison is made between, and one so a refusal is a distance rather than a coin. Run 31's reply is refused at every skew from -3 to +3, by at least three seconds at the worst of them, and that sweep is a test. The bracket's lower edge is the record the caller anchors on and its upper edge is `Rebooting.`; a log carrying no dated `Rebooting.` record is refused by name instead of being widened to its last line, which deleted `record_unix_span` and the boot's first record with it. `record_unix_secs` is private again. The skew derivation leaves `Driver::wire` for `clock_skew`, pure and held by a test that feeds it a host clock stepping backwards across the round trip — the condition the guard exists for, which was a `u64` subtraction under `overflow-checks` and panicked the loop instead of refusing. `src/lan.rs` is the cable's "text and frames in, verdicts out", beside `src/metaldevices.rs`: the lease record's grammar, the driver's link-up time, the pcap walk and the name netd asks under, none of which could be unit-tested where they sat. The walk's direction is now held by a test that feeds it the server's own echo of the option and expects a refusal, so reading the destination port instead is seen. The name is held to netd's declaration through `bootlog::declares`, which sees a rustfmt-wrapped declaration the exact-substring scan it replaces could not. `ping_once` refuses a host whose `ping` reads `-W` as seconds, which is every host but the bench Mac. `Dhcp::write` takes no `self` it never read. `boot.lancase.ping_secs` carries no `measured`: that reading is the reply this branch declares belongs to the next operating system. The two premises the judge spends and nothing has measured — the T14's RTC unchanged across the reset, and the router repeating the lease across the two operating systems — are `issues/hardware/the-cable-judge-spends-two-premises-nothing-has-measured.md` with what would close each. Prose the review refused, deleted: the fixture's provenance, the negative control's derivation off an out-of-tree transcript, the RTC premise stated as fact, the claim that a round trip holds two clocks to a second, the `-W` platform contract a refusal now carries, `tests/lancase`'s copy of `tests/e1000case`'s netd comment, `lan_hold`'s third telling of a priced row, `record_unix_secs`'s second paragraph, and two the branch merely passed by — `BootOptions::ssh_port`'s unfinished sentence and `tests/metalcase`'s citation to a deleted issue file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`only_the_direction_leaving_the_client_counts` asserts the client's own frame first, so the walk's one green arm stands beside the echo it must refuse rather than in a test of its own; and the cable judge's two premises are recorded in what closes each and nothing else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`bar-window` branched off 8dec3ec, msi-claim's first commit, and msi-claim has four commits past it; this branch needs all of them, so the whole branch is merged rather than the one commit `bar-window` carries. The one conflict is the `pcidev` module doc's "What is read back, and what is not" paragraph. 76fb456 deletes it as a register of tests no gate holds; host-bridge-aperture edited the same paragraph to add `account_for`'s records to it. The deletion wins, and the sentence the aperture side added goes with it: it is the same kind of claim about the same register. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The survey, `toyos_pci::bridge` and `PciDevice::forwarded_below_4g` are what this branch places a BAR out of; the aperture is what orders the candidates. Two conflicts, both additions on each side: `PciDevice::capability` (msi-claim) and `PciDevice::forwarded_below_4g` (bar-window) land at the same point in the impl block; both are kept. `publish`'s tail: the aperture side calls `account_for` under the `MACHINE` lock, the survey side drops the lock and surveys where the low window came out empty. Both are kept, in that order, because `account_for` borrows the guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`origin/lan-metal` at ebf5323 — two commits past PR #442's head 07eb6ec, which is the judge another agent was fixing. The green arm of this branch is `tests/lancase` on the T14, and lancase is this branch: the case's `system.toml`, `tests/common/lan.rs`, `src/lan.rs`, the metal loop's ping and its bracket judge, netd's DHCP client, and `lan_hold`. Without it there is no boot on which a placed BAR can be shown to carry a lease. lan-metal already carries `bar-window` (a85520a) and msi-claim's first commit (8dec3ec); msi-claim's four fix commits came in separately above. The one conflict is `tests/toyos-rust-tests/Cargo.lock`: host-bridge-abi bumped `toyos` to 0.7.0 and `toyos-abi` to 0.6.0, lan-metal added `toyos-tco` to the same dependency list. Both, since they are different edits to one list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… only orders the candidates
`pcidev` moved a claimed function's BARs onto the first 2 MiB boundary above
everything firmware described. In 64 bits that address always exists; below
4 GiB it never does, because the platform's fixed MMIO is at `0xFEC00000` and
the memory map reaches it — so the ThinkPad T14's I219, whose BAR is 32-bit and
shares its 2 MiB page with the internal NVMe's, could not be handed to a process
at all. And on both machines this project boots, the window it did choose lies
outside every window firmware answered for its root bridges; the branch below
this one recorded that and could do nothing about it, because refusing on those
grounds would have handed over nothing anywhere.
What was missing was never a better guess. No source in reach says an address
reaches the bus: the firmware memory map, this bus's assigned BARs, the ranges
its bridges forward and the windows firmware answered all say where something
*was found*, and the host bridge's aperture is `_CRS`, an AML method this kernel
does not run. So the machine is asked instead.
`toyos_pci::placement` is the pure half: free runs in, candidates out, the ones
a window firmware named holds first and the rest after, each aligned to the span
because a BAR's low address bits are hardwired to zero (PCIe §7.5.1.2.1). On the
T14's six surveyed runs and its two answered windows it yields
`0xae200000, 0x99200000, 0xa0800000, 0xbd000000, 0xd0000000, 0xfe200000` — one
run inside the answer, five outside, and the 736 MiB run a search by size would
have taken is fourth of the five. Five tests, no allocation.
`place_bar` is the effectful half, and it asks about one candidate at a time:
- the address must answer something no function claims before anything of this
function is there, and
- the BAR moved onto it, with memory decode on, must answer what the function
already answers where firmware put it.
**The reference is the function's own dword and not a constant, because the
constant is not one.** All-ones is what a PCIe root complex returns for a read
nobody claims (base spec §2.3.2) and what the T14's unrouted space answers; QEMU
11.1.0's q35 under this repository's OVMF answers `0x00000000`, measured at
`0x800200000` on a headless boot of `tests/netcase` — and virtio-net's own first
dword there is `0x00000000` too, measured on the same boot. A kernel holding
either number would refuse every placement on the other machine. The function's
answer needs no such number: an address nothing routes gives the machine's
answer rather than the function's, whichever that is.
A candidate that fails the first probe is skipped by name, a placement that
fails the second is undone whole — the register back to what firmware left in
it, decode back on — and either way the address is taken out of the runs so
nothing is offered it twice. A function no candidate carried is refused by name,
and the hand-over does not happen. `Refusal::NoWindow`, `WindowFull` and `Dead`
become `NoRun` and `NoPlacement`: a machine with no room and a machine with no
route are different facts.
`alone_in_its_page` keeps the two assertions that matter — no other function's
assigned BAR and no window this module cut inside the page — and loses the floor
it used to assert against, because `Machine::floor` was the claim that a window
is above everything firmware described and that is exactly what is no longer
true. What replaced it is the machine's own answer at the address.
The two probes are one read of one dword each, never a sweep: a device's first
page holds read-to-clear causes and pop-on-read queues, and reading 4 KiB of a
register file this kernel does not know would drive whatever is there.
`bar-placement-by-size` is the negative control, and it is the rule this design
refuses: order the runs largest first and ignore what firmware answered. On the
T14 that takes `0xd0000000`, which that machine's own `_CRS` puts inside no root
bus resource. `src/metal.rs`'s `FLASHABLE` rules on it — it changes the order
and nothing else, so the same two probes still decide each address.
`bar_placement_is_proven` is the guest arm: the netcase boot, one placement
record for `00:03.0`, both dwords in it read as numbers, and the address it
names inside a run the same boot printed.
Both issue files go, with the one citation to them:
issues/kernel/a-32-bit-bar-needs-the-host-bridges-aperture.md
issues/kernel/a-bar-is-placed-in-a-window-firmware-names-no-part-of.md
The first records that a 32-bit BAR cannot be placed for want of the aperture;
the second that the window chosen is outside it. Neither is true of this tree.
`issues/hardware/the-t14-answers-only-through-a-usb-stick.md` cited the first by
path and restated its premise in a bullet; both are rewritten to what is now the
case.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`origin/host-bridge-aperture` at 4e33235 — one commit past 1986f27, answering its review: the guest judge `aperture_account`, and a resource type the decoder refuses by name. Two conflicts in `kernel/src/pcidev/mod.rs`, both doc paragraphs whose subject this branch replaced. The aperture side's shortened wording is taken and this branch's sentences kept beside it; the paragraph saying a placement boundary is outside the windows firmware named goes, because there is no such boundary any more — each placement carries its own standing in its own record. `aperture_account`'s last check read the two placement windows out of `pcidev: ... a 32-bit window comes from ...` and looked for the account of each. Neither record exists here. What replaces it is the same claim over what does: every `placed at` line's address, with its standing against the named windows recomputed in the harness and held against the clause the kernel wrote. It keeps what the check was for — an account derived from an empty window list is red — and it reads an address the kernel chose rather than a span it announced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…e against The second probe holds a candidate's answer against what the function answers where firmware put it. A BAR firmware left at zero answers nowhere, and reading its dword would read physical address 0 — low RAM, whose content is nothing's answer. A candidate that then read the same value before and after would be taken for a placement while nothing routed it. Refused by name instead. No function on either machine in reach has such a BAR, which is why this is a guard and not a fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…dge forwards does not come back
A read of `0xd0000000` on the ThinkPad T14 did not answer all-ones. It did not
answer at all: the machine was gone for the 420 s the metal loop waits, through
the 60 s lockup detector and the 120 s boot deadline alike, and the owner
power-cycled it. A transaction no bridge forwards never completes on this
platform and the CPU cannot be interrupted out of it.
So the probe that asked an unknown address whether anything decoded it is gone.
It could only ever be issued in the one case it existed for, and in that case it
is fatal. On QEMU it merely read zeros, which is why nothing here caught it.
**What replaces it is read before ExitBootServices.** DXE's Global Coherency
Domain is the platform's own map of the physical address space (PI 1.8 Vol. 2
§7.2): EDK2's `PciHostBridgeDxe` adds each root bridge's Mem, PMem and
MemAbove4G apertures to it as `EfiGcdMemoryTypeMemoryMappedIo` and allocates
every BAR it assigns out of them, so a memory-mapped descriptor with a null
`ImageHandle` and `DeviceHandle` is aperture the bridges decode and nothing has
taken. `bootloader/src/gcd.rs` locates the DXE services table by its
configuration-table GUID, checks its `DXE_SERV` signature before calling
through a pointer firmware wrote, calls `GetMemorySpaceMap`, logs every
descriptor — held or free, whatever its type — and appends the free
memory-mapped ranges of 2 MiB or more to the same `root_bridge_windows` array.
The pool buffer is freed after the copy. The struct field orders are the PI
spec's and two size assertions hold them there.
No ABI change: both readers answer memory the root bridges decode, which is what
that array carries, and the kernel needs no way to tell them apart — it asks
whether an address is inside one and nothing else.
**In the kernel, "inside a window firmware declared" is the necessary condition
again, checked before any read.** A candidate inside none of them is left alone
by name: nothing reads it, nothing is placed at it, the next is tried. The only
probe left is the one that cannot hang — the function's own dword, read through
the BAR where firmware put it and again at the new address, inside a window.
`Refusal::NoPlacement`'s zero case is its own sentence, because a machine whose
free runs all lie outside every declared window is not one whose device failed
to answer.
The forced-order actuator keeps its meaning and loses its teeth-in-the-wrong-
place: it still orders the runs largest first, and it now asks
`placement::offered` for each run's standing against the windows rather than
passing an empty window list. On the T14 it reaches for `0xd0000000` first,
records that it is inside no declared window, does not touch it, and takes the
next — which is the boot that proves the condition is doing the work.
`placement::offered` is public for that caller, so the condition has one
derivation and not two.
Measured, QEMU 11.1.0 q35 under this repository's OVMF, `tests/netcase`:
pcidev: PCI 00:03.0 BAR 4 (0x4000 bytes) placed at 0x800200000 — inside firmware's mem 0x800100000; it answers 0x00000000 there, which is what this function answers at 0x800000000
`0x800100000` is a free GCD range, not one `Configuration()` named: that answer
was `mem 0x800000000..0x800100000`. The placement rests on the new reader.
`bar_placement_is_proven` reads the record as numbers — the address, the window
it claims to be inside, the dword — and holds the address against both the
windows record and the runs record of the same boot. The loader's GCD dump goes
to the UEFI console and `loader.log`, neither of which a QEMU boot capture
carries, so the T14's readback is where those lines are read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…nt of zero is two facts
Run 36 handed the ThinkPad T14's I219 to netd, netd mapped the BAR and took a
DMA grant, and the end-of-boot census read `userdev=0` on all eight CPUs for the
twenty seconds the boot was held. That count is a device nothing made speak and
a message that never reached a CPU at once, and nothing on the machine could
tell them apart: the census can count and cannot say when.
`Interrupt::take_unannounced` is true once per claim, and `drain_pending` — the
scheduler pass that already turns a message into a wake, where a `log!` is
allowed and an ISR's is not — says so:
pcidev: slot 0 took its first message on vector 0x28
measured on a QEMU q35 boot of `tests/netcase`, 50 ms after the hand-over. Its
absence from a boot whose function was handed over is now the other fact, by
itself.
`bar_placement_is_proven` reads it, and the vector it names is taken off that
boot's own hand-over record rather than written down twice. The word is a swap
for `take_pending`'s reason: two passes that both loaded `true` would both
announce one message. `clear` puts it back, so a re-claimed slot announces
again.
`cargo test -p kernel-loom` stays green: the field orders nothing and carries
nothing across, like every other word in that file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The first hand-over of the ThinkPad T14's own NIC worked and the card never spoke. `userdev=0` on all eight CPUs for the twenty seconds the boot was held, and `I219::open` returned `Ok` — netd's `undrivable` panics, a panic is an exit, and that boot carries no `exit: netd`. `issues/hardware/the-i219s-phy-is-never-brought-up-and-the-part-raises-no-interrupt.md` records what the driver does not do. `toyos-i219` is written from the 82574 datasheet, whose PHY is on the controller's own die; the T14's `8086:15fc` keeps its PHY in separate silicon the Management Engine shares. The crate has no MDIC, no PHY reset, no semaphore and no post-reset PHY configuration — its whole link bring-up is `CTRL.RST` and then `CTRL.SLU`, and `SLU` lets the MAC see a link signal rather than making the PHY produce one. It names the experiment that separates a silent part from an undelivered message and costs one boot: `ICS` is write-only, raises the causes written to it, is already declared in `regs.rs`, and nothing writes it. `issues/hardware/toyos-i219-refuses-a-part-outside-msi-x-mode-at-ivar.md` asked for exactly one reading — whether the bench's part takes the `IVAR` write — and run 36 is it. It took it, and taking it proved nothing: `accepted` asserts `read & wrote == wrote`, which a register reading all-ones satisfies and so does any reserved location that accepts a write. The file now says that, and its exit condition is to name what `0x000E4` is on this part rather than to run the write again. Both are the stage-2 I219 worker's, filed and not fixed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ress is offered to one claim Three holes the review found in `place_bar`, and each is closed where it was. **The reference was read before the windows were fetched.** `first_dword(was, size)` ran at whatever address firmware had put in the BAR with nothing checked at all, and this module's own `account_for` exists because machines have BARs inside no declared window. So the windows are fetched first and the address firmware assigned is named before it is touched: inside a window firmware declared, or a BAR firmware assigned and left the function decoding — firmware does not leave a function answering at an address its bridges do not forward, so that is firmware saying it routed it. A BAR that is zero is `BarUnassigned` as before; one that is neither named is `BarUnrouted`, refused by name and never read. `PciDevice::decodes_memory` is the reader, and `bring_up` takes it before `enable_memory_space` — after that call the bit is this kernel's answer and not firmware's. **The runs were cloned and the lock dropped across the whole probe loop.** Two claims arriving together — which `SLOTS`'s own doc says they do — could both be offered one address: the loser trips `alone_in_its_page`'s assertion or lands on top of the winner. Choosing an address and taking it out of the runs is now one operation and one critical section, `reserve_candidate`; the probe runs outside the lock and against a reservation no other claim can take. `toyos_pci::placement` holds the arithmetic: `reserve(&mut [Window], windows, span)` shortens the run it answered from and empties one no declared window holds, so a second offer of one address is not expressible — the `&mut` says it. `take_out`, `refused` and the two-pass `Candidates` iterator go with it; `cut` now only records the window the BAR keeps. `a_reserved_address_is_never_offered_twice` and `largest_run_first_still_offers_no_address_outside_a_window` are the host tests over that ordering, on the T14's own six runs and two windows. **A degenerate reference proved nothing.** `after == signature` settles a candidate against the dword the function answers where firmware put it — and where that dword is `0x00000000` or `0xFFFFFFFF` it is what a read nobody answered comes back as, so the comparison was `0 == 0` on q35, which is the only machine CI runs. Such a reference is now `BarReferenceEmpty`, refused by name. The dword read is no longer the BAR's first unconditionally: `toyos_pci::probe` declares which dword a function of a given identity answers a value of its own at, and a modern virtio function opens its common configuration with a selector that reads zero (virtio 1.2 §4.1.4.3), so its reference is the `device_feature` dword after it. Measured on the netcase boot: `0x30bf8024` at `+0x4`, where `+0x0` read `0x00000000`. `bar_placement_is_proven` reads all of it and one thing it did not have — an account of the address that came from outside the guest. `info mtree` over QMP is QEMU's own routing table, and it maps `virtio-pci-common-virtio-net` at `0x800200000`; the kernel's printed address is asserted equal to that rather than used. The arm also refuses a record whose two dwords differ, and one whose dwords are either of the two values an unanswered read produces. `Run` is gone: `toyos_pci::bridge::Window` is the same pair of addresses and the crate declared it already. The prose the review refused is deleted — the T14's 420 s in five places, the `Candidates` narration, `metal.rs`'s "the same two probes", the actuator's "with what firmware answered ignored", `gcd.rs`'s debug-dump justification and `main.rs`'s restatement of it. `issues/hardware/toyos-i219-refuses-a-part-outside-msi-x-mode-at-ivar.md` is back to what the stage-2 worker wrote with the one clause run 36 refuted cut out of it, and `the-t14-answers-only-through-a-usb-stick.md`'s bullet loses the design description rather than carrying a new one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`cargo run -- --help` from this worktree built an image and launched a guest on the owner's desktop, where it ran for 11,424 s of guest time. `--help` is not a flag `src/main.rs` declares; nothing asks whether a word is known, so an unknown one is ignored and the run falls through to the default launching path. Filed as `issues/build/an-unknown-flag-boots-qemu-instead-of-being-refused.md` and not fixed: it is the build system's and off this branch's path. The rule against `cargo run` without a non-launching flag exists for exactly this, and it is the only thing in front of it — every other unknown name in this tree is refused by name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ttle costs its function nothing
`placement::reserve` emptied a whole run whose first span-aligned address lay
outside every declared window, so the part of that run *inside* one was never
offered: run `0xa1000000..0xa4000000` against window `0xa2000000+0x1b000000`
answered one candidate at `0xa1000000` and discarded the 32 MiB above
`0xa2000000`. It now answers a run's first aligned address inside a window,
asking each window where it meets the run, and passes over a run that holds
none without touching it.
**An address outside a declared window is now unrepresentable rather than
refused at the caller.** `Candidate { at, inside: Option<u64> }` is
`Reservation { at, window }`: the kernel has no outside candidate to check for,
and the guard it used to carry — deletable without any arm noticing — is gone
with it. `release` puts a reservation back, and the walk is bounded at
`MAX_CANDIDATES`, so a function that answers nowhere no longer spends every
address of its width for the life of the boot however many times it is claimed.
**A BAR this kernel can settle nothing against costs the function its window,
not its hand-over.** QEMU's e1000e publishes a flash BAR whose every dword
reads `0x00000000`: no read through it can prove anything, and refusing the
claim over it took the whole card away. `https_tls13_e1000e` was red on
`01c6c2e8` — `netd never came up`, EXIT=1, the boot record
`NOT HANDED OVER — BAR 1 answers all-zeroes or all-ones where firmware put it`
— and is green here, EXIT=0, with the census counting the 55 `userdev`
interrupts the pre-branch run counted. `BarUnassigned`, `BarUnrouted` and
`BarReferenceEmpty` are per-BAR refusals now; a function none of whose BARs
could be settled still ends at `NoMappableBar`.
**One name for a routed address, not two.** `place_bar` held the
firmware-assigned address against the declared windows *or* against firmware
having left the function decoding memory; the second name decided nothing on
either machine in reach (the T14's `0xbcf00000` is inside
`0xa2000000..0xbd000000`, q35's `0x800000000` and `0xc0060000` are inside
`Configuration()`'s own answers) and rested on an unciteable premise about what
firmware does. It is deleted, with `PciDevice::decodes_memory`.
`bar-placement-by-size` is deleted with its `FLASHABLE` row: nothing in this
tree arms it, and the ordering it staged cannot reach an unrouted address in
any order now that `reserve` answers none — which
`largest_run_first_still_offers_no_address_outside_a_window` pins in the pure
crate.
`issues/kernel/no-boot-reaches-the-refusal-that-keeps-a-bar-read-inside-a-declared-window.md`
records what no arm here reaches, with the three addresses that are the reason.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Five comments and one module paragraph, each narrating or restating what is under it: `restore()`'s own two lines, the module header's copy of `reserve`'s doc, and the clause that promised a refusal leaves the machine "exactly as it began" — which a release another claim has taken an address above does not. What survives says what the code cannot: the `&mut` that makes a second offer unrepresentable, and that a refused BAR costs its window and not the function. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…'s first live false positive The T14 ran this branch's own lancase image and came back: `Boot: complete (1208ms)`, ssh again after 82 s, the stick 0 s, and a ping answered 81 s into the window. Those four are the readings `tests/metal-profile.toml` prices for that boot, so its rows carry them; `list.lancase.job_ms`, `lan.lancase.link_up_ms` and `lan.lancase.lease_ms` stay unmeasured, because nothing on this boot produced them — no `netd:` record of any kind reaches a T14 readback. The placement moved from run 36's `0xae200000` to `0xa0800000`, which is the whole of this round's straddling-run fix seen on hardware: the loader's first GCD dump of this machine declares `0xa0800000+0x1800000 mmio … free`, and `0xa0800000..0xa2000000` is a 24 MiB run whose first 2 MiB-aligned address is its own base. The code this round replaced emptied such a run over its standing at that first address and never offered the rest of it. The ping is the reading worth naming. It came from Ubuntu's wire one second before Ubuntu's own ssh: `1789380606` is `10:10:06 UTC`, and this boot's `Rebooting.` record is at `10:09:30`, 36 s earlier, at a measured clock skew of zero. It falls past the reset edge of the bracket, where `bootlog::host_second_inside_this_boot` refuses anything within `MARGIN` of that record or beyond it. Judged over the readback, `lan_dhcp_lease` reds with seven findings and the bracket is among them in its strictest form — the record it anchors on, `netd: DHCP: lease `, never crossed at all — so the judge credits this boot with no network rather than with a reply that was not its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…t, not merely refused
`probe::degenerate` is what keeps a candidate from being proved by `0 == 0`.
On the 82574L's flash window every dword reads `0x00000000`, so a BAR moved
onto an address nothing claims answers the signature it was settled against,
`after == signature` calls it placed, and a window nothing proved would be
handed to a process. Nothing asserted the record that refusal produces, so
deleting it was invisible to every arm: `https_tls13_e1000e` passed either way,
its verdict being that netd came up.
The judge now declares, per machine, why that card has a BAR no read can settle,
and asserts the count rather than looking for the line: `tests/e1000case` keeps
exactly one and says by which refusal, and the virtio bench keeps none. Removing
the refusal — which costs the variant with it, dead code being an error in this
kernel — reds the arm, twice and for that reason:
FAIL https_tls13_e1000e: "keeps BAR" appears 0 time(s) on a boot console
and this test staged 1
test result: FAILED. 0 passed, 1 failed, 1 total (397.5s) EXIT=1
`boot.lancase.ping_secs` loses its reading. The one ping that row has been
offered was answered by the operating system on the other side of the reset,
which the judge refuses; a ceiling tightened against 81 s would red on the first
real reply. The row carries no `measured` and says why.
The nine-line preamble narrating `bar_placement_is_proven`'s assertions and the
readback citation that resolves for no reader are deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Main landed the declaration-driven command line (#455) under three files this branch edits, the root bridges' memory windows (#448, #449) under the two it rewrites, and two issue files. The three-way merge took every hunk without a conflict; `git merge-tree --write-tree origin/main HEAD` had said so, exit 0 with no conflict list, against a base GitHub's own cached verdict was eleven commits behind. Two tracker files go in the merge rather than after it: `issues/build/an-unknown-flag-boots-qemu-instead-of-being-refused.md` is a defect main closed. `src/flags.rs`'s `check()` now refuses any word that is neither a declared flag nor a declared flag's value before anything is built, and `--help` is a declared flag answering `Outcome::Help`, so landing the file would put a false open claim in the tracker. Nothing cites it: `git grep` over the tree finds the slug nowhere. `issues/build/toyos-metal-parses-its-own-argv.md` arrives from main naming `Args::parse` in `src/metal.rs`, and the commit after this one puts that walk on `declare_flags!`/`Vocabulary` and deletes it. Nothing cites it either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…s command line off a declaration `src/metal.rs` ran the host's `ping`. That is a host binary outside the bar CLAUDE.md draws — "no binary outside those two", with four macOS tools and `cc` and Python declared as the standing failures — and it was a fifth, declared in `src/sourcegate.rs`'s row and nowhere else. Worse in its own terms, `ping_once` refused every non-macOS host by name, because `-W` is milliseconds to macOS's `ping` and seconds to everyone else's: a bench loop that cannot run on a Linux host, for a reason that is nobody's but that binary's. `src/icmp.rs` asks the question directly. `SOCK_DGRAM` with `IPPROTO_ICMP` is the socket both hosts open without privilege, so the probe is this repository's own code on either of them: an echo request carrying a payload no other probe of this process writes, its checksum computed here, and a reply identified by that payload rather than at an offset — one host leaves the IPv4 header in front of the message and the other does not, and the type byte is eight bytes before the payload either way. Linux's ping socket overwrites the identifier with the socket's own port, so the identifier is not read at all. A host whose kernel refuses the socket is refused by name, which keeps `Refusal::Probe` meaning what it meant: this host could not ask, as against nothing answered. Measured on the development host with a throwaway test, run and then removed: `echo(127.0.0.1, 1 s)` is `Ok(true)` and `echo(192.0.2.1, 1 s)` — TEST-NET-1, RFC 5737 — is `Ok(false)` after 1.000617125 s. The four committed tests are the decisions rather than the wire: a message carrying its own checksum sums to zero (RFC 1071 §1, and its §3 worked example), a request is an echo request carrying its token, a reply is found with and without the IPv4 header in front of it, and neither the request itself, nor another probe's reply, nor an unreachable message quoting this probe's request counts as an answer. `Args::parse` walked its own argv, matching ten flags as string patterns and deciding for itself what a missing value and a repeated flag meant. It is the third command line in this tree and the only one not read off a declaration; `src/flags.rs`'s `declare_flags!` and `Vocabulary` are what `cargo run --` and the harness both use, so `toyos-metal` uses them too. A flag given no value, an inline value on a flag that takes none, and a flag written twice are now refused by the same walk that refuses them for the other two, and `issues/build/toyos-metal-parses-its-own-argv.md` went in the merge below. `tests/common/lan.rs` priced `boot.<boot>.ping_secs` through `profile.judge` *before* `bootlog::host_second_inside_this_boot` decided whose reply it was, so a reading from the operating system on the other side of the reset was priced as this boot's and only then refused. The bracket is judged first and only what survives it is priced. `kernel/src/pcidev/mod.rs` says at `probe_dword` what a refused candidate leaves behind: the direct map's entries over its range, uncacheable. The boot map already covers every physical address, so nothing is taken that there is any giving back of, and a run holds no memory the firmware map described. `issues/kernel/no-boot-reaches-the-retry-that-undoes-a-placement-nothing-answered.md` records what no arm in this tree reaches, with its own exit condition: the comparison `after == signature` has no arm that reds when it is deleted, because on every machine in reach the first candidate answers, and the retry-and-undo path under it is therefore unexercised. Inside a declared window q35 routes every address to PCI, and the addresses that answer nothing lie outside every declared window where `placement::reserve` cannot spell them. The two tracker files this branch had edited without owning them are back to main's text (`issues/README.md`: "Do not touch an existing one you do not own"). One bullet does not come back: the I219's 32-bit BARs named `Refusal::NoWindow`, a variant this branch retires, so its citation goes with it rather than pointing at nothing. Prose the fourth review refused, deleted: the proof restated above the line that is the proof, the two-sentence argument for two `Display` arms, the `unannounced` reasoning a second time beside `record.rs`'s, "an address inside none of those windows is never offered" said twice in `toyos-pci`, and the eight-line paragraph above one `must_say` in `tests/toyos.rs`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…its sender
Three defects, all introduced by the commit that took the host's `ping` binary
out of the metal loop.
**The checksum was not pinned to its field.** Writing the sum into the
identifier — `message[4..6]` instead of `message[2..4]` — passed all four
committed tests, because the internet checksum's sums-to-zero property is
position-independent: any word of the message can carry the complement and the
total still folds to zero. On macOS, the host that sends the message as it was
given, every echo request would then go out with checksum `0x0000`, nothing
would answer, and the bench loop would report *nothing answered a ping* for
every boot. `the_checksum_is_the_field_rfc_792_gives_it` builds the unsummed
request from the token rather than from `request`, so what is compared is where
the sum went: bytes 2 and 3 carry the checksum of that message, and bytes 4
through 7 are what the unsummed one has. The one-field mutation now reds on the
second assertion, and a sum written anywhere else in the header reds on one of
the two.
**The reply's sender was discarded.** `Ok((got, _))` threw the peer away and the
socket is never connected, so any host on the wire returning this probe's token
counted as the address's answer — and whose reply it was is the question this
loop's judge already had one live false positive on. `is_reply` takes the sender
and the address asked, and answers `false` when they differ;
`nothing_but_the_reply_to_this_probe_counts` asserts it with this probe's own
answer returned from `10.0.0.9`.
**`chunks_exact(2)` reds the required `host` check.** `-D
clippy::chunks-exact-to-as-chunks` is on from 1.98.0, which is what the macOS
runner installs; this worktree's rustc is 1.97.1, so the local gate could not see
it (run 34880800558, job 104099314690, exit 101, `could not compile toyos-build
(lib)` and `(lib test)`). `as_chunks::<2>()` is the lint's own answer and the
remainder is the same odd tail.
The prose the review refused is deleted: the module header's paragraph about
somebody else's `ping` and its per-probe flag, the two test docs that argued
their tests' design, and the copy at `src/metal.rs` of what
`Refusal::Probe`'s own doc says.
cargo test --lib icmp EXIT=0 (5 passed)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
A line per GCD descriptor is 35 lines on q35 and 76 on the ThinkPad T14, and
they land at the head of the boot log — which on a machine with no serial port
is the panel `/system/bin/console` seeds itself from. `screen_console_shell`
reads that panel for a kernel `i8042:` line above the prompt, and this branch's
dump pushed every kernel line of the first screenful off it:
FAIL screen_console_shell: no `i8042:` line above the prompt, and the
console seeded 22093 bytes of kernel log — so the log reached the scrollback
and what is on the panel is some other part of it
red twice on `ci` run 34880800558 `guest (9)` (job 104099376049) and red again on
this host, where all 67 rows of the decoded panel are loader output, a blank line
and the prompt. That is this branch's line count and not a console defect this
branch found: the seed and the view are what they were, and 35 lines went in
front of them. With the dump cut the same test passes in 2 s.
What the kernel is handed is what the log now carries — one line per free MMIO
range of at least `GRANULE`, the ranges `KernelArgs::root_bridge_windows`
actually receives, with the count of descriptors behind them on the summary line
that was already there. The T14 reading this branch rests on is one of those
lines (`GCD: 0xa0800000+0x1800000 mmio cap=… free`), so nothing that was
evidence stops being printed. `GcdType`'s five other constants and its `name`
went with the dump that was their only reader.
cargo test --test toyos-build -- screen_console_shell EXIT=0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ghtly
`ci` run 34880800558 on this pull request carried all twelve `durations-shard-*`
artifacts and the `test-durations-merged` they merge to, so the three committed
`UNMEASURED` markers this stack registered have their measured prices:
bar_placement_is_proven 3,297 ms durations-shard-3
lan_dhcp_lease 3,029 ms durations-shard-1
lan_no_lease 32,709 ms durations-shard-2
Those three rows carry that run's `shards=12` prices, and **only** those three.
Replacing the whole merged artifact would have re-tiered five names this change
does not own — `sshd_exec` 8,478 against a committed 5,110,
`boot_partition_identity` 9,137 against 5,519, `root_candidate_malformed` 8,399
against 5,126, `sysret_ss_reload` 26,273 against 6,453, and
`screen_console_shell` 56,161 against 2,604, which is the wall time of the
failure that shard took rather than a price at all. `tests/toyos.rs`'s
`assert_fast_profile_label` grades the committed profile before anything boots,
so importing them reds every `cargo test` in the tree for five names whose
owners are elsewhere. That is the trap `47b794b9` names by the same argument and
`src/redlist.rs`'s standing `sysret_ss_reload` price row records; the merged
artifact is what says what this run measured, not what the tree commits.
`lan_no_lease` is over the line by construction and not by luck: its verdict is
that no lease arrived before `toyos_tco::LEASE_BOUND_MS` (20 s) ran out, and it
then drains ten seconds more to read what netd said afterwards. It is
`Tier::Nightly` with a `Why::TimerAnchored` row saying what stops being gated per
pull request — `lan_dhcp_lease` still carries the same client to a lease on a
wire that answers, and nothing else asks what it does on one that does not.
The two registration comments that said a name was Fast "with the UNMEASURED
bootstrap marker until priced" are priced now, so the clause goes.
cargo test --lib EXIT=0 (329 passed, 1 ignored)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…rranged The file asked for a guest whose claimed function sits behind a `pcie-root-port` with a memory window narrower than the aperture OVMF declares, and did not say whether the port's window or the loader's array is the thing to arrange. Without that the arm gets built and every candidate answers again. It is the port's window. The case exists only because `KernelArgs::root_bridge_windows` carries the loader's free GCD MMIO ranges, which are the host bridge's whole aperture rather than the port's slice of it — on q35 that range is `0xc1100000+0x3af00000`, read off this branch's own loader dump on `ci` run 34880800558 — so a candidate past the port's window is an address this kernel may name and the function does not answer at, which is exactly the state `place_bar`'s comparison exists to catch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…for the map is recorded
Round 6 cut the loader's per-descriptor GCD dump because it filled the panel.
The review answered that the dump left `loader.log` too, and named three ways to
settle it: a file-only emitter, an MMIO-only dump, or an `issues/` entry with
ownership, evidence and an exit condition. This takes the third, because the
first is refuted on this tree and the second buys back less than it costs.
**The file is not a second channel.** `/system/bin/console` seeds the screen from
every `*.log` file in `/log`, newest two (`userland/console/src/main.rs:277-286`),
`/log` is the root of the log partition, and the bootloader's `loader.log` sits
in it — so `loader.log` passes the extension filter, sorts after every
`YYYY-MM-DD-HHMMSS.log`, and is always the last file appended to the seed. A line
written to the file costs a panel row exactly as a printed one does. Measured
here at `ffd3f9c2` with the whole map moved to `loaderlog::line` and no
`println!`:
cargo test screen_console_shell EXIT=1
FAIL screen_console_shell: no `i8042:` line above the prompt, and the
console seeded 22093 bytes of kernel log
with all 34 of q35's map lines in the decoded panel and no kernel line above the
prompt; the same head without those lines on either channel is EXIT=0,
`PASS (2s)`, `30 kernel log rows above a prompt`. That control is the whole of
the evidence: the only difference between the two arms is a file nothing was
supposed to be reading.
So the map's absence is a compromise, and it is recorded rather than left as
neither:
`issues/diagnostics/the-panel-seeds-the-loaders-own-file-so-the-gcd-map-is-logged-nowhere.md`.
Its exit condition is the guest applying the rule the host-side reader already
has (`src/bootlog.rs:175-187` splits a `/log` listing into the loader's file and
logd's): once `loader.log` costs no panel row, one line per descriptor goes back
into the file alone at no cost. The MMIO-only dump was the alternative and is
worse until then — eighteen rows on the ThinkPad T14 and eleven on q35, paid on
the panel of the machine with no serial port, and still without the
`0xd0000000+0x2b000000 nonexistent` line the brief reasons about. The T14's
number is off this branch's own readback; q35's is the failing arm's own decoded
panel, 22 such lines across its two dumps:
grep -c ' mmio cap=' target/metal-placement-run39/lancase/loader.log 18
**A range the kernel has no room for is named.** Round 6 moved the `println!`
behind `out.get_mut(added)`, so a free MMIO range past `MAX_ROOT_BRIDGE_WINDOWS`
was printed nowhere and no count named it. It is printed now — once, with what
happened to it — and the summary carries how many there were. The loop no longer
stops at the first one: every free range the machine declares is in the log,
whether or not the array took it.
`GcdType` went with it. It carried one constant and one `==` after round 6, and
the plain `const MEMORY_MAPPED_IO: u32 = 3` beside the field's own doc is the
same code four lines shorter.
The three added comments the review read as prose are deleted, not rewritten:
`src/icmp.rs`'s `HOST` doc (false three assertions later), the
`the_checksum_is_the_field_rfc_792_gives_it` doc (its own test name restated),
and the second copy of `is_reply`'s doc inside
`nothing_but_the_reply_to_this_probe_counts`. `gcd.rs`'s "one line per range
handed over" comment is gone with the code it narrated.
cargo test --lib EXIT=0 329 passed
cargo test --workspace --exclude toyos-build EXIT=0 139 suites
cargo test screen_console_shell EXIT=0 PASS (2s)
cargo test bar_placement_is_proven EXIT=0 PASS (3s)
cargo test screen_loader_lines EXIT=0 PASS (3s)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Round 7 review: `loader.log` does not always sort last among the seed files, because logd also writes `unknown-NN.log` on a machine with no RTC and `l` sorts before `u`. The measured conclusion stands on the machines the gate runs on; the sentence claiming more than that is deleted, not rewritten. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Japabu
marked this pull request as ready for review
September 14, 2026 21:37
Japabu
enabled auto-merge
September 14, 2026 21:37
The panel's landing (#444) priced what the on-screen panel cost every boot, and this branch added a boot the panel's author had never seen. One conflict, in `tests/metal-profile.toml`: both sides appended a section to the end of the file, main's `panel_max_us`/`panel_us` rows for each boot and this branch's `lancase` rows for the cable. Both sections are kept, and `lancase` is priced for the two new numbers as well — `tests/common/metal.rs` judges `boot.<label>.panel_max_us` and `boot.<label>.panel_us` for every label it reads back, and a number the profile does not price is a refusal, not a pass. The ceilings are main's own derivations; no run has read the panel on that boot yet, so neither row carries a `measured`. The seven readings main moved on rows this branch never touched are main's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
github-merge-queue
Bot
removed this pull request from the merge queue due to no response for status checks
Sep 16, 2026
Japabu
enabled auto-merge
September 21, 2026 15:42
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A claimed function's BAR could not be placed below 4 GiB at all:
pcidevput oneon the first 2 MiB boundary above everything firmware described, and in 32 bits
there is no such address — the platform's fixed MMIO is at
0xFEC00000and thememory map reaches it. So the ThinkPad T14's I219, whose BAR is 32-bit and shares
its 2 MiB page with the internal NVMe's, could be handed to no process.
A BAR now goes where the machine proves it decodes, and no address this kernel
cannot name as routed is ever read. The name is one: an address inside a window
KernelArgs::root_bridge_windowscarries.bootloader/src/gcd.rsreads DXE'sGlobal Coherency Domain before
ExitBootServices(PI 1.8 Vol. 2 §7.2) and addsits free memory-mapped ranges to that array: EDK2's
PciHostBridgeDxeallocatesevery BAR out of the root bridges' apertures, so an MMIO descriptor with a null
ImageHandleandDeviceHandleis aperture the bridges decode and nothing hastaken. That is the necessary condition
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL'sConfiguration()cannot give, because it answers a bridge's current settings,which a BAR may lie outside.
toyos_pci::placement::reserveanswers no address outside a declared window atall —
Reservation { at, window }has no way to spell one — and takes theaddress out of the runs in the call that chooses it, so two claims arriving
together cannot be offered one. A run that straddles a window's edge answers the
part inside it; a run that holds no such address is passed over untouched.
Inside a window, the function itself is the proof:
place_barreads one dwordthrough the BAR where firmware put it, moves the BAR, reads the same dword there,
and takes the function answering its own value as what says the new address
decodes. A placement that does not answer is undone whole — the register back to
firmware's value, the address back to its run — and the next candidate tried, at
most
MAX_CANDIDATESof them, so a function that answers nowhere cannot spendthe machine's free space. That retry path is unexercised, and this body says so
under "What is not covered" below rather than claiming otherwise.
Three refusals are new, and each costs the function that BAR's window rather than
its hand-over:
BarUnroutedfor a BAR this kernel may not read,BarUnassignedfor one firmware gave no address, and
BarReferenceEmptyfor a BAR whosereference dword is
0x00000000or0xFFFFFFFF— the two values a read nobodyanswers comes back as. QEMU's e1000e is why: its flash BAR reads
0x00000000atevery dword, nothing can prove it decodes anywhere, and a function whose
registers answer through another BAR is still one a process can drive. A function
no BAR of which could be settled ends at
NoMappableBar. Which dword is read isdeclared per function identity in
toyos_pci::probe: a modern virtio functionopens its common configuration with a selector that reads zero (virtio 1.2
§4.1.4.3), so its reference is the
device_featuredword after it.The metal loop no longer runs a host binary
src/metal.rsran the host'sping, declared insrc/sourcegate.rsand nowhereelse. CLAUDE.md's dependency rule is "no binary outside those two", and its
standing failures are Python via
rust/x,cc, and four macOS FAT tools; thiswas a fifth. On its own terms it was worse than that:
ping_oncerefused everynon-macOS host by name, because
-Wis milliseconds to macOS'spingandseconds to everyone else's, so the bench loop could not run on a Linux host for a
reason that was nobody's but that binary's.
src/icmp.rsasks the question directly overSOCK_DGRAMwithIPPROTO_ICMP,the socket both hosts open without privilege. The echo request carries a payload
no other probe of the process writes, its checksum is computed here and written
into the field RFC 792 gives it, and a reply is
addr's only if it came fromaddrand carries that payload — found by the payload rather than at anoffset, because one host leaves the IPv4 header in front of the message and the
other does not, and the type byte is eight bytes before the payload either way.
The identifier is never read: Linux's ping socket overwrites it with the socket's
own port. A kernel that refuses the socket is refused by name, so
Refusal::Probestill means this host could not ask as against nothinganswered. The
Spawnrow is deleted with the call, andsourcegate's own gatereds on a row nothing runs as well as on an undeclared spawn, so the two could
only go together. There is no platform condition left anywhere on this path.
Three defects of that first version are fixed here, and two of them are tests
that could not have seen a wrong implementation:
(
message[4..6]) passed all four committed tests, because the internetchecksum sums to zero wherever in the message the complement sits. Every
request would then have gone out with checksum
0x0000on the host that sendswhat it was given, nothing would have answered, and the bench would have
reported nothing answered a ping for every boot — a verdict that measured
nothing.
the_checksum_is_the_field_rfc_792_gives_itbuilds the unsummedrequest from the token rather than from
request, so bytes 2-3 must carry thatmessage's checksum and bytes 4-7 must be what the unsummed one has. That
mutation now reds, and so does a sum written anywhere else in the header.
Ok((got, _))threw the peer away andthe socket is never connected, so any host on the wire returning this probe's
token counted as the address's answer — and whose reply it was is the question
this loop's judge has already had one live false positive on.
is_replytakesthe sender and the address asked and answers
falsewhen they differ;nothing_but_the_reply_to_this_probe_countsasserts it with this probe's ownanswer returned from
10.0.0.9.chunks_exact(2)reds the requiredhostcheck under-D clippy::chunks-exact-to-as-chunks, which arrives in 1.98.0 and thereforeon the macOS runner; this worktree's rustc is 1.97.1 and the local gate cannot
see it (run 34880800558, job 104099314690, exit 101,
could not compile toyos-build (lib)and(lib test)).as_chunks::<2>()is the lint's ownanswer, and the workspace gate below is re-run and green behind it.
toyos-metalalso stops parsing its own argv.declare_flags!/Vocabulary—the declaration
cargo run --and the harness both read — now carries its tenflags, so a flag given no value, an inline value on a flag that takes none, and a
flag written twice are refused by the same walk that refuses them for the other
two command lines. The hand walk decided each of those for itself, and silently.
The loader logs what it hands over
A line per GCD descriptor is 35 lines on q35 and 76 on the T14, and they land at
the head of the boot log — which on a machine with no serial port is the panel
/system/bin/consoleseeds itself from and the owner reads. They pushed thekernel's own first screenful off it, and
screen_console_shellis the test thatreads that panel:
Red twice on
guest (9)of run 34880800558 (job 104099376049) and red again onthe dev host, where all 67 rows of the decoded panel are loader output, a blank
line and the prompt. It is this branch's line count and not a console defect this
branch found: the seed and the view are what they were, and 35 lines went in
front of them. The log now carries one line per free MMIO range of at least
GRANULE— the rangesKernelArgs::root_bridge_windowsactually receives, andone for any such range the kernel has no room for — behind the summary line that
was already there, and the same test passes in 2 s.
The file is not a second channel, so that cut is a real loss.
/system/bin/consoleseeds the screen from every*.logfile in/log, newesttwo (
userland/console/src/main.rs:277-286), and/logis the root of the logpartition the bootloader writes
loader.logto — so a line written to it costs apanel row exactly as a printed one does. Measured: with the whole map moved to the file
alone and off the console,
cargo test screen_console_shellis EXIT=1 with all34 of q35's map lines on the decoded panel, where the same head without them on
either channel is EXIT=0,
PASS (2s),30 kernel log rows above a prompt. Onthe T14 this head logs 6 of the 76 GCD lines that branch's readback carries. The
70 dropped include firmware's current MMIO allocations — the reading that
checks this file's own premise against a real machine — and the 736 MiB
nonexistentrun. That is a compromise, and it is recorded rather than tradedaway:
issues/diagnostics/the-panel-seeds-the-loaders-own-file-so-the-gcd-map-is-logged-nowhere.mdhas ownership, that control as evidence, and an exit condition — the guest
applying the split
src/bootlog.rs:175-187already makes for the host-sidereader, after which
loader.logcosts no panel row and the whole map goes backinto the file alone.
The two checks
Negative control — the whole change reverted onto this PR's base.
kernel/src,bootloader/srcandtoyos-pci/srcchecked out atad61ed08, the base the green arm was measured on, thisbranch's arm at
ca50ef7bleft in place as the instrument. That is the whole change over those threedirectories:
git diff ad61ed08 ca50ef7b -- kernel/src bootloader/src toyos-pci/srcis 14 files, 1252 insertions, 221 deletions, and the revert takesall of it.
Restored with
git checkout HEAD --, and green in the gate table below.Oracle — an external specification this project did not write. The condition
every read on this path is held against is EDK2's own allocator contract: DXE's
GCD records each root bridge's aperture and
PciHostBridgeDxeallocates everyBAR out of it (PI 1.8 Vol. 2 §7.2), so a free MMIO descriptor there is address
space the bridges decode. The alignment the placement uses is the PCI base
spec's, not this kernel's: a BAR's low address bits are hardwired to zero, so a
window wider than a page starts on its own size (PCIe base spec §7.5.1.2.1),
cited at
toyos-pci/src/placement.rs. Both are read by the pure crate's testsover the T14's own windows and runs.
And the refusal that the reference dword settles nothing is asserted where it
fires.
probe::degenerateis what keeps a candidate from being proved by0 == 0, and nothing asserted the record it produces:https_tls13_e1000epassed with the refusal and without it, its verdict being that netd came up. The
judge now declares per machine why that card has a BAR no read can settle and
asserts the count —
tests/e1000casekeeps exactly one and says by whichrefusal, the virtio bench keeps none. Removing the refusal (which costs the
variant with it: dead code is an error in this kernel — 1 insertion, 14
deletions) reds the arm, twice and for that reason:
What is not covered, stated rather than implied
The comparison this change exists for has no arm that reds when it is deleted.
place_barsettles a candidate onafter == signature; on every machine inreach the first candidate answers, so deleting the comparison and accepting
unconditionally leaves every record byte-identical and
bar_placement_is_proven,pci_function_is_exclusive,https_tls13_e1000e,virtio_net_no_msixanduserdev_dma_faultall green. The retry-and-undo path under it — theleft {at:#x}record, the register back to firmware's value, the address back toits run — is therefore reached by no boot in this tree.
The tree cannot manufacture one. Inside a declared window q35 routes every
address to PCI, so a BAR moved anywhere inside one decodes there; the addresses
that answer nothing — the platform's fixed MMIO, a range no bridge forwards — lie
outside every declared window, where
Reservation { at, window }cannot spellthem and
aperture::decodewould refuse the read before the comparison wasreached. It is a present-state weakness, not a closed one, and it is recorded
with an exit condition in
issues/kernel/no-boot-reaches-the-retry-that-undoes-a-placement-nothing-answered.md:a guest whose claimed function sits behind a
pcie-root-portgiven a memorywindow of a few MiB. The port's window is the thing to arrange — the case
exists only because
root_bridge_windowscarries the loader's free GCD ranges,which are the host bridge's whole aperture (
0xc1100000+0x3af00000on q35) andnot the port's slice of it, so a candidate past that slice is an address this
kernel may name and the function does not answer at.
issues/kernel/no-boot-reaches-the-refusal-that-keeps-a-bar-read-inside-a-declared-window.mdcarries the same class for
BarUnrouted,NoRunandNoPlacement, with thethree addresses that make each unreachable.
The machine
The ThinkPad T14 ran
tests/lancaseoff this branch's image; the readback istarget/metal-placement-run39/lancase/, and every number below is from it. Itcame back:
PASS: the machine booted ToyOS in 1208 ms, ssh again after 82 s, thestick 0 s, EXIT=0.
git diff 4204e090 HEAD -- kernel bootloader toyos-pci toyos-abi toyos userland toyos-acpi toyos-dma system.tomlanswers four files, 33 insertions and 60deletions:
kernel/src/pcidev/mod.rs,toyos-abi/src/boot.rsandtoyos-pci/src/placement.rsare comment-only, andbootloader/src/gcd.rsis thelog cut above — which descriptors the loader prints, never which ranges it hands
the kernel. So the boot below is this head's reading, and the one executable
difference is that this head would write 6 of that readback's 76
GCD:linesinstead of all of them: the 5 free MMIO ranges of at least
GRANULEit handsover, and the summary.
The straddling-run fix is visible on hardware. The loader's first-ever GCD
dump of this machine —
75 descriptor(s); 5 free mmio range(s) of 0x200000 bytes or more handed to the kernel— carries0xa0800000+0x1800000 mmio … free, andthe BAR went to the run that begins at that declaration's own base:
0xa0800000..0xa2000000is the 24 MiB run whose first 2 MiB-aligned address liesbelow
0xa2000000; before this roundreserveemptied it whole over that andthe placement fell through to
0xae200000(the earlier metal run). The samedword answers at both addresses.
The card still raises nothing:
userdev=0on all eight CPUs at 22.845 s. That isissues/hardware/the-i219s-phy-is-never-brought-up-and-the-part-raises-no-interrupt.md,the PHY branch's subject, not this one's.
And the first live false positive
lan-metal's judge has been handed. Theloop recorded
192.168.1.46 answered a ping 81 s into the window … at 1789380606 UTC seconds— but that reply is Ubuntu's wire, and this boot was long gone: itsRebooting.record is at10:09:30and1789380606is10:10:06 UTC, 36 slater, with the loop's own
clock_skew 0.tests/common/lan.rsnow judges thatbracket before it prices anything, so a reading from the operating system on
the other side of the reset is refused rather than priced and then refused.
Three of that boot's readings are folded into
tests/metal-profile.toml:complete_ms1208,back_secs82,stick_secs0.ping_secscarries none.A
measuredis what the boot did, and the only ping that row has been offeredwas answered by the operating system on the other side of the reset — a ceiling
tightened against 81 s would red on the first real reply.
link_up_ms,lease_msandjob_msstay unmeasured for the same reason: nothing on this bootproduced them.
The profile: the three markers are priced
cirun 34880800558 on this pull request carried all twelvedurations-shard-*artifacts, so the three committed
UNMEASUREDmarkers this stack registered havetheir measured prices —
bar_placement_is_proven3,297 ms,lan_dhcp_lease3,029 ms,
lan_no_lease32,709 ms, each read off that run's own shard file andequal to its
test-durations-mergedartifact.Those three rows carry that run's
shards=12prices, and only those three.Replacing the whole merged artifact would have re-tiered five names this change
does not own —
sshd_exec8,478 against a committed 5,110,boot_partition_identity9,137 against 5,519,root_candidate_malformed8,399against 5,126,
sysret_ss_reload26,273 against 6,453, andscreen_console_shell56,161 against 2,604, which is the wall time of thatshard's failure and not a price at all.
assert_fast_profile_labelgrades thecommitted profile before anything boots, so importing them reds every
cargo testin the tree for five names whose owners are elsewhere.47b794b9refused the same trade for the same reason and
src/redlist.rs's standingsysret_ss_reloadprice row records it.lan_no_leaseis over the line by construction rather than by shard luck: itsverdict is that no lease arrived before
toyos_tco::LEASE_BOUND_MS(20 s) ranout, and it drains ten seconds more to read what netd said afterwards. It is
Tier::Nightlywith aWhy::TimerAnchoredrow saying what stops being gated perpull request.
Gates, each with the command's own exit status
Every
EXIT=is the command's own, never a pipeline's. Only--clippy,--build-onlyand--abi-split-checkwere used ascargo runflags, and allthree are declared in
src/flags.rs.Re-run at
ca50ef7b, whose change over the head above is the loader's log andcomments:
The merge from main
2898c0f0mergesorigin/mainatad61ed08— 22 commits, #455'sdeclaration-driven command lines under three files this branch edits, #448 and
#449's root bridge windows under the two it rewrites.
git merge --no-commit --no-ff origin/mainansweredAutomatic merge went well, EXIT=0, with noconflict to resolve in any file. Never a rebase.
Kernel, loader, build system and tests; no ABI.
issues/kernel/a-bar-is-placed-in-a-window-firmware-names-no-part-of.mdandissues/kernel/a-32-bit-bar-needs-the-host-bridges-aperture.mdare deleted withevery citation to them: neither is true of this tree.
issues/build/an-unknown-flag-boots-qemu-instead-of-being-refused.mdgoes in themerge from main, which brought the
src/flags.rsthat closed it, andissues/build/toyos-metal-parses-its-own-argv.mdgoes with the walk it named.🤖 Generated with Claude Code
https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK