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:
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::gettrappedio_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.
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()
and write()
methods is used to deal with MMIO events
- pio_read()
and pio_write()
methods is used to deal with PIO events
- get_assigned_resources()
method is used to get all resources assigned to the device
- get_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 by Mutex
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.
```rust use std::sync::Arc;
use dbsdevice::devicemanager::IoManager; use dbsdevice::resources::{DeviceResources, Resource}; use dbsdevice::{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.rawvalue(), offset.rawvalue() ); }
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.registerdeviceio(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); ```
This project is licensed under Apache License, Version 2.0.