diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index b6b3fad97e8..b3584f97e0c 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -10,7 +10,6 @@ use std::io::{self, Seek, SeekFrom}; use std::sync::{Arc, Mutex}; use event_manager::{MutEventSubscriber, SubscriberOps}; -use libc::EFD_NONBLOCK; use linux_loader::cmdline::Cmdline as LoaderKernelCmdline; #[cfg(target_arch = "x86_64")] use linux_loader::loader::elf::Elf as Loader; @@ -25,9 +24,8 @@ use utils::u64_to_usize; use vm_memory::ReadVolatile; #[cfg(target_arch = "aarch64")] use vm_superio::Rtc; -use vm_superio::Serial; -use crate::arch::InitrdConfig; +use crate::arch::{DeviceType, InitrdConfig}; #[cfg(target_arch = "aarch64")] use crate::construct_kvm_mpidrs; use crate::cpu_config::templates::{ @@ -36,12 +34,13 @@ use crate::cpu_config::templates::{ }; #[cfg(target_arch = "x86_64")] use crate::device_manager::legacy::PortIODeviceManager; +use crate::device_manager::mmio::persist::MMIODevManagerConstructorArgs; use crate::device_manager::mmio::MMIODeviceManager; -use crate::device_manager::persist::MMIODevManagerConstructorArgs; -use crate::devices::legacy::serial::SerialOut; +use crate::device_manager::DeviceManager; #[cfg(target_arch = "aarch64")] use crate::devices::legacy::RTCDevice; -use crate::devices::legacy::{EventFdTrigger, SerialEventsWrapper, SerialWrapper}; +use crate::devices::legacy::SerialDevice; +use crate::devices::pseudo::BootTimer; use crate::devices::virtio::balloon::Balloon; use crate::devices::virtio::block::device::Block; use crate::devices::virtio::device::VirtioDevice; @@ -53,7 +52,6 @@ use crate::devices::BusDevice; use crate::logger::{debug, error}; use crate::persist::{MicrovmState, MicrovmStateError}; use crate::resources::VmResources; -use crate::snapshot::Persist; use crate::vmm_config::boot_source::BootConfig; use crate::vmm_config::instance_info::InstanceInfo; use crate::vmm_config::machine_config::{VmConfig, VmConfigError}; @@ -147,16 +145,6 @@ fn create_vmm_and_vcpus( .map_err(VmmError::EventFd) .map_err(Internal)?; - // Instantiate the MMIO device manager. - // 'mmio_base' address has to be an address which is protected by the kernel - // and is architectural specific. - let mmio_device_manager = MMIODeviceManager::new( - crate::arch::MMIO_MEM_START, - crate::arch::MMIO_MEM_SIZE, - (crate::arch::IRQ_BASE, crate::arch::IRQ_MAX), - ) - .map_err(StartMicrovmError::RegisterMmioDevice)?; - // For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS` // while on aarch64 we need to do it the other way around. #[cfg(target_arch = "x86_64")] @@ -168,8 +156,11 @@ fn create_vmm_and_vcpus( set_stdout_nonblocking(); // Serial device setup. - let serial_device = - setup_serial_device(event_manager, std::io::stdin(), io::stdout()).map_err(Internal)?; + let serial = SerialDevice::new() + .map_err(VmmError::EventFd) + .map_err(Internal)?; + let serial = Arc::new(Mutex::new(BusDevice::Serial(serial))); + event_manager.add_subscriber(serial.clone()); // x86_64 uses the i8042 reset event as the Vmm exit event. let reset_evt = vcpus_exit_evt @@ -180,7 +171,7 @@ fn create_vmm_and_vcpus( // create pio dev manager with legacy devices let pio_device_manager = { // TODO Remove these unwraps. - let mut pio_dev_mgr = PortIODeviceManager::new(serial_device, reset_evt).unwrap(); + let mut pio_dev_mgr = PortIODeviceManager::new(serial, reset_evt).unwrap(); pio_dev_mgr.register_devices(vm.fd()).unwrap(); pio_dev_mgr }; @@ -199,6 +190,17 @@ fn create_vmm_and_vcpus( vcpus }; + let device_manager = DeviceManager { + mmio_devices: MMIODeviceManager::new( + crate::arch::MMIO_MEM_START, + crate::arch::MMIO_MEM_SIZE, + (crate::arch::IRQ_BASE, crate::arch::IRQ_MAX), + ) + .map_err(StartMicrovmError::RegisterMmioDevice)?, + #[cfg(target_arch = "x86_64")] + pio_diveces: pio_device_manager, + }; + let vmm = Vmm { events_observer: Some(std::io::stdin()), instance_info: instance_info.clone(), @@ -208,9 +210,7 @@ fn create_vmm_and_vcpus( uffd, vcpus_handles: Vec::new(), vcpus_exit_evt, - mmio_device_manager, - #[cfg(target_arch = "x86_64")] - pio_device_manager, + device_manager, }; Ok((vmm, vcpus)) @@ -501,9 +501,9 @@ pub fn build_microvm_from_snapshot( instance_id: &instance_info.id, }; - vmm.mmio_device_manager = - MMIODeviceManager::restore(mmio_ctor_args, µvm_state.device_states) - .map_err(MicrovmStateError::RestoreDevices)?; + vmm.device_manager + .restore(mmio_ctor_args, µvm_state.device_states) + .map_err(MicrovmStateError::RestoreDevices)?; vmm.emulate_serial_init()?; // Move vcpus to their own threads and start their state machine in the 'Paused' state. @@ -639,29 +639,6 @@ pub fn setup_interrupt_controller(vm: &mut Vm, vcpu_count: u8) -> Result<(), Sta .map_err(StartMicrovmError::Internal) } -/// Sets up the serial device. -pub fn setup_serial_device( - event_manager: &mut EventManager, - input: std::io::Stdin, - out: std::io::Stdout, -) -> Result>, VmmError> { - let interrupt_evt = EventFdTrigger::new(EventFd::new(EFD_NONBLOCK).map_err(VmmError::EventFd)?); - let kick_stdin_read_evt = - EventFdTrigger::new(EventFd::new(EFD_NONBLOCK).map_err(VmmError::EventFd)?); - let serial = Arc::new(Mutex::new(BusDevice::Serial(SerialWrapper { - serial: Serial::with_events( - interrupt_evt, - SerialEventsWrapper { - buffer_ready_event_fd: Some(kick_stdin_read_evt), - }, - SerialOut::Stdout(out), - ), - input: Some(input), - }))); - event_manager.add_subscriber(serial.clone()); - Ok(serial) -} - #[cfg(target_arch = "aarch64")] fn attach_legacy_devices_aarch64( event_manager: &mut EventManager, @@ -679,20 +656,39 @@ fn attach_legacy_devices_aarch64( if cmdline_contains_console { // Make stdout non-blocking. set_stdout_nonblocking(); - let serial = setup_serial_device(event_manager, std::io::stdin(), std::io::stdout())?; - vmm.mmio_device_manager - .register_mmio_serial(vmm.vm.fd(), serial, None) + + let serial = SerialDevice::new().map_err(VmmError::EventFd)?; + + let device_info = vmm + .device_manager + .mmio_devices + .allocate_device_info(1) .map_err(VmmError::RegisterMMIODevice)?; - vmm.mmio_device_manager - .add_mmio_serial_to_cmdline(cmdline) + device_info + .register_kvm_irqfd(vmm.vm.fd(), serial.serial.interrupt_evt()) .map_err(VmmError::RegisterMMIODevice)?; + + let serial = Arc::new(Mutex::new(BusDevice::Serial(serial))); + event_manager.add_subscriber(serial.clone()); + + let identifier = (DeviceType::Serial, DeviceType::Serial.to_string()); + vmm.device_manager + .mmio_devices + .add_bus_device_with_info(identifier, serial, device_info.clone()) + .map_err(VmmError::RegisterMMIODevice)?; + + cmdline + .insert("earlycon", &format!("uart,mmio,0x{:08x}", device_info.addr)) + .expect("All args are valid"); } - let rtc = RTCDevice(Rtc::with_events( - &crate::devices::legacy::rtc_pl031::METRICS, - )); - vmm.mmio_device_manager - .register_mmio_rtc(rtc, None) + let identifier = (DeviceType::Rtc, DeviceType::Rtc.to_string()); + let rtc = Arc::new(Mutex::new(BusDevice::RTCDevice(RTCDevice( + Rtc::with_events(&crate::devices::legacy::rtc_pl031::METRICS), + )))); + vmm.device_manager + .mmio_devices + .add_bus_device(identifier, rtc) .map_err(VmmError::RegisterMMIODevice) } @@ -802,7 +798,7 @@ pub fn configure_system_for_boot( &vmm.guest_memory, cmdline, vcpu_mpidr, - vmm.mmio_device_manager.get_device_info(), + vmm.device_manager.mmio_devices.get_device_info(), vmm.vm.get_irqchip(), initrd, ) @@ -817,7 +813,7 @@ fn attach_virtio_device( vmm: &mut Vmm, id: String, device: Arc>, - cmdline: &mut LoaderKernelCmdline, + _cmdline: &mut LoaderKernelCmdline, is_vhost_user: bool, ) -> Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -826,10 +822,22 @@ fn attach_virtio_device( // The device mutex mustn't be locked here otherwise it will deadlock. let device = MmioTransport::new(vmm.guest_memory().clone(), device, is_vhost_user); - vmm.mmio_device_manager - .register_mmio_virtio_for_boot(vmm.vm.fd(), id, device, cmdline) - .map_err(RegisterMmioDevice) - .map(|_| ()) + let _device_info = vmm + .device_manager + .mmio_devices + .add_device(vmm.vm.fd(), id, device) + .map_err(RegisterMmioDevice)?; + + #[cfg(target_arch = "x86_64")] + _cmdline + .add_virtio_mmio_device( + _device_info.len, + GuestAddress(_device_info.addr), + _device_info.irqs[0], + None, + ) + .expect("MMIO device len is 0x1000"); + Ok(()) } pub(crate) fn attach_boot_timer_device( @@ -838,13 +846,13 @@ pub(crate) fn attach_boot_timer_device( ) -> Result<(), StartMicrovmError> { use self::StartMicrovmError::*; - let boot_timer = crate::devices::pseudo::BootTimer::new(request_ts); - - vmm.mmio_device_manager - .register_mmio_boot_timer(boot_timer) - .map_err(RegisterMmioDevice)?; + let identifier = (DeviceType::BootTimer, DeviceType::BootTimer.to_string()); + let boot_timer = Arc::new(Mutex::new(BusDevice::BootTimer(BootTimer::new(request_ts)))); - Ok(()) + vmm.device_manager + .mmio_devices + .add_bus_device(identifier, boot_timer) + .map_err(RegisterMmioDevice) } fn attach_entropy_device( @@ -1078,6 +1086,12 @@ pub mod tests { setup_interrupt_controller(&mut vm, 1).unwrap(); } + let device_manager = DeviceManager { + mmio_devices: default_mmio_device_manager(), + #[cfg(target_arch = "x86_64")] + pio_diveces: pio_device_manager, + }; + Vmm { events_observer: Some(std::io::stdin()), instance_info: InstanceInfo::default(), @@ -1087,9 +1101,7 @@ pub mod tests { uffd: None, vcpus_handles: Vec::new(), vcpus_exit_evt, - mmio_device_manager, - #[cfg(target_arch = "x86_64")] - pio_device_manager, + device_manager, } } @@ -1185,7 +1197,8 @@ pub mod tests { attach_unixsock_vsock_device(vmm, cmdline, &vsock, event_manager).unwrap(); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_VSOCK), &vsock_dev_id) .is_some()); } @@ -1202,7 +1215,8 @@ pub mod tests { attach_entropy_device(vmm, cmdline, &entropy, event_manager).unwrap(); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_RNG), ENTROPY_DEV_ID) .is_some()); } @@ -1220,7 +1234,8 @@ pub mod tests { attach_balloon_device(vmm, cmdline, balloon, event_manager).unwrap(); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BALLOON), BALLOON_DEV_ID) .is_some()); } @@ -1348,7 +1363,8 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda ro")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1368,7 +1384,8 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 rw")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1389,7 +1406,8 @@ pub mod tests { assert!(!cmdline_contains(&cmdline, "root=PARTUUID=")); assert!(!cmdline_contains(&cmdline, "root=/dev/vda")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1425,15 +1443,18 @@ pub mod tests { assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 rw")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), "root") .is_some()); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), "secondary") .is_some()); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), "third") .is_some()); @@ -1461,7 +1482,8 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda rw")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1481,7 +1503,8 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 ro")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1501,7 +1524,8 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda rw")); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) .is_some()); } @@ -1515,7 +1539,8 @@ pub mod tests { let res = attach_boot_timer_device(&mut vmm, request_ts); res.unwrap(); assert!(vmm - .mmio_device_manager + .device_manager + .mmio_devices .get_device(DeviceType::BootTimer, &DeviceType::BootTimer.to_string()) .is_some()); } diff --git a/src/vmm/src/device_manager/mmio.rs b/src/vmm/src/device_manager/mmio/mod.rs similarity index 77% rename from src/vmm/src/device_manager/mmio.rs rename to src/vmm/src/device_manager/mmio/mod.rs index d0f10011bf7..2c807e24db0 100644 --- a/src/vmm/src/device_manager/mmio.rs +++ b/src/vmm/src/device_manager/mmio/mod.rs @@ -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; @@ -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)] @@ -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. @@ -74,6 +70,31 @@ pub struct MMIODeviceInfo { pub irqs: Vec, } +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 { @@ -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 { + pub fn allocate_device_info(&mut self, irq_count: u32) -> Result { let irqs = (0..irq_count) .map(|_| self.irq_allocator.allocate_id()) .collect::>() @@ -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>, - ) -> 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=@: 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 { - 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>, - device_info_opt: Option, + 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>, ) -> 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, + identifier: (DeviceType, String), + device: Arc>, + 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. @@ -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." @@ -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(); } } diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/mmio/persist.rs similarity index 93% rename from src/vmm/src/device_manager/persist.rs rename to src/vmm/src/device_manager/mmio/persist.rs index 094c4bf14a8..8ba7782b4c9 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/mmio/persist.rs @@ -12,9 +12,11 @@ use log::{error, warn}; use serde::{Deserialize, Serialize}; use vm_allocator::AllocPolicy; -use super::mmio::*; #[cfg(target_arch = "aarch64")] use crate::arch::DeviceType; +use crate::device_manager::mmio::*; +#[cfg(target_arch = "aarch64")] +use crate::devices::legacy::SerialDevice; use crate::devices::virtio::balloon::persist::{BalloonConstructorArgs, BalloonState}; use crate::devices::virtio::balloon::{Balloon, BalloonError}; use crate::devices::virtio::block::device::Block; @@ -38,6 +40,8 @@ use crate::devices::virtio::vsock::{ Vsock, VsockError, VsockUnixBackend, VsockUnixBackendError, TYPE_VSOCK, }; use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG}; +#[cfg(target_arch = "aarch64")] +use crate::devices::BusDevice; use crate::mmds::data_store::MmdsVersion; use crate::resources::{ResourcesError, VmResources}; use crate::snapshot::Persist; @@ -53,7 +57,7 @@ pub enum DevicePersistError { /// Block: {0} Block(#[from] BlockError), /// Device manager: {0} - DeviceManager(#[from] super::mmio::MmioError), + DeviceManager(#[from] MmioError), /// Mmio transport MmioTransport, #[cfg(target_arch = "aarch64")] @@ -370,11 +374,7 @@ impl<'a> Persist<'a> for MMIODeviceManager { { for state in &state.legacy_devices { if state.type_ == DeviceType::Serial { - let serial = crate::builder::setup_serial_device( - constructor_args.event_manager, - std::io::stdin(), - std::io::stdout(), - )?; + let serial = SerialDevice::new().map_err(crate::VmmError::EventFd)?; dev_manager .address_allocator @@ -383,14 +383,22 @@ impl<'a> Persist<'a> for MMIODeviceManager { MMIO_LEN, AllocPolicy::ExactMatch(state.device_info.addr), ) - .map_err(|e| { - DevicePersistError::DeviceManager(super::mmio::MmioError::Allocator(e)) - })?; + .map_err(|e| DevicePersistError::DeviceManager(MmioError::Allocator(e)))?; + + state + .device_info + .register_kvm_irqfd(vm, serial.serial.interrupt_evt())?; + + let serial = Arc::new(Mutex::new(BusDevice::Serial(serial))); + constructor_args + .event_manager + .add_subscriber(serial.clone()); - dev_manager.register_mmio_serial( - vm, + let identifier = (DeviceType::Serial, DeviceType::Serial.to_string()); + dev_manager.add_bus_device_with_info( + identifier, serial, - Some(state.device_info.clone()), + state.device_info.clone(), )?; } if state.type_ == DeviceType::Rtc { @@ -404,10 +412,13 @@ impl<'a> Persist<'a> for MMIODeviceManager { MMIO_LEN, AllocPolicy::ExactMatch(state.device_info.addr), ) - .map_err(|e| { - DevicePersistError::DeviceManager(super::mmio::MmioError::Allocator(e)) - })?; - dev_manager.register_mmio_rtc(rtc, Some(state.device_info.clone()))?; + .map_err(|e| DevicePersistError::DeviceManager(MmioError::Allocator(e)))?; + let identifier = (DeviceType::Rtc, DeviceType::Rtc.to_string()); + dev_manager.add_bus_device_with_info( + identifier, + Arc::new(Mutex::new(BusDevice::RTCDevice(rtc))), + state.device_info.clone(), + )?; } } } @@ -445,12 +456,14 @@ impl<'a> Persist<'a> for MMIODeviceManager { MMIO_LEN, AllocPolicy::ExactMatch(device_info.addr), ) - .map_err(|e| { - DevicePersistError::DeviceManager(super::mmio::MmioError::Allocator(e)) - })?; - - dev_manager.register_mmio_virtio(vm, id.clone(), mmio_transport, device_info)?; + .map_err(|e| DevicePersistError::DeviceManager(MmioError::Allocator(e)))?; + dev_manager.add_device_with_info( + vm, + id.clone(), + mmio_transport, + device_info.clone(), + )?; event_manager.add_subscriber(as_subscriber); Ok(()) }; @@ -740,10 +753,14 @@ mod tests { let entropy_config = EntropyDeviceConfig::default(); insert_entropy_device(&mut vmm, &mut cmdline, &mut event_manager, entropy_config); - Snapshot::serialize(&mut buf.as_mut_slice(), &vmm.mmio_device_manager.save()).unwrap(); + Snapshot::serialize( + &mut buf.as_mut_slice(), + &vmm.device_manager.mmio_devices.save(), + ) + .unwrap(); // We only want to keep the device map from the original MmioDeviceManager. - vmm.mmio_device_manager.soft_clone() + vmm.device_manager.mmio_devices.soft_clone() }; tmp_sock_file.remove().unwrap(); diff --git a/src/vmm/src/device_manager/mod.rs b/src/vmm/src/device_manager/mod.rs index b55c5154276..150bd63f745 100644 --- a/src/vmm/src/device_manager/mod.rs +++ b/src/vmm/src/device_manager/mod.rs @@ -5,9 +5,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. +#[cfg(target_arch = "x86_64")] +use self::legacy::PortIODeviceManager; +use self::mmio::persist::{DevicePersistError, DeviceStates, MMIODevManagerConstructorArgs}; +use self::mmio::MMIODeviceManager; +use crate::snapshot::Persist; + /// Legacy Device Manager. pub mod legacy; /// Memory Mapped I/O Manager. pub mod mmio; -/// Device managers (de)serialization support. -pub mod persist; + +#[derive(Debug)] +pub struct DeviceManager { + pub mmio_devices: MMIODeviceManager, + #[cfg(target_arch = "x86_64")] + pub pio_diveces: PortIODeviceManager, +} + +impl DeviceManager { + pub fn save(&self) -> DeviceStates { + self.mmio_devices.save() + } + + pub fn restore( + &mut self, + constructor_args: MMIODevManagerConstructorArgs, + state: &DeviceStates, + ) -> Result<(), DevicePersistError> { + self.mmio_devices = MMIODeviceManager::restore(constructor_args, state)?; + Ok(()) + } +} diff --git a/src/vmm/src/devices/bus.rs b/src/vmm/src/devices/bus.rs index 2b016d73083..d8e1fb79e14 100644 --- a/src/vmm/src/devices/bus.rs +++ b/src/vmm/src/devices/bus.rs @@ -65,7 +65,7 @@ pub enum BusDevice { RTCDevice(RTCDevice), BootTimer(BootTimer), MmioTransport(MmioTransport), - Serial(SerialDevice), + Serial(SerialDevice), #[cfg(test)] Dummy(DummyDevice), #[cfg(test)] @@ -127,7 +127,7 @@ impl BusDevice { _ => None, } } - pub fn serial_ref(&self) -> Option<&SerialDevice> { + pub fn serial_ref(&self) -> Option<&SerialDevice> { match self { Self::Serial(x) => Some(x), _ => None, @@ -159,7 +159,7 @@ impl BusDevice { _ => None, } } - pub fn serial_mut(&mut self) -> Option<&mut SerialDevice> { + pub fn serial_mut(&mut self) -> Option<&mut SerialDevice> { match self { Self::Serial(x) => Some(x), _ => None, diff --git a/src/vmm/src/devices/legacy/serial.rs b/src/vmm/src/devices/legacy/serial.rs index a348291fcea..d655572c257 100644 --- a/src/vmm/src/devices/legacy/serial.rs +++ b/src/vmm/src/devices/legacy/serial.rs @@ -7,14 +7,15 @@ //! Implements a wrapper over an UART serial device. use std::fmt::Debug; -use std::io; -use std::io::{Read, Write}; +use std::io::{self, Read, Stdin, Write}; use std::os::unix::io::{AsRawFd, RawFd}; use event_manager::{EventOps, Events, MutEventSubscriber}; +use libc::EFD_NONBLOCK; use log::{error, warn}; use serde::Serialize; use utils::epoll::EventSet; +use utils::eventfd::EventFd; use vm_superio::serial::{Error as SerialError, SerialEvents}; use vm_superio::{Serial, Trigger}; @@ -220,7 +221,26 @@ impl SerialWrapper = SerialWrapper; +pub type SerialDevice = SerialWrapper; + +impl SerialDevice { + pub fn new() -> Result { + let stdin = std::io::stdin(); + let stdout = io::stdout(); + let interrupt_evt = EventFdTrigger::new(EventFd::new(EFD_NONBLOCK)?); + let buffer_ready_event_fd = EventFdTrigger::new(EventFd::new(EFD_NONBLOCK)?); + Ok(SerialDevice { + serial: Serial::with_events( + interrupt_evt, + SerialEventsWrapper { + buffer_ready_event_fd: Some(buffer_ready_event_fd), + }, + SerialOut::Stdout(stdout), + ), + input: Some(stdin), + }) + } +} impl MutEventSubscriber for SerialWrapper diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 83d1607e1c6..deea70eefa6 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -113,6 +113,7 @@ use std::sync::mpsc::RecvTimeoutError; use std::sync::{Arc, Barrier, Mutex}; use std::time::Duration; +use device_manager::DeviceManager; use event_manager::{EventManager as BaseEventManager, EventOps, Events, MutEventSubscriber}; use seccompiler::BpfProgram; use userfaultfd::Uffd; @@ -124,9 +125,6 @@ use vstate::vcpu::{self, KvmVcpuConfigureError, StartThreadedError, VcpuSendEven use crate::arch::DeviceType; use crate::cpu_config::templates::CpuConfiguration; -#[cfg(target_arch = "x86_64")] -use crate::device_manager::legacy::PortIODeviceManager; -use crate::device_manager::mmio::MMIODeviceManager; use crate::devices::legacy::{IER_RDA_BIT, IER_RDA_OFFSET}; use crate::devices::virtio::balloon::{ Balloon, BalloonConfig, BalloonError, BalloonStats, BALLOON_DEV_ID, @@ -137,7 +135,6 @@ use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET}; use crate::logger::{error, info, warn, MetricsError, METRICS}; use crate::persist::{MicrovmState, MicrovmStateError, VmInfo}; use crate::rate_limiter::BucketUpdate; -use crate::snapshot::Persist; use crate::vmm_config::instance_info::{InstanceInfo, VmState}; use crate::vstate::memory::{ GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, @@ -307,10 +304,11 @@ pub struct Vmm { // Used by Vcpus and devices to initiate teardown; Vmm should never write here. vcpus_exit_evt: EventFd, + device_manager: DeviceManager, // Guest VM devices. - mmio_device_manager: MMIODeviceManager, - #[cfg(target_arch = "x86_64")] - pio_device_manager: PortIODeviceManager, + // device_manager.mmio_devices: MMIODeviceManager, + // #[cfg(target_arch = "x86_64")] + // pio_device_manager: PortIODeviceManager, } impl Vmm { @@ -335,7 +333,9 @@ impl Vmm { device_type: DeviceType, device_id: &str, ) -> Option<&Mutex> { - self.mmio_device_manager.get_device(device_type, device_id) + self.device_manager + .mmio_devices + .get_device(device_type, device_id) } /// Starts the microVM vcpus. @@ -372,10 +372,10 @@ impl Vmm { self.vcpus_handles.reserve(vcpu_count); for mut vcpu in vcpus.drain(..) { - vcpu.set_mmio_bus(self.mmio_device_manager.bus.clone()); + vcpu.set_mmio_bus(self.device_manager.mmio_devices.bus.clone()); #[cfg(target_arch = "x86_64")] vcpu.kvm_vcpu - .set_pio_bus(self.pio_device_manager.io_bus.clone()); + .set_pio_bus(self.device_manager.pio_diveces.io_bus.clone()); self.vcpus_handles .push(vcpu.start_threaded(vcpu_seccomp_filter.clone(), barrier.clone())?); @@ -389,7 +389,7 @@ impl Vmm { /// Sends a resume command to the vCPUs. pub fn resume_vm(&mut self) -> Result<(), VmmError> { - self.mmio_device_manager.kick_devices(); + self.device_manager.mmio_devices.kick_devices(); // Send the events. self.vcpus_handles @@ -469,7 +469,8 @@ impl Vmm { #[cfg(target_arch = "x86_64")] { let mut guard = self - .pio_device_manager + .device_manager + .pio_diveces .stdio_serial .lock() .expect("Poisoned lock"); @@ -486,7 +487,8 @@ impl Vmm { /// Injects CTRL+ALT+DEL keystroke combo in the i8042 device. #[cfg(target_arch = "x86_64")] pub fn send_ctrl_alt_del(&mut self) -> Result<(), VmmError> { - self.pio_device_manager + self.device_manager + .pio_diveces .i8042 .lock() .expect("i8042 lock was poisoned") @@ -512,7 +514,7 @@ impl Vmm { self.vm.save_state(&mpidrs).map_err(SaveVmState)? } }; - let device_states = self.mmio_device_manager.save(); + let device_states = self.device_manager.save(); let memory_state = self.guest_memory().describe(); @@ -618,7 +620,8 @@ impl Vmm { drive_id: &str, path_on_host: String, ) -> Result<(), VmmError> { - self.mmio_device_manager + self.device_manager + .mmio_devices .with_virtio_device_with_id(TYPE_BLOCK, drive_id, |block: &mut Block| { block .update_disk_image(path_on_host) @@ -634,7 +637,8 @@ impl Vmm { rl_bytes: BucketUpdate, rl_ops: BucketUpdate, ) -> Result<(), VmmError> { - self.mmio_device_manager + self.device_manager + .mmio_devices .with_virtio_device_with_id(TYPE_BLOCK, drive_id, |block: &mut Block| { block .update_rate_limiter(rl_bytes, rl_ops) @@ -645,7 +649,8 @@ impl Vmm { /// Updates the rate limiter parameters for block device with `drive_id` id. pub fn update_vhost_user_block_config(&mut self, drive_id: &str) -> Result<(), VmmError> { - self.mmio_device_manager + self.device_manager + .mmio_devices .with_virtio_device_with_id(TYPE_BLOCK, drive_id, |block: &mut Block| { block.update_config().map_err(|err| err.to_string()) }) @@ -661,7 +666,8 @@ impl Vmm { tx_bytes: BucketUpdate, tx_ops: BucketUpdate, ) -> Result<(), VmmError> { - self.mmio_device_manager + self.device_manager + .mmio_devices .with_virtio_device_with_id(TYPE_NET, net_id, |net: &mut Net| { net.patch_rate_limiters(rx_bytes, rx_ops, tx_bytes, tx_ops); Ok(()) diff --git a/src/vmm/src/persist.rs b/src/vmm/src/persist.rs index e1290ca8492..3d71dad9062 100644 --- a/src/vmm/src/persist.rs +++ b/src/vmm/src/persist.rs @@ -26,7 +26,7 @@ use crate::cpu_config::templates::StaticCpuTemplate; use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; #[cfg(target_arch = "x86_64")] use crate::cpu_config::x86_64::cpuid::CpuidTrait; -use crate::device_manager::persist::{DevicePersistError, DeviceStates}; +use crate::device_manager::mmio::persist::{DevicePersistError, DeviceStates}; use crate::logger::{info, warn}; use crate::resources::VmResources; use crate::snapshot::Snapshot; @@ -684,7 +684,7 @@ mod tests { #[test] fn test_microvm_state_snapshot() { let vmm = default_vmm_with_devices(); - let states = vmm.mmio_device_manager.save(); + let states = vmm.device_manager.mmio_devices.save(); // Only checking that all devices are saved, actual device state // is tested by that device's tests. diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index 25e83816236..57940449a74 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use utils::net::ipv4addr::is_link_local_valid; use crate::cpu_config::templates::CustomCpuTemplate; -use crate::device_manager::persist::SharedDeviceType; +use crate::device_manager::mmio::persist::SharedDeviceType; use crate::logger::{info, log_dev_preview_warning}; use crate::mmds; use crate::mmds::data_store::{Mmds, MmdsVersion};