Repository navigation
PCI apertures are a declaration the kernel holds, made from each root's _CRS (design; stage A: the declaration and the hybrid IOMMU rule) - #916
Conversation
…claim is handed over without one The kernel kept firmware's window list as a bare Vec, read wherever a BAR was placed, and an empty or self-contradicting list reached placement as if it were a machine's apertures. It is now one of three states: unknown, declared, or refused. `toyos_pci::aperture::check` decides whether a list can be held at all (at least one window, each an extent inside the address space, no two sharing an address) and refuses it whole, because a list with one window dropped has a hole a BAR would be placed in. A PCI claim reads the state before it reserves a slot or touches the function: a machine with no declaration, or a refused one, hands nothing over and says which. The windows a claim was admitted under are the ones its placement reads, passed down rather than read again. The loader's list is still the one declared, at `publish`, so every machine that declared windows before declares the same ones now; the T14's recorded list is the host test's held case. This is the first stage of taking the declaration from each PCI root's `_CRS` in acpiserver (issues/an-aarch64-cargo-run-boot-hands-over-no-pci-claim.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Orchestrator rulings on the two open decisions.
Finish stage A on that: the state machine and the fail-closed claim as built, plus the hybrid placement rule keyed on the unit's width against the CPU's; then the checks ( |
… past them, and under the declared windows otherwise A domain's device addresses have to miss every root-bridge window, since a bridge may route a request inside one peer-to-peer before the unit sees it (PCIe Base §2.4). Until now each was placed a quarter of the way up what the unit translates and ended under the first window the loader listed, which made every domain depend on a window list. Where the unit translates more bits than the CPU's physical addresses (VT-d §11.4.2's MGAW, and the SMMUv3 tables' 48-bit input, against CPUID 8000_0008H or ID_AA64MMFR0_EL1.PARange), the domain sits above every address the CPU can issue: no memory and no window the CPU reaches lies there, so nothing needs to be declared. That covers `virt` and q35. Where it does not, as on the T14 (`aw=48 mgaw=39`, windows reaching 0x8000000000, tests/checks/claims.rs), the domain keeps the old rule: a quarter of the way up, ending under the first declared window or reserved region above that. It now reads `pcidev`'s accepted declaration rather than the loader's raw list, and with none held there is no domain, refused as `Undeclared`. The loader's list still feeds that declaration, so the T14's domains are placed as before. AMD-Vi makes no domain on main; its width (48882) is the rule's to read when it does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…s the loader's window list The root's _CRS on virt declares neither ECAM nor flash; stage B of #916 declares from it and closes the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Review of #916 at
|
Addendum: the published T14 evidence (
|
The shared domain registry and AMD-Vi's own domains (#909) take the optional declaration as vtd and the SMMUv3 do: Domains::avoid holds None where no declaration is accepted, and amdvi::init reserves its IVMDs only beside declared windows, as vtd its RMRRs. pcidev hands the IOMMU a validated toyos_pci::aperture::Declaration, which only check() makes. PARange 0b0111 is FEAT_D128's 56 bits; the window header names no machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
A claim now takes its function by number (#918); the declaration is still read before the function is taken or touched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…le is asserted, and every boot says the CPU's width The AMD bench's recorded root-bridge list (#909's bench log) joins the T14's as a held case of `check`. Its AMD-Vi domains are 48 bits (mode-4 host tables, 48882 §2.2.3) and its IVinfo's PASize is 48; if its CPU also issues 48-bit addresses it keeps the declared rule, from 2^46 to 2^48 with no window above 2^46, which a const assertion now holds. The CPU's width was never in a boot log, so `iommu::init` says it once and the bench's own boot decides which rule it is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Round 1 answered at
Evidence: https://github.com/ToyOSOrg/evidence/blob/79c0224a9a354773bb77d3f960d508fe5dc0fb67/round2/README.md |
|
Round 2 on metal published: evidence commit
Every file is whole (none is 0 bytes) and masked with |
Round 2 review of #916 at
|
Any unit translating no more bits than the CPU's physical addresses takes the declared rule, AMD-Vi's mode-4 tables on a 48-bit CPU among them; the header read as if VT-d alone did. The repeated "above all memory" goes, and the declaration test's doc says its lists are seven windows, some meeting end to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Round 3 at
|
…intremap pcidev: main's root-bridge declaration (`windows` to `bring_up` and `place_bars`, the `Undeclared` and `AperturesRefused` refusals) beside this branch's `NotRemapped` reasons and `NotIsolated`; the table's BAR is placed like any other, so `table_bar` goes. amdvi: `domain::start` takes main's optional reserved ranges and this branch's platform ids. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
Evidence branch |
Status: draft. Stage A, final head
01f59e28c(main9842eeda5merged). Review round 3 asked for a comment fix only. Both machines ran5dabb22b5: the T14 3 of 3, the AMD bench 1 of 1. Since then only comments and the merge changed, and--ci hostis green at the head.Problem
cargo run -- --arch aarch64hands over none of its four PCI functions (diskserver's NVMe, soundserver, netstack, vdagent). Each BAR is moved onto 2 MiB slots inside ECAM at0x4010000000, and none of them answers (the issue's log, lines 212–233 and 326–343).The cause is the window list the loader declares. It concatenates two reads:
0x10000000..0x10100000and0x8000000000..0x8000100000, 1 MiB each, too small for a 2 MiB slot;loader/src/gcd.rs): flash0x4000000..0x8000000,0x10100000..0x3f000000(which takes in the PCI I/O window), ECAM0x4010000000..0x4020000000, and high MMIO.Only some of the second read are bridge windows. #869 was closed because excluding ECAM in the kernel fixes one reader of that false list and leaves the rest. The owner's ruling: aperture discovery does not go in the loader; AML stays in acpiserver; the redesign builds on #875's root walk; and a refused or partial declaration fails closed for the IOMMU as well as for placement.
What
_CRSsays onvirt(measured)I dumped the
virtDSDT that QEMU 11.1.1 / edk2 publishes (same machine options ascargo run, HVF), loaded it intoyos-amlon the host and evaluated\_SB.PCI0._CRS. No hardware access happened during the load or the evaluation. The root isPNP0A08(_CIDPNP0A03), segment 0, bus 0. Its resource list:0..=0xff;0x10000000..=0x3efeffff;0..=0xffff, translated to0x3eff0000;0x8000000000..=0xffffffffff.That excludes ECAM, flash and the I/O window, and the 64-bit window has room for all four functions.
toyos_acpi::memory_windowsreads only 64-bit entries today, so it must learn the 16- and 32-bit ones.Design
Ownership. acpiserver declares the root bridges' memory, and the kernel holds the declaration. The kernel's state is unknown, declared (checked whole, never replaced) or refused (by name). A PCI claim is handed over only under a declaration, and places BARs only inside its windows.
Stage A (this branch).
151046b1c.toyos_pci::aperture::checkrefuses a declaration as a whole if it is empty, has a window of no length or one past the address space, or has two windows sharing an address.pcidevholds the three states. A claim is refused before it reserves a slot or touches the function when nothing is declared, or the declaration was refused. The loader's list is still what gets declared, atpublish, so x86 is unchanged: the T14's recorded seven-window list is the test's accepted case, and q35's list is accepted (bar_map_againpasses).2c166146f. The hybrid IOMMU rule (kernel/src/iommu/window.rs,bounds).MGAW/SAGAW, or the SMMUv3 tables' 48-bit input; the CPU's is CPUID 8000_0008HEAX[7:0]orID_AA64MMFR0_EL1.PARange(newarch::cpu::physical_address_bits).pcidev's accepted declaration instead of the loader's raw list, and with no declaration held there is no domain (IommuError::Undeclared).aw=48 mgaw=39against windows reaching 2^39) stays on the old rule, so its domains are placed as before.virtand q35 move above the CPU's width.iommu/domains.rs):amdvi::initreserves its IVMDs only beside declared windows, asvtddoes its RMRRs. Its domains are 48 bits (mode-4 host tables, 48882 §2.2.3).pcidevhands the IOMMU atoyos_pci::aperture::Declaration, which onlycheckconstructs.iommu::initlogs the CPUs' physical width once, so every boot shows which rule applied.PARange0b0111 is 56 bits (FEAT_D128). It is mapped now instead of panicking.97a2253fe. The issue records the cause, that PCI apertures are a declaration the kernel holds, made from each root's _CRS (design; stage A: the declaration and the hybrid IOMMU rule) #916 has taken it up (status: assigned), and that stage B hands over the four claims.The AMD bench.
checkand is now a case in the host test.va=64 pa=48, but its domains are 48-bit mode-4 tables, so the width the rule compares is 48.5dabb22b5:iommu: the CPUs issue 48-bit physical addresses. So the bench takes the declared rule. Its domains run from 2^46 to 2^48 with no window above 2^46, as before (addresses from 0x400000000000 to 0x1000000000000), and a const assertion holds that case.iommu: the CPUs issue 39-bit physical addressesagainst itsmgaw=39unit, so it also takes the declared rule (0x2000000000to0x4000000000, as before).virttake the above-the-CPU rule (virt_smmu's domain at 2^40).Stage B.
_CRSon each PCI root The AML interpreter finished to ACPI 6.5:Loadand_OSC, the namespace initialised, every address space, every event, and the bounds stated #875's walk finds, keeps memory windows only, and declares them once through a new operation on its acpi claim. The kernel admits a declaration only from the unknown state and only from the acpi claim's holder.loader/src/gcd.rs,loader/src/rootbridge.rs,PciRootBridgeIo, andKernelArgs::root_bridge_windowswith its count._CRSmemory window with a non-zero_TRA(a bus address that differs from the CPU address) is refused by name. The declaration stays in CPU addresses, which the above-the-CPU premise rests on.IOMMU: why the hybrid rule
Option 2 (above the CPU's width in all cases) leaves the T14 no room. Its unit translates 39 bits (
iommu: domain2 root=0xa31000 aw=48 mgaw=39), and its declared windows reach0x8000000000= 2^39 (tests/checks/claims.rs:11-13). So the CPU issues at least 39-bit addresses, and a floor at 2^39 meets the unit's ceiling.The hybrid rule keeps option 2 wherever the unit has room, and falls back to the declared windows, failing closed, only where it does not. I chose not to take a firmware over-approximation for the IOMMU alone, because it keeps a loader read the ruling removes.
"Nothing the CPU reaches lies above its physical width" is the premise of the above-the-CPU placement: a root-bridge window is MMIO the CPU must address.
Roast
virtand the recorded T14 is always accepted. It rests oncheck's host test and on reading the code; stage B's wait gives it a guest test.virtdomain now starts at 2^40 instead of 2^46. Every device in those guests reaches it (4 x86 and 3 aarch64 guests pass).checkaccepting its loader list (measured to be accepted) and onbounds(see above).Checks
Evidence, all at
6f5f5e759(every file re-masked): metal round 2agents/arm-aperture/metal-r2/; round 1round2/at79c0224a9. Earlier:pr-916at516afa638(T14), and stage A at47f3a9be0. Paths are masked; the README lists every command, exit code and revision.main9842eeda5:cargo run -- --ci host01f59e28cmain5b48a3d17:cargo run -- --ci host5dabb22b5cargo test --test toyos-build -- bar_map_again lent_device_memory_refused usbd_drives_the_spare iommu_virtio_platform amdvi_selftest amdvi_firmware_left(q35: VT-d, and QEMU's amd-iommu)5dabb22b5cargo test --test toyos-build -- virt_smmu virt_claim_lpi virt_low_ecam(virt, HVF)5dabb22b5claim_reuses_its_remapping_entry,domain_ends_below_the_host_bridges,usbd_drives_the_type_c_controller5dabb22b50x2000000000..0x4000000000(judge, runner, readbacks)amdvi_translates_the_stick_controller5dabb22b50x400000000000..0x1000000000000(judge, runner, kernel log)5dabb22b5cargo run -- --ci host3f421a7a8cargo test --test toyos-build -- bar_map_again lent_device_memory_refused usbd_drives_the_spare iommu_virtio_platform(x86_64 q35, TCG)3f421a7a8cargo test --test toyos-build -- virt_smmu virt_claim_lpi virt_low_ecam(aarch64virt, HVF)3f421a7a8virt_smmu's domain write lands at0x10000000040(2^40) (log)virt_smmu3f421a7a8minusproduction.patch0x400000000040, the old 2^46 floor. The test judges translation, not placement, so the control shows the change's effect rather than a red (log)checkmutation-overlap.patcha_declaration_is_held_only_whole_and_disjointred (log)boundsmutation-undeclared.patchwindow.rs:183fails the kernel build (log)claim_reuses_its_remapping_entry,domain_ends_below_the_host_bridges,usbd_drives_the_type_c_controller3f421a7a8rc=0(judge, republished with its provenance, runner, readbacks). Every domain isaw=48 mgaw=39 addresses from 0x2000000000 to 0x4000000000, the old rule unchangedvirtDSDT_CRSprobe (host, a throwaway test deleted afterwards)abc2bfc15Not established yet: the ARM machine's four claims, which are stage B.
No metal exists for AArch64. The
cargo runaarch64 boot still refuses its claims at placement inside ECAM, as the issue records, until stage B.🤖 Generated with Claude Code
https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy