- All legacy devices are
BusDevice
s. - A virtio MMIO or PCI device are
BusDevices
s. TheBusDevice
trait is used for BAR, config space. - A virtio MMIO or PCI device encapsulate an actual virtio device (net, block, etc)
pub trait BusDevice: Send {
/// Reads at `offset` from this device
fn read(&mut self, offset: u64, data: &mut [u8]) {}
/// Writes at `offset` into this device
fn write(&mut self, offset: u64, data: &[u8]) {}
/// Triggers the `irq_mask` interrupt on this device
fn interrupt(&self, irq_mask: u32) {}
}
pub struct Bus {
devices: BTreeMap<BusRange, Arc<Mutex<BusDevice>>>,
}
/// Implements the
/// [MMIO](http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-1090002)
/// transport for virtio devices.
///
/// This requires 3 points of installation to work with a VM:
///
/// 1. Mmio reads and writes must be sent to this device at what is referred to here as MMIO base.
/// 1. `Mmio::queue_evts` must be installed at `virtio::NOTIFY_REG_OFFSET` offset from the MMIO
/// base. Each event in the array must be signaled if the index is written at that offset.
/// 1. `Mmio::interrupt_evt` must signal an interrupt that the guest driver is listening to when it
/// is written to.
///
/// Typically one page (4096 bytes) of MMIO address space is sufficient to handle this transport
/// and inner virtio device.
pub struct MmioDevice {
device: Box<VirtioDevice>,
device_activated: bool,
features_select: u32,
acked_features_select: u32,
queue_select: u32,
interrupt_status: Arc<AtomicUsize>,
interrupt_evt: Option<EventFd>,
driver_status: u32,
config_generation: u32,
queues: Vec<Queue>,
queue_evts: Vec<EventFd>,
mem: Option<GuestMemory>,
}
All MmioDevice
s:
- Include a
VirtioDevice
- Are
BusDevice
s:
impl BusDevice for MmioDevice {
[...]
}
/// Trait for virtio devices to be driven by a virtio transport.
///
/// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the
/// device. Once the guest driver has configured the device, `VirtioDevice::activate` will be called
/// and all the events, memory, and queues for device operation will be moved into the device.
/// Optionally, a virtio device can implement device reset in which it returns said resources and
/// resets its internal.
pub trait VirtioDevice: Send {
/// The virtio device type.
fn device_type(&self) -> u32;
/// The maximum size of each queue that this device supports.
fn queue_max_sizes(&self) -> &[u16];
/// The set of feature bits shifted by `page * 32`.
fn features(&self, page: u32) -> u32 {
let _ = page;
0
}
/// Acknowledges that this set of features should be enabled.
fn ack_features(&mut self, page: u32, value: u32);
/// Reads this device configuration space at `offset`.
fn read_config(&self, offset: u64, data: &mut [u8]);
/// Writes to this device configuration space at `offset`.
fn write_config(&mut self, offset: u64, data: &[u8]);
/// Activates this device for real usage.
fn activate(
&mut self,
mem: GuestMemory,
interrupt_evt: EventFd,
status: Arc<AtomicUsize>,
queues: Vec<Queue>,
queue_evts: Vec<EventFd>,
) -> ActivateResult;
/// Optionally deactivates this device and returns ownership of the guest memory map, interrupt
/// event, and queue events.
fn reset(&mut self) -> Option<(EventFd, Vec<EventFd>)> {
None
}
}
/// Manages the complexities of registering a MMIO device.
pub struct MMIODeviceManager {
pub bus: devices::Bus,
pub vm_requests: Vec<VmRequest>,
guest_mem: GuestMemory,
mmio_base: u64,
irq: u32,
id_to_addr_map: HashMap<String, u64>,
}
impl MMIODeviceManager {
/// Create a new DeviceManager handling mmio devices (virtio net, block).
pub fn new(guest_mem: GuestMemory, mmio_base: u64) -> MMIODeviceManager {
MMIODeviceManager {
guest_mem,
vm_requests: Vec::new(),
mmio_base,
irq: IRQ_BASE,
bus: devices::Bus::new(),
id_to_addr_map: HashMap::new(),
}
}
/// Register a device to be used via MMIO transport.
pub fn register_device(
&mut self,
device: Box<devices::virtio::VirtioDevice>,
cmdline: &mut kernel_cmdline::Cmdline,
id: Option<String>,
) -> Result<u64> {
[...]
let mmio_device = devices::virtio::MmioDevice::new(self.guest_mem.clone(), device)
.map_err(Error::CreateMmioDevice)?;
[...]
self.bus
.insert(Arc::new(Mutex::new(mmio_device)), self.mmio_base, MMIO_LEN)
.map_err(Error::BusError)?;
}
}