Rust虚拟化开发库vfio-bindings的使用:高效绑定VFIO接口实现PCI设备直通与硬件虚拟化
完整示例代码
下面是一个完整的vfio-bindings使用示例,展示了从初始化到PCI设备直通的完整流程:
use vfio_bindings::bindings::vfio;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::mem;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 初始化VFIO容器
let container = File::open("/dev/vfio/vfio")?;
// 检查API版本
let api_version = unsafe { vfio::vfio_get_api_version(container.as_raw_fd()) };
if api_version != vfio::VFIO_API_VERSION {
eprintln!("不支持的VFIO API版本: {}", api_version);
return Ok(());
}
// 2. 打开IOMMU组
let group_num = 1; // 实际使用中需要替换为正确的组号
let group_path = format!("/dev/vfio/{}", group_num);
let group = File::open(&group_path)?;
// 检查组状态
let mut group_status = vfio::vfio_group_status {
argsz: mem::size_of::<vfio::vfio_group_status>() as u32,
flags: 0,
};
unsafe { vfio::vfio_get_group_status(group.as_raw_fd(), &mut group_status) }?;
if !group_status.flags.contains(vfio::VfioGroupFlags::VFIO_GROUP_FLAGS_VIABLE) {
eprintln!("IOMMU组不可用");
return Ok(());
}
// 3. 将组添加到容器并设置IOMMU
unsafe { vfio::vfio_group_set_container(group.as_raw_fd(), container.as_raw_fd()) }?;
unsafe { vfio::vfio_set_iommu(container.as_raw_fd(), vfio::VfioContainerType::VFIO_TYPE1_IOMMU) }?;
// 4. 打开设备
let device = unsafe { vfio::vfio_group_get_device_fd(group.as_raw_fd()) }?;
// 5. 获取设备信息
let mut device_info = vfio::vfio_device_info {
argsz: mem::size_of::<vfio::vfio_device_info>() as u32,
flags: 0,
num_regions: 0,
num_irqs: 0,
};
unsafe { vfio::vfio_get_device_info(device, &mut device_info) }?;
println!("发现设备: {}个区域, {}个IRQ", device_info.num_regions, device_info.num_irqs);
// 6. 获取PCI设备信息
let mut pci_info = vfio::vfio_pci_device_info {
argsz: mem::size_of::<vfio::vfio_pci_device_info>() as u32,
flags: 0,
region_info: [0; vfio::VFIO_PCI_NUM_REGIONS],
irq_info: [0; vfio::VFIO_PCI_NUM_IRQS],
config_space: [0; vfio::VFIO_PCI_CONFIG_SPACE_SIZE],
};
unsafe { vfio::vfio_get_pci_device_info(device, &mut pci_info) }?;
// 打印PCI设备信息
println!("Vendor ID: {:04x}", u16::from_le_bytes([pci_info.config_space[0], pci_info.config_space[1]]));
println!("Device ID: {:04x}", u16::from_le_bytes([pci_info.config_space[2], pci_info.config_space[3]]));
// 7. 映射BAR0区域
let region_index = 0;
let mut region_info = vfio::vfio_region_info {
argsz: mem::size_of::<vfio::vfio_region_info>() as u32,
flags: 0,
index: region_index,
cap_offset: 0,
size: 0,
offset: 0,
};
unsafe { vfio::vfio_get_region_info(device, &mut region_info) }?;
let bar0_ptr = unsafe {
libc::mmap(
ptr::null_mut(),
region_info.size as usize,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
device,
region_info.offset as libc::off_t,
)
};
if bar0_ptr == libc::MAP_FAILED {
return Err("映射BAR0失败".into());
}
println!("BAR0映射到: {:p}, 大小: {}字节", bar0_ptr, region_info.size);
// 8. 设置DMA映射
let mut dma_map = vfio::vfio_iommu_type1_dma_map {
argsz: mem::size_of::<vfio::vfio_iommu_type1_dma_map>() as u32,
flags: vfio::VfioDmaMapFlags::VFIO_DMA_MAP_FLAG_READ | vfio::VfioDmaMapFlags::VFIO_DMA_MAP_FLAG_WRITE,
vaddr: 0x1000 as u64,
iova: 0x1000,
size: 4096,
};
unsafe { vfio::vfio_dma_map(container.as_raw_fd(), &dma_map) }?;
// 9. 设置中断
let mut irq_set = vfio::vfio_irq_set {
argsz: mem::size_of::<vfio::vfio_irq_set>() as u32,
flags: vfio::VfioIrqSetFlags::VFIO_IRQ_SET_DATA_EVENTFD |
vfio::VfioIrqSetFlags::VFIO_IRQ_SET_ACTION_TRIGGER,
index: vfio::VfioIrqType::VFIO_PCI_MSIX_IRQ_INDEX,
start: 0,
count: 1,
};
let event_fd = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) };
if event_fd == -1 {
return Err("创建eventfd失败".into());
}
irq_set.data = event_fd as u64;
unsafe { vfio::vfio_set_irqs(device, &irq_set) }?;
println!("中断设置完成,event_fd: {}", event_fd);
// 10. 清理资源
unsafe { libc::munmap(bar0_ptr, region_info.size as usize) };
unsafe { libc::close(event_fd) };
Ok(())
}
代码说明
-
初始化流程:
- 打开VFIO容器文件(/dev/vfio/vfio)
- 验证API版本兼容性
- 打开IOMMU组文件(/dev/vfio/[group_num])
- 检查IOMMU组状态
-
设备设置:
- 将IOMMU组添加到容器
- 设置IOMMU类型(通常使用VFIO_TYPE1_IOMMU)
- 获取设备文件描述符
-
PCI设备操作:
- 获取PCI设备配置信息
- 读取Vendor ID和Device ID
- 映射设备的BAR0区域
-
高级功能:
- 设置DMA映射
- 配置中断处理(使用eventfd)
-
资源清理:
使用建议
- 在生产环境中,应该添加更完善的错误处理和资源清理逻辑
- 考虑使用RAII模式管理资源(如文件描述符、内存映射等)
- 对于性能敏感的应用,可以预分配DMA缓冲区
- 中断处理应该放在单独的线程中,使用epoll或类似机制监控eventfd
这个完整示例展示了vfio-bindings库的主要功能,可以作为开发PCI设备直通应用的起点。根据具体需求,可以在此基础上进行扩展和完善。