1 个不稳定版本

0.1.0 2021年12月20日

#346硬件支持

37 每月下载量

Apache-2.0 OR BSD-3-Clause

71KB
1K SLoC

vm-device

vm-device 包提供

  • 定义在特定总线上的读和写操作的设备特性
  • 设备管理器(特定总线的特性和具体实现),用于操作设备和调度I/O
  • 定义资源和其约束的抽象(例如特定总线的地址范围、中断号等)

设计

虚拟设备模型围绕四个特性构建,分别是用于程序化I/O(PIO)的DevicePioMutDevicePio,以及用于内存映射I/O(MMIO)的DeviceMmioMutDeviceMmio》。这些特性定义了处理读和写操作的方法。不同之处在于,DevicePioDeviceMmio只需要不可变self借用,而MutDevicePioMutDeviceMmio需要可变借用。

设备管理器抽象由IoManager结构实现。它定义了两个总线,一个用于PIO,一个用于MMIO。对于每个总线,借助PioManagerMmioManager特性,管理器提供设备注册以及调度读和写请求的方法。管理器将根据访问的地址范围确定哪个设备负责处理I/O请求,并将请求路由到该设备。

PioManagerMmioManager特性作为接口有用,原因如下。首先,当提供的IoManager不足时,允许进行替代实现。其次,允许其他crate依赖于特性而不依赖于实际实现。

以下图表展示了使用IoManager的虚拟机内核、宿主内核和VMM之间的交互。虚拟机内核中的驱动程序发出一个I/O请求。该请求由宿主内核的虚拟机管理程序(KVM)转换为VMM处理的触发器。触发器可以是vCPU退出或eventfd通知。VMM提取地址信息并确定目标总线。然后,它向IoManager派发新的请求,该请求检查请求的地址是否已在总线上注册了虚拟设备,并最终将该请求发送到该设备。vm-device

用法

设备通常连接到特定的总线,因此需要实现一种类型的特制。例如,x86上的串行端口是PIO设备,而VirtIO设备使用MMIO。设备也可以同时实现这两种。一旦确定了I/O类型,下一步就是选择可变和不可变特制变体。如果需要修改设备内部状态,则需要使用可变变体。

在将任何I/O请求调度到新设备之前,它需要在该总线指定的地址范围内注册到IoManager实例中。通过调用IoManager::new()(无需任何配置)来创建一个新的IoManager很容易。内部管理器将设备作为特制对象存储在Arc的包装中,因此如果设备实现MutDevicePioMutDeviceMmio,则它必须包装在Mutex中。该包包含自动实现DevicePio for Mutex<T> where T: MutDevicePioDeviceMmio for Mutex<T> where T: MutDeviceMmio,但仅限于标准库中的Mutex类型。对于任何其他第三方包中的Mutex类型,通用实现必须由用户完成。

从现在起,IoManager将路由已注册地址范围到设备的I/O请求。请求由客户端代码调度,例如在处理VM退出时,使用IoManager的方法pio_readpio_writemmio_readmmio_write

示例

实现简单的日志PIO设备

use std::sync::{Arc, Mutex};
use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
use vm_device::device_manager::{IoManager, PioManager};
use vm_device::MutDevicePio;

struct LogDevice {}

impl MutDevicePio for LogDevice {
    fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, _data: &mut [u8]) {
        println!("mut pio_read: base {:?}, offset {}", base, offset);
    }

    fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {
        println!(
            "mut pio_write: base {:?}, offset {}, data {:?}",
            base, offset, data
        );
    }
}

将设备注册到IoManager并执行I/O

fn main() {
    let mut manager = IoManager::new();
    let device = LogDevice {};
    let bus_range = PioRange::new(PioAddress(0), 10).unwrap();
    manager
        .register_pio(bus_range, Arc::new(Mutex::new(device)))
        .unwrap();
    manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap();
}

测试

使用单元测试和集成测试测试vm-device。它利用rust-vmm-ci进行持续测试。所有测试都在rustvmm/dev容器中运行。

许可证

此项目受以下任一许可证的许可:

无运行时依赖项