Skip to content

A claimed function is armed on the mechanism it publishes, and the arming says which case it refused - #443

Open
Japabu wants to merge 4 commits into
mainfrom
msi-claim
Open

A claimed function is armed on the mechanism it publishes, and the arming says which case it refused#443
Japabu wants to merge 4 commits into
mainfrom
msi-claim

Conversation

@Japabu

@Japabu Japabu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

pcidev::bring_up armed MSI-X and nothing else, so a function publishing no MSI-X capability was refused by name and its holder never ran. Every function this project had handed to a process 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 I219 at 00:1f.6 is not one of those. Measured, 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.

The arming says which case it refused

PciDevice::enable_msix collapsed four outcomes into one None. Its caller therefore had to walk the capability list again to tell "publishes no table" from "publishes one this kernel could not arm", and the refusal it raised asserted a reason false on three of the four paths. It now answers Result<Mmio, Unarmed> (kernel/src/drivers/pci.rs:33-44):

  • Unarmed::Absent — the function publishes no MSI-X capability.
  • Unarmed::Unusable — it publishes one whose table this kernel could not reach (Msix::decode, the BIR, or the table address).
  • Unarmed::Blocked — the unit refuses this function's message. enable_msi calls the same PciDevice::message, so MSI would carry the same refused message.

bring_up is one match over that and walks no capability list of its own — kernel/src/pcidev/mod.rs:489-496:

let armed = match pci.enable_msix(VECTORS[slot]) {
    Ok(entry) => Armed::Msix(entry),
    Err(Unarmed::Unusable) => return Err(Refusal::MsixUnusable),
    Err(Unarmed::Blocked) => return Err(Refusal::NoInterrupt),
    Err(Unarmed::Absent) => {
        pci.enable_msi(VECTORS[slot]).then_some(Armed::Msi).ok_or(Refusal::NoInterrupt)?
    }
};
  • publishes MSI-X → armed on MSI-X, or refused MsixUnusable. Its table and PBA are in a BAR, and falling back to MSI would be arming a function whose table is a structure this kernel could not read; a holder that can write a table entry can point the device's write at any address the LAPIC decodes.
  • publishes MSI only → armed on MSI. Its message is a word of configuration space, which has no write path from userland.
  • neither → NoInterrupt, which is virtio_net_no_msix's refusal.

There is no else if publishes(msi::CAP_ID) arm any more, and no closure re-deriving what the arming already knew. enable_msi opens by looking its capability up and answers false without one (kernel/src/drivers/pci.rs:324-326), so that arm decided nothing: deleting it moved no log line on any machine, and no test in any tier could have seen it. The test that sees the surviving structure is virtio_net_no_msix (tests/common/faults.rs:281-283, Fast): it takes the Absent arm, reads enable_msi's false back, and requires neither its MSI-X nor its MSI could be armed.

Refusal::MsixUnusable says only what is true on every path it fires on. It used to say 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 withholds that BAR (kernel/src/pcidev/mod.rs:539). The message() path is split off entirely: it is NoInterrupt now, because enable_msi fails identically there, and the console had already printed PCI …: not armed — {why} one line above the old contradiction.

PciDevice::capability(id) (kernel/src/drivers/pci.rs:363-366) is the one spelling of "find this function's capability by id", replacing 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.

That hand-over rule is stated once, in the module whose subject line is a function driven by a process (kernel/src/pcidev/mod.rs:1,18-23). The kernel's own xHCI (kernel/src/drivers/xhci/wait/boot.rs:101-105) and HDA (kernel/src/drivers/hda.rs:664-668) drivers arm MSI-X or fall back to MSI, and that is not the same choice under a contradicting rule: nothing is handed over there, so no BAR carrying a table ever reaches a holder. Both, and virtio_sound::arm_interrupt, move from .is_some()/.is_none() to .is_ok()/.is_err() and decide nothing new.

Bound holds whichever mechanism was armed. The driver above the boundary cannot tell which it got and does not have to: both deliver the same vector into the same Interrupt and the claim answers the same handle either way. disable_msi is the enable bit alone.

Size, measured

