Rust虚拟化开发库vfio-bindings的使用:高效绑定VFIO接口实现PCI设备直通与硬件虚拟化

Rust虚拟化开发库vfio-bindings的使用:高效绑定VFIO接口实现PCI设备直通与硬件虚拟化

设计

vfio-bindings crate设计为使用bindgen生成的VFIO Rust FFI绑定。

通过Rust的"features"支持多个Linux版本。对于每个支持的Linux版本,都会引入一个功能。

当前支持的功能/ Linux版本:

  • vfio-v5_0_0 包含Linux内核版本5.0的绑定

使用方法

首先,在您的Cargo.toml中添加以下内容:

vfio-bindings = "0.3"

接下来,将此添加到您的crate根目录:

extern crate vfio_bindings;

默认情况下,vfio-bindings将导出它支持的最新可用内核版本的包装,但您可以通过在Cargo.toml中指定来选择不同的版本:

vfio-bindings = { version = "0.3", features = ["vfio-v5_0_0"]}

示例代码

要使用这些绑定,您可以这样做:

use vfio_bindings::bindings::vfio::*;

完整示例DEMO

以下是一个使用vfio-bindings实现PCI设备直通的完整示例:

// 导入vfio绑定
use vfio_bindings::bindings::vfio::*;
use std::os::unix::io::AsRawFd;
use std::fs::File;

fn main() -> std::io::Result<()> {
    // 打开VFIO设备
    let vfio_dev = File::open("/dev/vfio/vfio")?;
    
    // 检查VFIO API版本
    let version = unsafe {
        let mut ver = 0;
        ioctl(vfio_dev.as_raw_fd(), VFIO_GET_API_VERSION, &mut ver)?;
        ver
    };
    
    if version != VFIO_API_VERSION {
        eprintln!("VFIO API版本不匹配");
        return Ok(());
    }
    
    // 获取VFIO容器
    let container = unsafe {
        let mut container_fd = 0;
        ioctl(vfio_dev.as_raw_fd(), VFIO_GET_CONTAINER, &mut container_fd)?;
        File::from_raw_fd(container_fd)
    };
    
    // 设置IOMMU类型 (这里使用Type1)
    unsafe {
        ioctl(container.as_raw_fd(), VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU)?;
    }
    
    // 打开PCI设备
    let pci_dev = File::open("/sys/bus/pci/devices/0000:00:01.0/")?;
    
    // 将PCI设备绑定到VFIO
    unsafe {
        ioctl(pci_dev.as_raw_fd(), VFIO_GROUP_GET_DEVICE_FD, &mut container.as_raw_fd())?;
    }
    
    // 现在可以使用VFIO接口进行PCI设备直通操作...
    
    Ok(())
}

许可证

此代码根据Apache-2.0或BSD-3-Clause许可证授权。


1 回复

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(())
}

代码说明

  1. 初始化流程

    • 打开VFIO容器文件(/dev/vfio/vfio)
    • 验证API版本兼容性
    • 打开IOMMU组文件(/dev/vfio/[group_num])
    • 检查IOMMU组状态
  2. 设备设置

    • 将IOMMU组添加到容器
    • 设置IOMMU类型(通常使用VFIO_TYPE1_IOMMU)
    • 获取设备文件描述符
  3. PCI设备操作

    • 获取PCI设备配置信息
    • 读取Vendor ID和Device ID
    • 映射设备的BAR0区域
  4. 高级功能

    • 设置DMA映射
    • 配置中断处理(使用eventfd)
  5. 资源清理

    • 解除内存映射
    • 关闭文件描述符

使用建议

  1. 在生产环境中,应该添加更完善的错误处理和资源清理逻辑
  2. 考虑使用RAII模式管理资源(如文件描述符、内存映射等)
  3. 对于性能敏感的应用,可以预分配DMA缓冲区
  4. 中断处理应该放在单独的线程中,使用epoll或类似机制监控eventfd

这个完整示例展示了vfio-bindings库的主要功能,可以作为开发PCI设备直通应用的起点。根据具体需求,可以在此基础上进行扩展和完善。

回到顶部