tests: Add VFIO mmap BAR exclusion coverage

Exercise the new VFIO BAR exclusion option with NVIDIA
passthrough tests so the integration suite checks that selected
BARs are skipped.

The tests cover both legacy VFIO and iommufd paths while
preserving the existing hardware availability guards.

Signed-off-by: Damian Barabonkov <dbctl@pm.me>
Assisted-by: OpenCode:gpt-5.5
This commit is contained in:
Damian Barabonkov
2026-05-06 08:18:17 +02:00
committed by Rob Bradford
parent a858a1f115
commit 4eb1717fb0

View File

@@ -10643,7 +10643,90 @@ mod windows {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
mod vfio { mod vfio {
use crate::*; use crate::*;
const NVIDIA_VFIO_DEVICE: &str = "/sys/bus/pci/devices/0002:00:01.0"; const NVIDIA_VFIO_DEVICE: &str = "/sys/bus/pci/devices/0002:00:01.0";
const IORESOURCE_MEM: u64 = 0x0000_0200;
const IORESOURCE_PREFETCH: u64 = 0x0000_2000;
fn nvidia_vfio_device_ready() -> bool {
if !std::path::Path::new(NVIDIA_VFIO_DEVICE).exists() {
println!("SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} not found");
return false;
}
let driver_path = format!("{NVIDIA_VFIO_DEVICE}/driver");
if let Ok(driver) = std::fs::read_link(&driver_path) {
let driver_name = driver.file_name().unwrap_or_default().to_string_lossy();
if driver_name != "vfio-pci" {
println!(
"SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} bound to {driver_name}, not vfio-pci"
);
return false;
}
} else {
println!("SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} not bound to any driver");
return false;
}
true
}
fn largest_nvidia_prefetchable_memory_bar() -> Option<u8> {
let resource_path = format!("{NVIDIA_VFIO_DEVICE}/resource");
let resource = match std::fs::read_to_string(&resource_path) {
Ok(resource) => resource,
Err(e) => {
println!("SKIPPED: failed to read {resource_path}: {e}");
return None;
}
};
let mut selected_bar = None;
let mut selected_size = 0;
for (index, line) in resource.lines().take(6).enumerate() {
let mut fields = line.split_whitespace();
let Some(start) = fields.next() else {
continue;
};
let Some(end) = fields.next() else {
continue;
};
let Some(flags) = fields.next() else {
continue;
};
let parse_hex = |value: &str| u64::from_str_radix(value.trim_start_matches("0x"), 16);
let Ok(start) = parse_hex(start) else {
continue;
};
let Ok(end) = parse_hex(end) else {
continue;
};
let Ok(flags) = parse_hex(flags) else {
continue;
};
if flags & IORESOURCE_MEM == 0 || end < start || (start == 0 && end == 0) {
continue;
}
if flags & IORESOURCE_PREFETCH == 0 {
continue;
}
let size = end - start + 1;
if size > selected_size {
selected_bar = Some(index as u8);
selected_size = size;
}
}
if selected_bar.is_none() {
println!(
"SKIPPED: no non-empty prefetchable memory BAR found for {NVIDIA_VFIO_DEVICE}"
);
}
selected_bar
}
fn platform_cfg(iommufd: bool) -> String { fn platform_cfg(iommufd: bool) -> String {
if iommufd { if iommufd {
@@ -10878,25 +10961,66 @@ mod vfio {
test_nvidia_card_iommu_address_width_common(true); test_nvidia_card_iommu_address_width_common(true);
} }
fn test_nvidia_guest_numa_generic_initiator_common(iommufd: bool) { fn test_nvidia_card_x_exclude_mmap_bars_common(iommufd: bool) {
// Skip test if VFIO device is not available or not ready if !nvidia_vfio_device_ready() {
if !std::path::Path::new(NVIDIA_VFIO_DEVICE).exists() {
println!("SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} not found");
return; return;
} }
// Check if device is bound to vfio-pci driver let Some(bar) = largest_nvidia_prefetchable_memory_bar() else {
let driver_path = format!("{NVIDIA_VFIO_DEVICE}/driver"); return;
if let Ok(driver) = std::fs::read_link(&driver_path) { };
let driver_name = driver.file_name().unwrap_or_default().to_string_lossy();
if driver_name != "vfio-pci" { let disk_config = UbuntuDiskConfig::new(JAMMY_VFIO_IMAGE_NAME.to_string());
println!( let guest = Guest::new(Box::new(disk_config));
"SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} bound to {driver_name}, not vfio-pci"
); let mut child = GuestCommand::new(&guest)
return; .args(["--cpus", "boot=4"])
} .args(["--memory", "size=1G"])
} else { .args(["--kernel", fw_path(FwType::RustHypervisorFirmware).as_str()])
println!("SKIPPED: VFIO device {NVIDIA_VFIO_DEVICE} not bound to any driver"); .args(["--platform", &platform_cfg(iommufd)])
.args([
"--device",
format!("path={NVIDIA_VFIO_DEVICE},x_exclude_mmap_bars=[{bar}]").as_str(),
])
.default_disks()
.default_net()
.capture_output()
.spawn()
.unwrap();
let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot().unwrap();
assert!(wait_until(Duration::from_secs(10), || guest.check_nvidia_gpu()));
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("Skipping VFIO BAR mmap"),
"Expected x_exclude_mmap_bars log in stderr: {stderr}"
);
assert!(
stderr.contains(format!("BAR {bar}").as_str()),
"Expected skipped BAR index in stderr: {stderr}"
);
handle_child_output(r, &output);
}
#[test]
fn test_nvidia_card_x_exclude_mmap_bars() {
test_nvidia_card_x_exclude_mmap_bars_common(false);
}
#[test]
fn test_iommufd_nvidia_card_x_exclude_mmap_bars() {
test_nvidia_card_x_exclude_mmap_bars_common(true);
}
fn test_nvidia_guest_numa_generic_initiator_common(iommufd: bool) {
if !nvidia_vfio_device_ready() {
return; return;
} }