git diff origin/main HEAD --numstat -- kernel toyos-pci tests userland is +111 / −61; git diff origin/main HEAD --shortstat over the whole diff is 12 files changed, 194 insertions(+), 104 deletions(-). toyos-pci/src/lib.rs and userland/netd/src/virtio_net.rs are byte-identical to origin/main (git diff origin/main HEAD -- <path> empty). tests/common/iommu.rs is not, and an earlier revision of this body said it was: git diff origin/main HEAD --shortstat -- tests/common/iommu.rs is 1 file changed, 1 insertion(+), 6 deletions(-), the six-line BAR_MOVED/MSIX_ARMED doc cut to its one contract clause.

The two checks

Negative control — the whole change reverted onto the base the green arm was measured on. Every implementation line this branch adds is in kernel/ and toyos-pci/; tests/common/faults.rs's one changed assertion is the only test change. So the control is exactly:

git checkout origin/main -- kernel toyos-pci
cargo test --test toyos-build -- virtio_net_no_msix     # expected: red
git checkout HEAD -- kernel toyos-pci

The base kernel prints its MSI-X could not be armed; the arm requires neither its MSI-X nor its MSI could be armed.

What it measures, and what it does not. It reverts the whole implementation and is red on the base, which is the rule. What it discriminates is the refusal string: the MSI arm, Unarmed's three cases, MsixUnusable and both disable_msi sites are reverted by it and every arm stays green. That hole is issues/kernel/nothing-reaches-the-msi-arm-of-a-claimed-function.md, not a claim of coverage made here.

This control has not been run, and neither have the four guest arms. Every cargo test --test toyos-build invocation in this worktree is refused before it builds, because the machine-wide sysroot is claimed by another checkout for an ABI change that is not on main yet:

$ cargo test --test toyos-build -- virtio_net_no_msix
thread 'main' panicked at src/toolchain.rs:1382:29:
this worktree and the shared sysroot at /Users/jan/Dev/jan/toyos/rust disagree about toyos-abi/src…
the sysroot belongs to /Users/jan/Dev/jan/toyos-aperture (branch wt/toyos-aperture)…
**Wait for it to land and merge main.** This refusal then ends by itself.
Do not pass --claim-sysroot.
EXIT=101

That refusal names --claim-sysroot as the thing not to do, so it was not done, main was not merged in, and no workaround was attempted. The four guest arms run on CI for this PR regardless; the control above is one command and is owed before this lands.

Independent oracle — Linux's own reading of this exact function on this exact machine: IR-PCI-MSI-0000:00:1f.6 … enp0s31f6 and msi_irqs/162 mode=msi, a differential implementation nobody here wrote, saying 00:1f.6 is an MSI part. Beside it, the capability's register layout is the PCI spec's and toyos-pci/src/msi.rs's tests encode it: address, data and mask offsets all move with the 64-bit address bit, and a vector written at the wrong one lands in whatever capability comes next in the list.

What the bench has measured, and what it has not

Two T14 runs, on different bases — run 28 is tip=220305b4 (t14-run28/lancase.log:1), run 29 is tip=24625c6b (t14-run29/lancase.log:1), whose parents are 938957ae and 8dec3ec8, and 938957ae ("The address the loop pings is the claimed function's, not the machine's name") is in one and not the other. The pair is therefore not a negative control and a green arm; it is two measurements.

  • Run 28 (t14-run28/lancase.log:352, 1.349 s): pcidev: PCI 00:1f.6 NOT HANDED OVER — its MSI-X could not be armed…; netd exited code=0 at 2.268 s (:370).
  • Run 29 (t14-run29/lancase.log:352-353, 1.445 s): iommu: irte5 source=00:1f.6 … vector=0x28 and PCI 00:1f.6: msi address=0xfee000b8 data=0x00000000 — MSI armed all the way to the message, on real hardware. Then :354: 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; netd exited code=0 at 2.521 s (:372).

So the claim is still refused and the cable is still dark: this change moved the refusal from NoInterrupt to NoWindow, and no interrupt has been delivered on MSI yet. The stacked 32-bit-window branch is what the next gate needs.

Sentences in this branch's record that are false, retracted by name

History is not rewritten here, so each retraction is a later message.

Commit 8dec3ec8 — "nothing answered on the cable for the twenty seconds the boot stayed up", contradicted by t14-run28/lancase.log:12; and "Run 28 on the T14 is this change reverted whole, on the base the granted claim will be measured against", contradicted by the two tips above. Both retracted in 76fb456d's message.

