Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Prerequisite for proper device manager. #4509

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 112 additions & 87 deletions src/vmm/src/builder.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,15 @@ use std::fmt::Debug;
use std::sync::{Arc, Mutex};

use kvm_ioctls::{IoEventAddress, VmFd};
use linux_loader::cmdline as kernel_cmdline;
use log::info;
use serde::{Deserialize, Serialize};
use utils::eventfd::EventFd;
use vm_allocator::{AddressAllocator, AllocPolicy, IdAllocator};

#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::DeviceInfoForFDT;
use crate::arch::DeviceType;
use crate::arch::DeviceType::Virtio;
#[cfg(target_arch = "aarch64")]
use crate::devices::legacy::RTCDevice;
use crate::devices::pseudo::BootTimer;
use crate::devices::virtio::balloon::Balloon;
use crate::devices::virtio::block::device::Block;
use crate::devices::virtio::device::VirtioDevice;
Expand All @@ -31,8 +28,9 @@ use crate::devices::virtio::rng::Entropy;
use crate::devices::virtio::vsock::TYPE_VSOCK;
use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG};
use crate::devices::BusDevice;
#[cfg(target_arch = "x86_64")]
use crate::vstate::memory::GuestAddress;

/// Persis logic for MMIODeviceManager.
pub mod persist;

