Skip to content

vfio: synthesize PASID capability for nested devices - #4375

Open
Henry Li (henryli001) wants to merge 1 commit into
microsoft:mainfrom
henryli001:lihl/vfio-synthetic-pasid-capability
Open

vfio: synthesize PASID capability for nested devices#4375
Henry Li (henryli001) wants to merge 1 commit into
microsoft:mainfrom
henryli001:lihl/vfio-synthetic-pasid-capability

Conversation

@henryli001

Copy link
Copy Markdown

Summary

Expose a synthetic PCIe PASID capability for nested IOMMUFD devices when Linux VFIO hides the physical capability from the guest.

Changes

  • Read each endpoint's maximum PASID width and execution/privileged support from IOMMU_GET_HW_INFO.
  • Keep per-device PASID capabilities separate from physical SMMU capabilities so PASID-capable GPUs and non-PASID devices can share a vSMMU.
  • Add PASID to the recognized PCIe extended capability IDs.
  • Append a synthetic PASID capability to the guest-visible extended capability chain when the endpoint supports PASID and VFIO does not expose an existing capability.
  • Emulate and reset the PASID control register while filtering unsupported control bits.
  • Treat the fabricated capability header as read-only so guest writes never reach physical config space.
  • Leave endpoints without PASID support unchanged.

This is the endpoint-capability portion split out of #4344. It is based directly on main and does not depend on that PR.

Validation

  • cargo +1.95.0 test -p vfio_assigned_device --lib (33 passed)
  • RUSTFLAGS='-D warnings' cargo +1.95.0 clippy -p vfio_assigned_device --all-targets --all-features
  • cargo xtask fmt --only-diffed
  • The combined endpoint and vSMMU behavior was validated with four GB200 GPUs in an Ubuntu 24.04 UEFI guest: CUDA initialization, 189471 MiB per GPU, and NVLink fabric all succeeded.

Read per-device PASID width and execution/privileged support from IOMMU_GET_HW_INFO, keep those capabilities separate from physical SMMU capabilities, and expose a synthetic PASID extended capability when VFIO hides the physical one.

Append the capability to the visible extended-capability chain, emulate and reset its control register, keep its fabricated header read-only, and leave devices without PASID support unchanged.
@henryli001
Henry Li (henryli001) requested a review from a team as a code owner September 3, 2026 06:05
Copilot AI lite review requested due to automatic review settings September 3, 2026 06:05
@github-actions github-actions Bot added the unsafe Related to unsafe code label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

⚠️ Unsafe Code Detected

This PR modifies files containing unsafe Rust code. Extra scrutiny is required during review.

For more on why we check whole files, instead of just diffs, check out the Rustonomicon

@henryli001

Copy link
Copy Markdown
Author

This is the endpoint half split from #4344. It is independently based on main; combined GB200 validation is documented in both PR descriptions. The prior review finding about synthetic header writes is addressed here by intercepting the header as a read-only no-op.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The synthetic PASID insertion logic can attach the new capability to an extended-capability header that this code also patches into an invalid/filtered state, which risks making the synthetic capability undiscoverable to guests.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds per-endpoint PASID discovery for nested (IOMMUFD-accelerated) VFIO devices and, when VFIO does not expose a physical PASID capability, synthesizes a guest-visible PCIe PASID extended capability (including control emulation/reset) so PASID-capable endpoints can function correctly behind a shared vSMMU.

Changes:

  • Extend IOMMU_GET_HW_INFO plumbing to return per-endpoint PASID parameters (max PASID width + exec/privileged support) and thread them through the VFIO assigned-device bring-up path.
  • Track PASID as a recognized PCIe extended capability ID and discover existing PASID capability presence.
  • Append and emulate a synthetic PASID extended capability (header patching + control register emulation) when the endpoint supports PASID and no PASID capability is already exposed.
File summaries
File Description
vm/devices/user_driver/vfio_sys/src/iommufd.rs Exposes additional IOMMU_GET_HW_INFO outputs (max PASID width + capability bits) via get_hw_info.
vm/devices/pci/vfio_assigned_device/src/iommufd_nesting.rs Returns both host SMMU caps and new per-device PASID caps derived from IOMMU_GET_HW_INFO.
vm/devices/pci/vfio_assigned_device/src/manager.rs Threads per-device PASID caps through nesting output alongside host SMMU caps.
vm/devices/pci/vfio_assigned_device/src/resolver.rs Constructs per-device PasidCapabilities and passes them into assigned-device construction.
vm/devices/pci/vfio_assigned_device/src/lib.rs Detects existing PASID extended capability, patches extended-cap chain, synthesizes PASID capability, and emulates PASID control/reset.
vm/devices/pci/pci_core/src/spec.rs Adds PASID to the PCIe extended capability ID enum.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 1328 to 1332
let cap_id = caps::ExtendedCapabilityId((header & 0xFFFF) as u16);
let cap_next = ((header >> 20) & 0xFFF) as u16;
result.last_ext_cap_offset = Some(offset);

tracing::debug!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems real?

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

value: u32,
}

const SYNTHETIC_PASID_OFFSET: u16 = 0xff8;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it feasible to find a free place in config space instead? While unlikely, I think a device could have something (or a config space patch) here, and this hardcoded approach doesn't scale very well

Comment on lines 1328 to 1332
let cap_id = caps::ExtendedCapabilityId((header & 0xFFFF) as u16);
let cap_next = ((header >> 20) & 0xFFF) as u16;
result.last_ext_cap_offset = Some(offset);

tracing::debug!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems real?

if self.capability & PASID_CAP_PRIV != 0 {
writable |= PASID_CAP_PRIV;
}
self.control = requested & writable;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super familiar with VFIO or iommufd, does this change end up enabling PASID in endpoint hardware / physical SMMU?

let af_flr_control_offset = caps.af_flr_control_offset;
let config_patches = caps.config_patches;
let mut config_patches = caps.config_patches;
let synthetic_pasid = synthesize_pasid_capability(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there's a reason we would want to disable this feature and present the config space without a synthesized capability? I'm trying to understand whether this should be configurable in the CLI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

unsafe Related to unsafe code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants