2 个不稳定版本
0.2.0 | 2022 年 10 月 24 日 |
---|---|
0.1.0 | 2022 年 3 月 7 日 |
#974 在 硬件支持
52 每月下载量
在 5 个crates中使用 (4 直接)
65KB
1K SLoC
dbs-device
作为 vm-device 的对应物,`dbs-device` crate 定义了 Dragonball 安全沙盒的设备模型。`dbs-device` crate 与 vm-device 共享一些共同的概念和数据结构,但由于 VMM 设计的不同,它也不同于 vm-device。
`dbs-device` crate 提供:
- DeviceIo 和 DeviceIoMut:用于处理捕获的 MMIO/PIO 访问请求的 trait。
- IoManager:用于处理捕获的 MMIO/PIO 访问请求的 IO 管理器。
- IoManagerContext:用于 IO 管理器上下文对象的 trait,以支持运行时设备热插拔。
- ResourceConstraint,Resource 和 DeviceResources:资源分配要求和约束。
设计
`dbs-device` crate 设计用于支持虚拟机的设备模型。
设备模型的核心概念是 端口 I/O 和 内存映射 I/O,这是 CPU 和设备之间进行 I/O 的两种主要方法。
`dbs-device` crate 提供的设备模型工作如下:
- VMM 创建一个全局资源管理器、设备管理器和 IO 管理器。
- 设备管理器创建由 VMM 配置的虚拟设备
- 创建设备对象
- 查询设备分配要求和约束,设备返回一个 ResourceConstraint 数组。
- 从资源管理器为设备分配资源,资源管理器返回一个 DeviceResources 对象。
- 将分配的资源分配给设备。
- 设备管理器将设备注册到IO管理器。
- 通过调用 DeviceIo::get_trapped_io_resources() 查询捕获的地址范围
- 使用捕获的地址范围将设备注册到IO管理器
- 虚拟机访问那些捕获的MMIO/PIO地址范围,并触发VM IO退出事件进入VMM。
- VMM解析VM退出事件并将这些事件调度到IO管理器。
- IO管理器通过搜索捕获的地址范围查找设备,并调用设备的 DeviceIO 处理器来处理这些捕获的MMIO/PIO访问请求。
使用方法
首先,一个虚拟机需要创建一个 IoManager 来帮助它将I/O事件调度到设备。一个 IoManager 有两种类型的总线,即PIO总线和解密总线,以处理不同类型的I/O。
然后,在创建设备时,需要实现 DeviceIo 或 DeviceIoMut 特性,以接收来自虚拟机操作系统中驱动程序发送的读取或写入事件
read()
和write()
方法用于处理MMIO事件pio_read()
和pio_write()
方法用于处理PIO事件get_assigned_resources()
方法用于获取分配给设备的所有资源get_trapped_io_resources()
方法用于获取分配给设备的仅MMIO/PIO资源
DeviceIo 和 DeviceIoMut 的区别在于传递给方法的 self
的引用类型
- DeviceIo 特性会将不可变引用
&self
传递给方法,因此设备实现将提供内部可变性和线程安全保护 - DeviceIoMut 特性会将可变引用
&mut self
传递给方法,并且它可以直接给被Mutex
包裹的设备提供可变性,以简化实现内部可变性的难度。
此外,DeviceIo 特性具有为 Mutex<T: DeviceIoMut>
的自动实现
最后,需要使用register_device_io()
函数将设备添加到IoManager,该函数会根据所拥有的资源将设备添加到PIO总线或MMIO总线。如果一个设备既有MMIO资源又有PIO资源,它将被添加到pio总线和mmio总线。因此,设备将被Arc<T>
封装。
从现在起,IoManager将为已注册的地址范围向设备派遣I/O请求。
示例
use std::sync::Arc;
use dbs_device::device_manager::IoManager;
use dbs_device::resources::{DeviceResources, Resource};
use dbs_device::{DeviceIo, IoAddress, PioAddress};
struct DummyDevice {}
impl DeviceIo for DummyDevice {
fn read(&self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {
println!(
"mmio read, base: 0x{:x}, offset: 0x{:x}",
base.raw_value(),
offset.raw_value()
);
}
fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]) {
println!(
"mmio write, base: 0x{:x}, offset: 0x{:x}",
base.raw_value(),
offset.raw_value()
);
}
fn pio_read(&self, base: PioAddress, offset: PioAddress, data: &mut [u8]) {
println!(
"pio read, base: 0x{:x}, offset: 0x{:x}",
base.raw_value(),
offset.raw_value()
);
}
fn pio_write(&self, base: PioAddress, offset: PioAddress, data: &[u8]) {
println!(
"pio write, base: 0x{:x}, offset: 0x{:x}",
base.raw_value(),
offset.raw_value()
);
}
}
// Allocate resources for device
let mut resources = DeviceResources::new();
resources.append(Resource::MmioAddressRange {
base: 0,
size: 4096,
});
resources.append(Resource::PioAddressRange { base: 0, size: 32 });
// Register device to `IoManager` with resources
let device = Arc::new(DummyDevice {});
let mut manager = IoManager::new();
manager.register_device_io(device, &resources).unwrap();
// Dispatch I/O event from `IoManager` to device
manager.mmio_write(0, &vec![0, 1]).unwrap();
let mut buffer = vec![0; 4];
manager.pio_read(0, &mut buffer);
许可证
本项目采用Apache License, Version 2.0授权。
依赖项
~300–760KB
~18K SLoC