vmm: Use virtio-blk to support booting from disk image

After the virtio-blk device support has been introduced in the
previous commit, the vmm need to rely on this new device to boot
from disk images instead of initrd built into the kernel.

In order to achieve the proper support of virtio-blk, this commit
had to handle a few things:

  - Register an ioevent fd for each virtqueue. This important to be
    notified from the virtio driver that something has been written
    on the queue.

  - Fix the retrieval of 64bits BAR address. This is needed to provide
    the right address which need to be registered as the notification
    address from the virtio driver.

  - Fix the write_bar and read_bar functions. They were both assuming
    to be provided with an address, from which they were trying to
    find the associated offset. But the reality is that the offset is
    directly provided by the Bus layer.

  - Register a new virtio-blk device as a virtio-pci device from the
    vm.rs code. When the VM is started, it expects a block device to
    be created, using this block device as the VM rootfs.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf
2019-05-06 10:27:40 -07:00
committed by Samuel Ortiz
parent 65f96e408f
commit b67e0b3dad
7 changed files with 327 additions and 22 deletions

View File

@@ -17,11 +17,13 @@ use std::fmt;
use std::fs::File;
use std::io;
mod block;
mod device;
mod queue;
pub mod transport;
pub use self::block::*;
pub use self::device::*;
pub use self::queue::*;
@@ -123,4 +125,7 @@ pub enum Error {
event: DeviceEventT,
},
IoError(io::Error),
EpollCreateFd(io::Error),
EpollCtl(io::Error),
EpollWait(io::Error),
}

View File

@@ -218,6 +218,18 @@ impl VirtioPciDevice {
})
}
/// Gets the list of queue events that must be triggered whenever the VM writes to
/// `virtio::NOTIFY_REG_OFFSET` past the MMIO base. Each event must be triggered when the
/// value being written equals the index of the event in this list.
pub fn queue_evts(&self) -> &[EventFd] {
self.queue_evts.as_slice()
}
/// Gets the event this device uses to interrupt the VM when the used queue is changed.
pub fn interrupt_evt(&self) -> Option<&EventFd> {
self.interrupt_evt.as_ref()
}
fn is_driver_ready(&self) -> bool {
let ready_bits =
(DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_DRIVER_OK | DEVICE_FEATURES_OK) as u8;
@@ -305,6 +317,24 @@ impl PciDevice for VirtioPciDevice {
&mut self.configuration
}
fn ioeventfds(&self) -> Vec<(&EventFd, u64, u64)> {
let bar0 = self
.configuration
.get_bar64_addr(self.settings_bar as usize);
let notify_base = bar0 + NOTIFICATION_BAR_OFFSET;
self.queue_evts()
.iter()
.enumerate()
.map(|(i, event)| {
(
event,
notify_base + i as u64 * u64::from(NOTIFY_OFF_MULTIPLIER),
i as u64,
)
})
.collect()
}
fn allocate_bars(
&mut self,
allocator: &mut SystemAllocator,
@@ -326,8 +356,9 @@ impl PciDevice for VirtioPciDevice {
})? as u8;
println!(
"VIRTIO PCI BAR starts at 0x{:x}",
virtio_pci_bar_addr.raw_value()
"VIRTIO PCI BAR starts at 0x{:x}, size 0x{:x}",
virtio_pci_bar_addr.raw_value(),
CAPABILITY_BAR_SIZE
);
ranges.push((virtio_pci_bar_addr, CAPABILITY_BAR_SIZE));
@@ -349,10 +380,7 @@ impl PciDevice for VirtioPciDevice {
Ok(ranges)
}
fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
// The driver is only allowed to do aligned, properly sized access.
let bar0 = u64::from(self.configuration.get_bar_addr(self.settings_bar as usize));
let offset = addr - bar0;
fn read_bar(&mut self, offset: u64, data: &mut [u8]) {
match offset {
o if o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE => self.common_config.read(
o - COMMON_CONFIG_BAR_OFFSET,
@@ -380,9 +408,7 @@ impl PciDevice for VirtioPciDevice {
}
}
fn write_bar(&mut self, addr: u64, data: &[u8]) {
let bar0 = u64::from(self.configuration.get_bar_addr(self.settings_bar as usize));
let offset = addr - bar0;
fn write_bar(&mut self, offset: u64, data: &[u8]) {
match offset {
o if o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE => self.common_config.write(
o - COMMON_CONFIG_BAR_OFFSET,