From 791ca3388f6a42a48fe562d45b588036cd82af13 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 27 Nov 2019 15:28:22 +0000 Subject: [PATCH] vmm: device_manager: Add ability to notify via GED device Add ability to notify via the GED device that there is some new hotplug activity. This will be used by the CpuManager (and later DeviceManager itself) to notify of new hotplug activity. Currently it has a hardcoded IRQ of 5 as the ACPI tables also need to refer to this IRQ and the IRQ allocation does not permit the allocation of specific IRQs. Signed-off-by: Rob Bradford --- vmm/src/device_manager.rs | 98 ++++++++++++++++++++++++++++++--------- vmm/src/vm.rs | 2 +- 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index eccacc14d..7f7197893 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -12,7 +12,7 @@ use crate::config::ConsoleOutputMode; use crate::vm::VmInfo; -use devices::ioapic; +use devices::{ioapic, HotPlugNotificationType}; use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::*; use libc::O_TMPFILE; @@ -169,6 +169,9 @@ pub enum DeviceManagerError { /// Failed to allocate IO port AllocateIOPort, + + // Failed to make hotplug notification + HotPlugNotification(io::Error), } pub type DeviceManagerResult = result::Result; @@ -402,6 +405,10 @@ pub struct DeviceManager { // Virtual IOMMU ID along with the list of device IDs attached to the // virtual IOMMU. This is useful for filling the ACPI IORT table. virt_iommu: Option<(u32, Vec)>, + + // ACPI GED notification device + #[cfg(feature = "acpi")] + ged_notification_device: Option>>, } impl DeviceManager { @@ -460,10 +467,13 @@ impl DeviceManager { reset_evt.try_clone().map_err(DeviceManagerError::EventFd)?, )?; - DeviceManager::add_acpi_device( + #[cfg(feature = "acpi")] + let ged_notification_device = DeviceManager::add_acpi_devices( + vm_info, &address_manager, reset_evt.try_clone().map_err(DeviceManagerError::EventFd)?, _exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?, + &ioapic, )?; if cfg!(feature = "pci_support") { @@ -492,6 +502,8 @@ impl DeviceManager { mmap_regions, cmdline_additions, virt_iommu, + #[cfg(feature = "acpi")] + ged_notification_device, }) } @@ -658,31 +670,58 @@ impl DeviceManager { } #[allow(unused_variables)] - fn add_acpi_device( + #[cfg(feature = "acpi")] + fn add_acpi_devices( + vm_info: &VmInfo, address_manager: &Arc, reset_evt: EventFd, exit_evt: EventFd, - ) -> DeviceManagerResult<()> { - #[cfg(feature = "acpi")] - { - let acpi_device = Arc::new(Mutex::new(devices::AcpiShutdownDevice::new( - exit_evt, reset_evt, - ))); + ioapic: &Option>>, + ) -> DeviceManagerResult>>> { + let acpi_device = Arc::new(Mutex::new(devices::AcpiShutdownDevice::new( + exit_evt, reset_evt, + ))); - address_manager - .allocator - .lock() - .unwrap() - .allocate_io_addresses(Some(GuestAddress(0x3c0)), 0x8, None) - .ok_or(DeviceManagerError::AllocateIOPort)?; + address_manager + .allocator + .lock() + .unwrap() + .allocate_io_addresses(Some(GuestAddress(0x3c0)), 0x8, None) + .ok_or(DeviceManagerError::AllocateIOPort)?; - address_manager - .io_bus - .insert(acpi_device.clone(), 0x3c0, 0x4) - .map_err(DeviceManagerError::BusError)?; - } + address_manager + .io_bus + .insert(acpi_device.clone(), 0x3c0, 0x4) + .map_err(DeviceManagerError::BusError)?; - Ok(()) + // We need to hardcode this as the ACPI tables need to specify a particular IRQ and it's not possible + // to ask the allocator for a specific one. + let ged_irq = 5; + let interrupt: Box = if let Some(ioapic) = &ioapic { + Box::new(UserIoapicIrq::new(ioapic.clone(), ged_irq)) + } else { + let ged_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; + vm_info + .vm_fd + .register_irqfd(&ged_evt, ged_irq as u32) + .map_err(DeviceManagerError::Irq)?; + + Box::new(KernelIoapicIrq::new(ged_evt)) + }; + let ged_device = Arc::new(Mutex::new(devices::AcpiGEDDevice::new(interrupt))); + + address_manager + .allocator + .lock() + .unwrap() + .allocate_io_addresses(Some(GuestAddress(0xb000)), 0x1, None) + .ok_or(DeviceManagerError::AllocateIOPort)?; + + address_manager + .io_bus + .insert(ged_device.clone(), 0xb000, 0x1) + .map_err(DeviceManagerError::BusError)?; + Ok(Some(ged_device)) } fn add_legacy_devices( @@ -1540,6 +1579,23 @@ impl DeviceManager { None } } + + pub fn notify_hotplug( + &self, + _notification_type: HotPlugNotificationType, + ) -> DeviceManagerResult<()> { + #[cfg(feature = "acpi")] + return self + .ged_notification_device + .as_ref() + .unwrap() + .lock() + .unwrap() + .notify(_notification_type) + .map_err(DeviceManagerError::HotPlugNotification); + #[cfg(not(feature = "acpi"))] + return Ok(()); + } } impl Drop for DeviceManager { diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 3bf2ba5c8..a7c782450 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -54,7 +54,7 @@ use vm_memory::{ use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::terminal::Terminal; -const X86_64_IRQ_BASE: u32 = 5; +const X86_64_IRQ_BASE: u32 = 6; // CPUID feature bits const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.