Repository navigation
machine-arm: pcie-root-port under the SMMUv3 with per-bus stream IDs - #336
Merged
Merged
Conversation
This was referenced Oct 14, 2026
tamnd
added a commit
that referenced
this pull request
Oct 14, 2026
Part of #9. vfio-pci now maps the BARs of the device into the guest, like `vfio_region_mmap()` in QEMU. Reads and writes to a mapped BAR go to the device memory without a pread or pwrite per access. While INTx is pending the mmaps are off, so the guest's next BAR access traps and serves as the EOI, and a timer on the virtual clock turns them back on once INTx is quiet, as `vfio_intx_mmap_enable()` does. What is in it: - ruvm-mem: a RAM device region type for host device memory, made with `memory_region_init_ram_device_ptr()`, that the vfio listener maps for DMA like RAM. - ruvm-sys: the region info capabilities (sparse mmap areas and `MSIX_MAPPABLE`), `VFIO_DEVICE_FEATURE_DMA_BUF`, and `HostMemory::map_device()`, which reserves the whole region and maps each area into it. - vfio-pci: the sparse mmap areas as `vfio_setup_region_sparse_mmaps()` reads them, with size 0 areas dropped, the areas sorted, and QEMU's error for overlapping areas. Each area becomes a "NAME BAR n mmaps[i]" subregion of the trapped BAR, then the dma-buf probe runs with QEMU's two warnings. A failed mmap prints "Failed to mmap NAME BAR n. Performance may be slow" and the BAR stays trapped. Without `MSIX_MAPPABLE` the pages of the MSI-X table are cut out of the mmaps, as `vfio_pci_fixup_msix_region()` does. INTx turns the mmaps off and arms the timer, the timer rearms while INTx is pending, and turning INTx off deletes the timer and turns the mmaps on. The container maps the RAM device sections for DMA, and a failure there is a warning with QEMU's peer to peer hint instead of an error. - system: `x-no-mmap` and `x-intx-mmap-timeout-ms` now do what they do in QEMU, and the virtual clock is passed to realize for the timer. Where it differs from QEMU: - Under TCG, ruvm reads and writes a RAM device region through its IO callbacks, not through the TLB. There are no KVM, HVF or WHPX slots for it yet. - `vfio_sub_page_bar_update_mapping()` is not ported, so a BAR smaller than a host page stays trapped. - The page sizes for the DMA map check come from the host page size, as `VFIO_IOMMU_GET_INFO` is not ported. - `x-msix-relocation` is not ported. Checked: On server2, on 9596e67, the branch with main up to #336 merged in: - clippy with `-D warnings` and `--all-targets` for ruvm-mem, ruvm-hw-iommu, ruvm-sys, ruvm-hw-pci, ruvm-hw-vfio, ruvm-machine-x86, ruvm-system and ruvm-machine-arm. - clippy for x86_64-pc-windows-gnu and aarch64-apple-darwin on the same crates. - tests of ruvm-mem, ruvm-hw-iommu, ruvm-sys, ruvm-hw-pci and ruvm-hw-vfio, and the ruvm-machine-x86, ruvm-system and ruvm-machine-arm libs. New tests cover the sparse mmap areas, the MSI-X table cut and the INTx timer on a manual clock. - xtask unsafe-audit, provenance, layers and style. The same checks without ruvm-machine-arm, the release build and the L1 compare below ran on 7b2159e, the commit before that merge. The merge brings in #335 and #336, which touch only Arm files. - An L1 compare on the 7b2159e release build. L1 is QEMU 11.1 with `intel-iommu,caching-mode=on` and an e1000e at 00:04.0 bound to vfio-pci. L2 is run with `-device vfio-pci,host=0000:00:04.0,addr=2` under qtest, as QEMU 11.1 and ruvm, each with and without `x-no-mmap=on`, and then with `x-intx-mmap-timeout-ms=0`. Each batch waits for all of its answers, and the syscall counts are the syscr deltas of the L2 process from /proc. Part A, with `-S`, where the virtual clock does not move. qtest places the BARs, reads and writes BAR0, BAR1, the IO BAR and the MSI-X BAR, sends a frame by DMA, programs the IOAPIC and raises INTx. | Step | QEMU 11.1 | ruvm | QEMU x-no-mmap | ruvm x-no-mmap | | --- | --- | --- | --- | --- | | mmaps of the device fd | BAR0 0x20000, BAR1 0x20000, BAR3 0x4000 | same | none | none | | 300 STATUS reads | 10 | 5 | 310 | 305 | | 300 STATUS reads after INTx | 310 | 305 | 310 | 305 | All 71 answers are the same in the four runs. Both print "VFIO dma-buf not supported in kernel, using mmap fallback, P2P DMA will not work", as the L1 kernel has no dma-buf for vfio. ruvm prints it without QEMU's `-device vfio-pci,host=0000:00:04.0,addr=2:` prefix. The MSI-X BAR is mapped whole in both, since the kernel reports `MSIX_MAPPABLE`. Part B, in a guest that runs, so the timer can fire. SeaBIOS places BAR0 at 0xfeba0000 in all four runs. qtest raises INTx, waits 3 seconds, does one STATUS read, then reads ICR, masks the interrupt and does 300 reads, waits 3 seconds and does 300 reads more. | Step | QEMU 11.1 | ruvm | QEMU timeout 0 | ruvm timeout 0 | | --- | --- | --- | --- | --- | | 300 reads before INTx | 16 | 6 | 16 | 6 | | one read 3 s after INTx | 5, trapped | 3, trapped | 4, trapped | 3, trapped | | ICR and 300 reads | 319 | 307 | 318 | 307 | | 300 reads 3 s later | 14 | 5 | 314 | 305 | The INTx step itself is two writes at 4, 2, 3 and 2, so the one read adds a pread in every run. ICR reads 0x80000001 in all four. The mmaps are off while INTx is pending, back on after the timeout, and stay off with a timeout of 0, in QEMU and in ruvm. How many of the 300 reads right after the EOI trap depends on when the next timer tick lands, and was between 74 and 319 over the debug runs for both.
This was referenced Oct 14, 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.
What
-device pcie-root-porton Arm virt, with PCI functions behind it under the SMMUv3, as QEMU 11.1 does it.smmu_get_sid()does withPCI_BUILD_BDF(pci_bus_num(bus), devfn). Before this the stream ID was fixed when the function was plugged.SmmuV3::device_ops_withtakes the stream ID as a closure;device_ops(sid)still works and calls it.pci_init_bus_master()gives it one in QEMU.iort_host_bridges()withpci_bus_range()).virt_acpi_build_update(), the ACPI tables, loader and RSDP are built again on the first fw_cfg read after each reset, so the IORT has the bus numbers the firmware gave the root ports. A reset clears the flag, asvirt_acpi_build_reset()does.build_pci_bridge_aml()).-device pcie-root-porttakesid,bus=pcie.0,addr,chassis,slot,port,hotplugandmultifunction. PCI types takebus=<root port id>. The other QEMU properties of the type fail with "is not supported by ruvm yet".The shared notifier API is not touched. The only change in hw-iommu is
device_ops_within smmuv3.rs.Comparison with QEMU 11.1
Alpine linux-virt 6.12.110 with
-M virt,iommu=smmuv3,gic-version=3 -cpu max -m 1G -smp 1 -device pcie-root-port,id=rp1,chassis=1 -device virtio-blk-pci,bus=rp1,drive=d0,disable-legacy=on,iommu_platform=on, booted both with-kernel Image(device tree) and with edk2 and-kernel vmlinuz-virt(ACPI). The initramfs prints the SMMU and IOMMU dmesg lines, the PCI functions with their IOMMU groups, the IORT, the md5 of the 16 MiB disk, then writes a sector, drops the caches, reads it back, and prints the md5 again.Results, ruvm against QEMU 11.1:
ias 44-bit, oas 44-bit (features 0x00088705), 262144 cmdq entries, 131072 evtq entries./sys/firmware/acpi/tables/IORTis the same 0xec bytes on both, with the root complex mapping IDs 0 to 0x1ff (buses 0 and 1) to the SMMU.3cf64d06...before, the sector written through the SMMU reads back, and the md5 after isb6fb6854...on both, in the guest and on the host image.A first ruvm run with
-smp 2stalled inmulti_cpu_stopwith soft lockups after the SMMU probe, on a server with a load of about 25 on 6 cores. With-smp 1the same point took about 45 s and then went on. I did not check whether main does the same.Differences from QEMU, not done here
pcie-root-portwithout anidfails. QEMU names its buspcie.<n>with an n that depends on the other buses, so nothing can be plugged behind it by name anyway.x-speed,acpi-indexand so on), fail with "not supported by ruvm yet".pci-bridgeandpcie-pci-bridgeare not ported, so the requester ID aliasing ofpci_device_get_iommu_bus_devfn()is not needed yet.pcie-root-portyet.accel=on, CMDQV) needs host SMMU hardware we do not have, so it comes last. Notifiers and replay will build on the IOMMU notifier API from hw-vfio, intel-iommu: DMA through the vIOMMU for vfio-pci #330.Tests
smmu_stream_id_behind_a_root_port: with the stream table entry of stream 0 bypassing and the entry of 0x100 aborting, the MSI of a function plugged while its bus was 0 is dropped once the guest numbers the bus 1, and gets through with the SMMU off.iort_behind_a_root_port: the IORT maps 0x200 IDs to the SMMU with the bus numbered 1. Read through fw_cfg it keeps the numbers of its first read until the next reset, then has 0x400 for buses 1 to 3. The DSDT hasS00insideS10.root_port_and_virtio_iommu, and planner tests for the properties and errors.cargo test -p ruvm-hw-iommu -p ruvm-machine-arm -p ruvm-system -p ruvm-hw-pci -p ruvm-firmware -p ruvm-mem,cargo test -p ruvm-system --lib, the linux-user x86 suite, clippy-D warnings,cargo check --workspace --all-targets, the xtasks and the Windows check all pass.