2 unstable releases
0.2.0 | Oct 24, 2022 |
---|---|
0.1.0 | Mar 7, 2022 |
#1161 in Hardware support
21 downloads per month
Used in 5 crates
(4 directly)
65KB
1K
SLoC
dbs-device
The dbs-device
crate, as a counterpart of vm-device, defines device model for the Dragonball Secure Sandbox.
The dbs-device
crate shares some common concepts and data structures with vm-device, but it also diverges from
vm-device due to different VMM designs.
The dbs-device crate provides:
- DeviceIo and DeviceIoMut: trait for device to handle trapped MMIO/PIO access requests.
- IoManager: IO manager to handle trapped MMIO/PIO access requests.
- IoManagerContext: trait for IO manager context object to support device hotplug at runtime.
- ResourceConstraint, Resource and DeviceResources: resource allocation requirements and constraints.
Design
The dbs-device crate is designed to support the virtual machine's device model.
The core concepts of device model are Port I/O and Memory-mapped I/O, which are two main methods of performing I/O between CPU and devices.
The device model provided by the dbs-device crate works as below:
- The VMM creates a global resource manager, device manager and IO manager.
- The device manager creates virtual devices configured by the VMM
- create device object
- query device allocation requirements and constraints, the device returns an array of ResourceConstraint.
- allocate resources for device from the resource manager, resource manager returns a DeviceResources object.
- assign the allocated resources to the device.
- The device manager register devices to the IO manager.
- query trapped address ranges by calling DeviceIo::get_trapped_io_resources()
- register the device to the IO manager with trapped address range
- The guest access those trapped MMIO/PIO address ranges, and triggers VM IO Exit events to trap into the VMM.
- The VMM parses the VM exit events and dispatch those events to the IO manager.
- The IO manager looks up device by searching trapped address ranges, and call the device's DeviceIO handler to process those trapped MMIO/PIO access requests.
Usage
First, a vm needs to create an IoManager to help it dispatch I/O events to devices. And an IoManager has two types of bus, the PIO bus and the MMIO bus, to handle different types of IO.
Then, when creating a device, it needs to implement the DeviceIo or DeviceIoMut trait to receive read or write events send by driver in guest OS:
read()
andwrite()
methods is used to deal with MMIO eventspio_read()
andpio_write()
methods is used to deal with PIO eventsget_assigned_resources()
method is used to get all resources assigned to the deviceget_trapped_io_resources()
method is used to get only MMIO/PIO resources assigned to the device
The difference of DeviceIo and DeviceIoMut is the reference type of self
passed to method:
- DeviceIo trait would pass a immutable reference
&self
to method, so the implementation of device would provide interior mutability and thread-safe protection itself - DeviceIoMut trait would pass a mutable reference
&mut self
to method, and it can give mutability to device which is wrapped byMutex
directly to simplify the difficulty of achieving interior mutability.
Additionally, the DeviceIo trait has an auto implement for Mutex<T: DeviceIoMut>
Last, the device needs to be added to IoManager by using register_device_io()
, and the function would add device
to PIO bus and/or MMIO bus by the resources it have. If a device has not only MMIO resource but PIO resource,
it would be added to both pio bus and mmio bus. So the device would wrapped by Arc<T>
.
From now on, the IoManager will dispatch I/O requests for the registered address ranges to the device.
Examples
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);
License
This project is licensed under Apache License, Version 2.0.
Dependencies
~245–700KB
~16K SLoC