Commit 76fb456d — its message attributed "five sites" to grep -rn 'disable_msi\b\|enable_msi\b' kernel/. Re-run, that command prints seven lines: the two definitions plus five call sites. The enumeration that followed it was of the call sites and is right. Its body also cited the choice at mod.rs:487-496, which was the comment above the block and one line short of it. Both retracted in 6d59a093's message, and this body carries the corrected numbers.

What this leaves standing, and to whom

Recorded in issues/ with ownership, evidence and an exit condition — not in this body:

  • issues/kernel/nothing-reaches-the-msi-arm-of-a-claimed-function.md — no test in any tier arms a claimed function on MSI. virtio_net_no_msix calls enable_msi from bring_up and reads false back; nothing reaches a true, so nothing reaches disable_msi from either hand-back site, Armed::Msi's teardown, Refusal::MsixUnusable or Unarmed::Blocked. The two pre-existing MSI armings (xHCI's at boot.rs:104 and HDA's at hda.rs:666) never disarm, so MSI teardown is exercised nowhere in the tree. Owned by the network track's stage-2 I219 worker; the exit is the first userdev interrupt counted against a claim on 00:1f.6, plus netd exiting from that claim.
  • issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.mdSYS_DEVICE_REG_WRITE on a RegTarget::PciConfig target is refused NotSupported and nothing reads that refusal, which is what a handed-over MSI function's safety rests on. The exit is a guest arm in which a holder calls it on its own claim, red against a kernel whose arm answers Ok.
  • issues/hardware/toyos-i219-refuses-a-part-outside-msi-x-mode-at-ivar.mdtoyos-i219 writes §10.2.4.9's IVAR and reads it back, that section defines the register only "in MSI-X mode", and 00:1f.6 is measured to be outside it. Owned by the stage-2 I219 worker. That is the half of the old tracker this branch does not remove, which is why the file is narrowed and renamed rather than deleted.

Green

Each row is the line the command printed and its exit status, run in this worktree at 6d59a093.

command result exit
cargo test -p toyos-pci test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 0
cargo test --lib test result: ok. 295 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out 0
cargo test --workspace --exclude toyos-build 138 suites, 1347 passed, 0 failed, 0 ignored 0
cargo clippy --workspace --all-targets --keep-going -- $ADOPTED -D warnings Finished dev profile, no warning 0
cargo clippy --target x86_64-unknown-none -- $ADOPTED -D warnings (in kernel) no warning 0
cargo clippy --target x86_64-unknown-none --features boot-actuators,test-actuators -- $ADOPTED -D warnings (in kernel) no warning 0
cargo clippy --target x86_64-unknown-uefi -- $ADOPTED -W clippy::undocumented_unsafe_blocks -D warnings (in bootloader) no warning 0
cargo clippy -p toyos-abi --all-targets --keep-going -- -W clippy::undocumented_unsafe_blocks -D warnings no warning 0
cargo test --test toyos-build -- virtio_net_no_msix (and the other three arms, and the negative control) not run: the sysroot is claimed by /Users/jan/Dev/jan/toyos-aperture 101

All five src/clippy.rs shapes are above — the whole gate, not a subset. $ADOPTED is the workflow's six lints, spliced token by token as src/clippy.rs:14-20 declares them. cargo test -p toyos-pci is 40 here against 39 on origin/main: this branch adds disabling_clears_the_enable_bit_and_nothing_else and nothing else.

🤖 Generated with Claude Code

`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
@Japabu
Japabu enabled auto-merge September 8, 2026 15:32
Japabu added a commit that referenced this pull request Sep 8, 2026
The green arm cannot be measured until the kernel grants netd the I219's
function, and that is #443. Merged here so the image the T14 runs carries it;
when #443 lands, merging main into this branch is the same commits again.
@Japabu
Japabu disabled auto-merge September 8, 2026 15:38
Japabu added a commit that referenced this pull request Sep 8, 2026
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.
…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
@Japabu Japabu changed the title A claimed function is armed on MSI where it has no MSI-X A claimed function is armed on the mechanism it publishes, and MSI is not a fallback Sep 8, 2026
Japabu and others added 2 commits September 8, 2026 18:58
…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
…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
@Japabu Japabu changed the title A claimed function is armed on the mechanism it publishes, and MSI is not a fallback A claimed function is armed on the mechanism it publishes, and the arming says which case it refused Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant