Dry-run validation that [`prepare_pci_group_for_passthrough`] would accept this slice of BDFs as a complete IOMMU group right now. Mirrors the structural checks of the prepare call without performing any kernel-state-mutating operations. Intended for `ValidateSandboxCreate`-style pre-flight paths. Rejects: - empty slices (`EmptyGroup`), - malformed BDFs (`InvalidBdf`), - duplicate entries (`Inva
(
sysfs: &SysfsRoot,
bdfs: &[&str],
)
| 368 | /// Unlike [`validate_pci_for_passthrough`], this function does not consult |
| 369 | /// the current binding state of any device; it is purely structural. |
| 370 | pub fn validate_pci_group_for_passthrough( |
| 371 | sysfs: &SysfsRoot, |
| 372 | bdfs: &[&str], |
| 373 | ) -> Result<(), VfioError> { |
| 374 | let (primary, companions) = bdfs.split_first().ok_or(VfioError::EmptyGroup)?; |
| 375 | |
| 376 | for bdf in bdfs { |
| 377 | validate_bdf(bdf)?; |
| 378 | if !sysfs.pci_device_ref(bdf).exists() { |
| 379 | return Err(VfioError::DeviceNotFound { |
| 380 | bdf: (*bdf).to_string(), |
| 381 | }); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | let declared: BTreeSet<&str> = bdfs.iter().copied().collect(); |
| 386 | if declared.len() != bdfs.len() { |
| 387 | // Duplicate entries would make the IOMMU equality check pass while |
| 388 | // double-binding the same device. |
| 389 | return Err(VfioError::InvalidBdf { |
| 390 | bdf: format!("duplicate entries in PCI group: {bdfs:?}"), |
| 391 | }); |
| 392 | } |
| 393 | |
| 394 | let expected_group = sysfs.pci_device_ref(primary).iommu_group()?; |
| 395 | for bdf in companions { |
| 396 | let g = sysfs.pci_device_ref(bdf).iommu_group()?; |
| 397 | if g != expected_group { |
| 398 | return Err(VfioError::GroupMismatch { |
| 399 | bdf: (*bdf).to_string(), |
| 400 | expected_group, |
| 401 | actual_group: g, |
| 402 | }); |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | let kernel_peers = sysfs.iommu_group_devices(expected_group)?; |
| 407 | let undeclared: Vec<String> = kernel_peers |
| 408 | .iter() |
| 409 | .filter(|p| !declared.contains(p.as_str())) |
| 410 | .cloned() |
| 411 | .collect(); |
| 412 | if !undeclared.is_empty() { |
| 413 | return Err(VfioError::IommuGroupConflict { |
| 414 | bdf: (*primary).to_string(), |
| 415 | group: expected_group, |
| 416 | peers: undeclared, |
| 417 | }); |
| 418 | } |
| 419 | |
| 420 | Ok(()) |
| 421 | } |
| 422 | |
| 423 | /// Atomically bind every `PCIe` device in a shared IOMMU group to `vfio-pci`. |
| 424 | /// |