Update the URLs to better/existing links Signed-off-by: stevenhorsman <steven@uk.ibm.com>
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 Memory-mapped I/O and port-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
- query trapped address ranges by calling [
- 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&selfto method, so the implementation of device would provide interior mutability and thread-safe protection itself - [
DeviceIoMut] trait would pass a mutable reference&mut selfto method, and it can give mutability to device which is wrapped byMutexdirectly 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.