…-Fi P1)
A claim used to move every BAR it handed over onto a 2 MiB slot of its own,
because 2 MiB pages were the only ones this kernel mapped into a process.
That move is why the AMD bench's xHCI 03:00.4 (behind 00:08.1, whose window
bus 3 fills) and the AX210 behind its root port could not be claimed, and
it is what pcidev's placement machinery existed for.
User mappings now take any run of 4 KiB-aligned pages: a 2 MiB leaf wherever
a whole aligned frame lands on a 2 MiB boundary, a table of 4 KiB leaves for
the rest (toyos_userbound::leaves, host-tested, shared by both
architectures). Unmapping a slot of 4 KiB leaves frees its table: on x86-64
after a user-half shootdown, since a sibling's paging-structure cache may
still name it (SDM Vol. 3A 4.10.3); on AArch64 after the broadcast TLBI by
ASID that already completes on every CPU. x86-64's 4 KiB leaves carry their
own PAT encoding (bit 7), and the AddressSpace's never-written `pages` map
is gone.
pcidev records every memory BAR at publish and hands a BAR over at that
address, in the pages it covers, only where no other function decodes inside
them and the pages lie in the root bridges' declared memory
(toyos_pci::pages, host-tested); otherwise the BAR is withheld by name and
the function still handed over. The register must still hold firmware's
address (BarMoved). Every BAR is chosen before anything on the function
changes, and the interrupt is armed last, so no refusal follows an armed
message and bring_up's undo path is gone. publish logs how many memory BARs
share a 4 KiB page with another function: the bench measurement.
Deleted: toyos_pci::placement (free runs, reservation, behind_bridges),
toyos_pci::probe (the "answers its own value" reference dword),
toyos_pci::bridge (the bridge walk), bar::placement, PciDevice::bridge and
::forwarded, and in pcidev place_bar(s), Source, Candidate, give_back,
cut_already, was_cut, cut, alone_in_its_page, probe_dword, the 2 MiB slot
runs and their records, and the refusals only they raised. MSI-X arming
answers its table's address, which is where tear_down masks it.
No BAR firmware left unassigned is assigned: main had no path that did
(place_bar refused BarUnassigned), and the hot-plugged endpoint that needs
one arrives with lifecycle stage 2, which brings its own assignment inside
the port's window.
Tests: bar_map_behind_a_bridge asserts the NIC's BAR is handed over at the
address enumeration read; msix_in_place reads the MSI-X entry the kernel
armed back through the holder's mapping of the table's page, in
bar_map_again's boot and as the msix_table_read_in_place metal row (the
T14's AX210 and NVMe, the AMD bench's AX200); amdvi_usbd_drives_its_own_entry
and its judge return from c648c14. The NVMe guests drop
msix-exclusive-bar=on and drive QEMU's default controller through its table
in BAR 0.
Issues: closes a-bar-behind-a-bridge-with-no-free-2-mib-... and
no-boot-reaches-the-retry-that-undoes-a-placement-nothing-answered; restates
P1 of there-is-no-wifi, the survey and refusal-reach issues and the
process-memory track, whose ledger, counts and straddle segments device
windows did not need.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016UzbDnM9m6nQWhbjGi6Lv1
P1 of
issues/there-is-no-wifi.md, with its prerequisite, 4 KiB user leaves for device windows.Problem
A claim moved every BAR it handed over onto a 2 MiB slot of its own, because 2 MiB pages were the only ones the kernel mapped into a process. That move is why the AMD bench's xHCI
03:00.4(behind00:08.1, whose window bus 3 fills) and the AX210 behind its root port could not be claimed, and it was what pcidev's placement machinery existed for.Resulting behaviour
map_range, both arches): a 2 MiB leaf wherever a whole aligned frame lands on a 2 MiB boundary, a table of 4 KiB leaves for the rest. The choice is one pure function,toyos_userbound::leaves, host-tested. Unmapping a slot of 4 KiB leaves answers the table it unlinked (paging::Detached), which the caller drops inside its existing shootdown, since a sibling's paging-structure cache may still name it (SDM 3A §4.10.3); no extra shootdown is issued. x86-64 4 KiB leaves carry their own PAT bit (bit 7). Page-table ownership and the IOMMU domain layout are unchanged.publishrecords every memory BAR; a claim hands one over only where no other function decodes inside its pages and they lie in the declared root-bridge memory (toyos_pci::pages, host-tested). Otherwise the BAR is withheld by name (BarPages,BarUnrouted) and the function still handed over. The MSI-X table's page goes with its BAR; the kernel's remapping-entry rule (AMD-Vi stage 6: interrupt remapping, and claims only where every unit filters SMIs #921) is untouched, and tear-down masks the entry at the table address arming read.pcidev: N memory BAR(s), M of them sharing a 4096-byte page with another function or starting inside one. q35: 11 to 14 BARs by machine shape, 0 sharing (boots). Metal at 5f4418f: T14 21 BARs, AMD 14, 0 sharing (T14, AMD).msix-exclusive-bar=onis gone frombuild/qemu.rsandtests/common/qemu.rs: the NVMe guests drive QEMU's default controller through its table in BAR 0 (nvme_disk_bootsand the diskserver boots green).Deleted (measured)
40 files, +914 −2032 overall; kernel/lib/sdk/build +538 −1784. Gone:
toyos_pci::placement(545 lines: free runs, reservations,behind_bridges),toyos_pci::probe(the "answers its own value" proof),toyos_pci::bridge(the bridge walk, 291),bar::placement,PciDevice::bridge/forwarded, and in pcidevplace_bar(s),Source/Candidate,give_back,cut_already/was_cut/cut,alone_in_its_page,probe_dword, the 2 MiB runs and slot records (pcidev −620 +164), and the refusals only they raised; the x86AddressSpace::pagesmap nothing wrote (#940 deletes it too, merged with main c0b1547).Decisions
place_barrefusedBarUnassigned), and the hot-plugged endpoint that needs one is lifecycle stage 2's (wt/toyos-lifecycle-2), whoseplace_unassignedwill need rewriting onto this: a small assignment inside the port's window, no probe.OWNEDledger or per-process count is needed, and a BAR's pages are in physical order, so a typed value straddling two of them is still one run (object_run's one-segment store andwindow_split's bound hold). What remains of stage 3 — the ledger, demand-zero stacks, 4 KiB TLS, straddle segments, the T14 floor — is restated in the issue.What temporarily stops working
Nothing measured.
Bench round 1 (5f4418f, run by the orchestrator)
T14 3/3 PASS. AMD 2/3:
amdvi_translates_the_stick_controllerandmsix_table_read_in_placePASS, and the first claim behind a bridge on metal handed03:00.4's BAR 0 over in place (0xd0100000..0xd0200000) and usbd drove it.amdvi_usbd_drives_its_own_entrywas red inend_returns_claims, which chose 1022:1639 by id on a bench that has two; it now chooses the spare asusbd_sparedoes and claims it by arrival (dc18514). Readbacks, judges and manifests: bench.Review round 1
Fixed the two comment blockers (AArch64
Tables' ownership,survey's doc and unrouted line), took the shootdown suggestion above, droppedbar_map_behind_a_bridge's by-construction address comparison (the job's register read through the BAR behind the port is the claim), recorded P1's T14 NVMe exit as an Identify Controller completing through the in-place entry, recorded that P1's rows need no metal-row naming first, added theplace_unassignedrewrite andMachine::decodedto lifecycle stage 2, and the nits. Declined: keepingPagesinBound(one rounding helper, no defect), and refusing a claim whose MSI-X table BAR is unrouted (pre-existing; recorded nowhere new). All four bench images re-staged, since the unmap change is in every kernel.Tests and evidence (ToyOSOrg/evidence
pr-947, README)cargo run -- --ci host, exit 0, 86 steps, at ecab334 (log); earlier at 3ef1e64 (log). New host tests:toyos_pci::pages(in place, sub-page BAR, page sharing refused, off-page, wrap) andtoyos_userbound::leaves(device windows on and off a 2 MiB boundary, whole frames, misaligned runs); segment test notes the straddle-in-one-run case.--build-onlyx86-64 and aarch64, exit 0 at ecab334 (x86, a64) and at 3ef1e64 (x86, a64).usbd_refused_without_smi_filterandusbd_refused_without_amdvi_remapping. At 3ef1e64, 9/9 exit 0 (log):usbd_drives_the_spare,bar_map_again(now also runsmsix_in_place: the NIC's armed MSI-X entry read back through its holder's mapping of BAR 1's table page equals what the kernel logged writing),bar_map_behind_a_bridge(rewritten: was "BAR placed inside the port's window"; now the NIC behind the port is handed a BAR in place and the job reads its register through it),lent_device_memory_refused,a_drives_row_holds_one_process_per_function,the_compositor_drives_the_virtio_gpu,iommu_virtio_platform,virt_claim_lpi,nvme_disk_boots. No row deleted. An earlier red run (log) foundbar_map_againreading the NIC's first BAR, which is now BAR 1 (the table, withheld before because it read zero at the probe dword); the job names BAR 4.a_page_another_function_decodes_in_is_refused_by_name; a short 2 MiB-aligned window mapped as a 2 MiB leaf reds two leaf tests; both restored green. The "ledger" control the brief named has no subject: no ledger was built (above).usbd_drives_the_type_c_controller,claim_reuses_its_remapping_entry, newmsix_table_read_in_place(AX210 and NVMe entries at BAR 0 + 0x2000 read through their holders' mappings); AMDamdvi_usbd_drives_its_own_entry(restored with its judge from c648c14),amdvi_translates_the_stick_controller,msix_table_read_in_place(the AX200 behind 00:02.2).Issues
Closed:
a-bar-behind-a-bridge-with-no-free-2-mib-of-its-windows-is-not-handed-over,no-boot-reaches-the-retry-that-undoes-a-placement-nothing-answered. Rewritten: P1 inthere-is-no-wifi(built; bench exits owed; the T14 NVMe driven through its table stays owed, the disk being another system's),nothing-reds-when-the-bar-survey-drops-an-assigned-bar,no-boot-reaches-the-refusal-that-keeps-a-bar-read-inside-a-declared-window,process-memory-is-2-mib-pages...(device windows done, stage 3 remains), and citations in four others.Risks
🤖 Generated with Claude Code