Files
kata-containers/src/dragonball/dbs_device
stevenhorsman e84d234721 doc: Update broken/slow URLs
Update the URLs to better/existing links

Signed-off-by: stevenhorsman <steven@uk.ibm.com>
2026-02-10 21:58:28 +01:00
..
2025-02-19 21:20:30 +08:00
2025-02-19 21:20:30 +08:00
2026-02-10 21:58:28 +01:00

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
  • 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() 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.

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.