/// Errors for MMIO device manager.
#[derive(Debug, thiserror::Error, displaydoc::Display)]
Expand All @@ -41,8 +39,6 @@ pub enum MmioError {
Allocator(vm_allocator::Error),
/// Failed to insert device on the bus: {0}
BusInsert(crate::devices::BusError),
/// Failed to allocate requested resourc: {0}
Cmdline(linux_loader::cmdline::Error),
/// Failed to find the device on the bus.
DeviceNotFound,
/// Invalid device type found on the MMIO bus.
Expand Down Expand Up @@ -74,6 +70,31 @@ pub struct MMIODeviceInfo {
pub irqs: Vec<u32>,
}

impl MMIODeviceInfo {
pub fn register_kvm_device(
&self,
vm: &VmFd,
mmio_device: &MmioTransport,
) -> Result<(), MmioError> {
let locked_device = mmio_device.locked_device();
for (i, queue_evt) in locked_device.queue_events().iter().enumerate() {
let io_addr = IoEventAddress::Mmio(
self.addr + u64::from(crate::devices::virtio::NOTIFY_REG_OFFSET),
);
vm.register_ioevent(queue_evt, &io_addr, u32::try_from(i).unwrap())
.map_err(MmioError::RegisterIoEvent)?;
}
vm.register_irqfd(locked_device.interrupt_evt(), self.irqs[0])
.map_err(MmioError::RegisterIrqFd)?;
Ok(())
}

pub fn register_kvm_irqfd(&self, vm: &VmFd, eventfd: &EventFd) -> Result<(), MmioError> {
vm.register_irqfd(eventfd, self.irqs[0])
.map_err(MmioError::RegisterIrqFd)
}
}

/// Manages the complexities of registering a MMIO device.
#[derive(Debug)]
pub struct MMIODeviceManager {
Expand All @@ -100,7 +121,7 @@ impl MMIODeviceManager {
}

/// Allocates resources for a new device to be added.
fn allocate_mmio_resources(&mut self, irq_count: u32) -> Result<MMIODeviceInfo, MmioError> {
pub fn allocate_device_info(&mut self, irq_count: u32) -> Result<MMIODeviceInfo, MmioError> {
let irqs = (0..irq_count)
.map(|_| self.irq_allocator.allocate_id())
.collect::<vm_allocator::Result<_>>()
Expand All @@ -117,178 +138,64 @@ impl MMIODeviceManager {
Ok(device_info)
}

/// Register a device at some MMIO address.
fn register_mmio_device(
&mut self,
identifier: (DeviceType, String),
device_info: MMIODeviceInfo,
device: Arc<Mutex<BusDevice>>,
) -> Result<(), MmioError> {
self.bus
.insert(device, device_info.addr, device_info.len)
.map_err(MmioError::BusInsert)?;
self.id_to_dev_info.insert(identifier, device_info);
Ok(())
}

/// Register a virtio-over-MMIO device to be used via MMIO transport at a specific slot.
pub fn register_mmio_virtio(
/// Add new virtio-over-MMIO device.
pub fn add_device(
&mut self,
vm: &VmFd,
device_id: String,
mmio_device: MmioTransport,
device_info: &MMIODeviceInfo,
) -> Result<(), MmioError> {
// Our virtio devices are currently hardcoded to use a single IRQ.
// Validate that requirement.
if device_info.irqs.len() != 1 {
return Err(MmioError::InvalidIrqConfig);
}
let identifier;
{
let locked_device = mmio_device.locked_device();
identifier = (DeviceType::Virtio(locked_device.device_type()), device_id);
for (i, queue_evt) in locked_device.queue_events().iter().enumerate() {
let io_addr = IoEventAddress::Mmio(
device_info.addr + u64::from(crate::devices::virtio::NOTIFY_REG_OFFSET),
);
vm.register_ioevent(queue_evt, &io_addr, u32::try_from(i).unwrap())
.map_err(MmioError::RegisterIoEvent)?;
}
vm.register_irqfd(locked_device.interrupt_evt(), device_info.irqs[0])
.map_err(MmioError::RegisterIrqFd)?;
}

self.register_mmio_device(
identifier,
device_info.clone(),
Arc::new(Mutex::new(BusDevice::MmioTransport(mmio_device))),
)
}

/// Append a registered virtio-over-MMIO device to the kernel cmdline.
#[cfg(target_arch = "x86_64")]
pub fn add_virtio_device_to_cmdline(
cmdline: &mut kernel_cmdline::Cmdline,
device_info: &MMIODeviceInfo,
) -> Result<(), MmioError> {
// as per doc, [virtio_mmio.]device=<size>@<baseaddr>:<irq> needs to be appended
// to kernel commandline for virtio mmio devices to get recognized
// the size parameter has to be transformed to KiB, so dividing hexadecimal value in
// bytes to 1024; further, the '{}' formatting rust construct will automatically
// transform it to decimal
cmdline
.add_virtio_mmio_device(
device_info.len,
GuestAddress(device_info.addr),
device_info.irqs[0],
None,
)
.map_err(MmioError::Cmdline)
}

/// Allocate slot and register an already created virtio-over-MMIO device. Also Adds the device
/// to the boot cmdline.
pub fn register_mmio_virtio_for_boot(
&mut self,
vm: &VmFd,
device_id: String,
mmio_device: MmioTransport,
_cmdline: &mut kernel_cmdline::Cmdline,
) -> Result<MMIODeviceInfo, MmioError> {
let device_info = self.allocate_mmio_resources(1)?;
self.register_mmio_virtio(vm, device_id, mmio_device, &device_info)?;
#[cfg(target_arch = "x86_64")]
Self::add_virtio_device_to_cmdline(_cmdline, &device_info)?;
let device_info = self.allocate_device_info(1)?;
self.add_device_with_info(vm, device_id, mmio_device, device_info.clone())?;
Ok(device_info)
}

#[cfg(target_arch = "aarch64")]
/// Register an early console at the specified MMIO configuration if given as parameter,
/// otherwise allocate a new MMIO resources for it.
pub fn register_mmio_serial(
/// Add new virtio-over-MMIO device with specified
/// device info.
pub fn add_device_with_info(
&mut self,
vm: &VmFd,
serial: Arc<Mutex<BusDevice>>,
device_info_opt: Option<MMIODeviceInfo>,
device_id: String,
mmio_device: MmioTransport,
device_info: MMIODeviceInfo,
) -> Result<(), MmioError> {
// Create a new MMIODeviceInfo object on boot path or unwrap the
// existing object on restore path.
let device_info = if let Some(device_info) = device_info_opt {
device_info
} else {
self.allocate_mmio_resources(1)?
let identifier = {
let locked_device = mmio_device.locked_device();
(DeviceType::Virtio(locked_device.device_type()), device_id)
};

vm.register_irqfd(
serial
.lock()
.expect("Poisoned lock")
.serial_ref()
.unwrap()
.serial
.interrupt_evt(),
device_info.irqs[0],
)
.map_err(MmioError::RegisterIrqFd)?;
device_info.register_kvm_device(vm, &mmio_device)?;

let identifier = (DeviceType::Serial, DeviceType::Serial.to_string());
// Register the newly created Serial object.
self.register_mmio_device(identifier, device_info, serial)
self.add_bus_device_with_info(
identifier,
Arc::new(Mutex::new(BusDevice::MmioTransport(mmio_device))),
device_info,
)
}

#[cfg(target_arch = "aarch64")]
/// Append the registered early console to the kernel cmdline.
pub fn add_mmio_serial_to_cmdline(
&self,
cmdline: &mut kernel_cmdline::Cmdline,
/// Add new MMIO device to the MMIO bus.
pub fn add_bus_device(
&mut self,
identifier: (DeviceType, String),
device: Arc<Mutex<BusDevice>>,
) -> Result<(), MmioError> {
let device_info = self
.id_to_dev_info
.get(&(DeviceType::Serial, DeviceType::Serial.to_string()))
.ok_or(MmioError::DeviceNotFound)?;
cmdline
.insert("earlycon", &format!("uart,mmio,0x{:08x}", device_info.addr))
.map_err(MmioError::Cmdline)
let device_info = self.allocate_device_info(1)?;
self.add_bus_device_with_info(identifier, device, device_info)
}

#[cfg(target_arch = "aarch64")]
/// Create and register a MMIO RTC device at the specified MMIO configuration if
/// given as parameter, otherwise allocate a new MMIO resources for it.
pub fn register_mmio_rtc(
/// Add new MMIO device to the MMIO bus with specified
/// device info.
pub fn add_bus_device_with_info(
&mut self,
rtc: RTCDevice,
device_info_opt: Option<MMIODeviceInfo>,
identifier: (DeviceType, String),
device: Arc<Mutex<BusDevice>>,
device_info: MMIODeviceInfo,
) -> Result<(), MmioError> {
// Create a new MMIODeviceInfo object on boot path or unwrap the
// existing object on restore path.
let device_info = if let Some(device_info) = device_info_opt {
device_info
} else {
self.allocate_mmio_resources(1)?
};

// Create a new identifier for the RTC device.
let identifier = (DeviceType::Rtc, DeviceType::Rtc.to_string());
// Attach the newly created RTC device.
self.register_mmio_device(
identifier,
device_info,
Arc::new(Mutex::new(BusDevice::RTCDevice(rtc))),
)
}

/// Register a boot timer device.
pub fn register_mmio_boot_timer(&mut self, device: BootTimer) -> Result<(), MmioError> {
// Attach a new boot timer device.
let device_info = self.allocate_mmio_resources(0)?;

let identifier = (DeviceType::BootTimer, DeviceType::BootTimer.to_string());
self.register_mmio_device(
identifier,
device_info,
Arc::new(Mutex::new(BusDevice::BootTimer(device))),
)
self.bus
.insert(device, device_info.addr, device_info.len)
.map_err(MmioError::BusInsert)?;
self.id_to_dev_info.insert(identifier, device_info);
Ok(())
}

/// Gets the information of the devices registered up to some point in time.
Expand Down Expand Up @@ -735,15 +642,15 @@ mod tests {
(crate::arch::IRQ_BASE, crate::arch::IRQ_MAX),
)
.unwrap();
let device_info = device_manager.allocate_mmio_resources(0).unwrap();
let device_info = device_manager.allocate_device_info(0).unwrap();
assert_eq!(device_info.irqs.len(), 0);
let device_info = device_manager.allocate_mmio_resources(1).unwrap();
let device_info = device_manager.allocate_device_info(1).unwrap();
assert_eq!(device_info.irqs[0], crate::arch::IRQ_BASE);
assert_eq!(
format!(
"{}",
device_manager
.allocate_mmio_resources(crate::arch::IRQ_MAX - crate::arch::IRQ_BASE + 1)
.allocate_device_info(crate::arch::IRQ_MAX - crate::arch::IRQ_BASE + 1)
.unwrap_err()
),
"Failed to allocate requested resource: The requested resource is not available."
Expand All @@ -755,14 +662,14 @@ mod tests {
}

let device_info = device_manager
.allocate_mmio_resources(crate::arch::IRQ_MAX - crate::arch::IRQ_BASE - 1)
.allocate_device_info(crate::arch::IRQ_MAX - crate::arch::IRQ_BASE - 1)
.unwrap();
assert_eq!(device_info.irqs[16], crate::arch::IRQ_BASE + 16);
assert_eq!(
format!("{}", device_manager.allocate_mmio_resources(2).unwrap_err()),
format!("{}", device_manager.allocate_device_info(2).unwrap_err()),
"Failed to allocate requested resource: The requested resource is not available."
.to_string()
);
device_manager.allocate_mmio_resources(0).unwrap();
device_manager.allocate_device_info(0).unwrap();
}
}
Loading
Loading