diff --git a/devices/src/irq_event.rs b/devices/src/irq_event.rs index 545c6004ce..ea13d96656 100644 --- a/devices/src/irq_event.rs +++ b/devices/src/irq_event.rs @@ -7,6 +7,8 @@ use base::AsRawDescriptors; use base::Event; use base::RawDescriptor; use base::Result; +use serde::Deserialize; +use serde::Serialize; /// A structure suitable for implementing edge triggered interrupts in device backends. pub struct IrqEdgeEvent(Event); @@ -45,6 +47,7 @@ impl IrqEdgeEvent { /// the interrupt with an End of Interrupt (EOI) command, the IRQ chip will signal the resample /// event. Each time the resample event is signalled, the device should re-check its state and call /// [`IrqLevelEvent::trigger()`] again if the interrupt should still be asserted. +#[derive(Debug, Serialize, Deserialize)] pub struct IrqLevelEvent { /// An event used by the device backend to signal hypervisor/VM about data or new unit /// of work being available. diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 66f08fa56b..fb612c6b6d 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -107,8 +107,14 @@ pub use self::pci::BarRange; pub use self::pci::CrosvmDeviceId; #[cfg(feature = "pci-hotplug")] pub use self::pci::HotPluggable; +#[cfg(feature = "pci-hotplug")] +pub use self::pci::IntxParameter; +#[cfg(feature = "pci-hotplug")] +pub use self::pci::NetResourceCarrier; pub use self::pci::PciAddress; pub use self::pci::PciAddressError; +pub use self::pci::PciBarConfiguration; +pub use self::pci::PciBarIndex; pub use self::pci::PciBus; pub use self::pci::PciClassCode; pub use self::pci::PciConfigIo; @@ -120,6 +126,9 @@ pub use self::pci::PciRoot; pub use self::pci::PciRootCommand; pub use self::pci::PciVirtualConfigMmio; pub use self::pci::PreferredIrq; +#[cfg(feature = "pci-hotplug")] +pub use self::pci::ResourceCarrier; + pub use self::pci::StubPciDevice; pub use self::pci::StubPciParameters; pub use self::pflash::Pflash; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 052609466c..dff3198150 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -78,6 +78,12 @@ pub use self::pci_device::PciDevice; pub use self::pci_device::PreferredIrq; #[cfg(feature = "pci-hotplug")] pub use self::pci_hotplug::HotPluggable; +#[cfg(feature = "pci-hotplug")] +pub use self::pci_hotplug::IntxParameter; +#[cfg(feature = "pci-hotplug")] +pub use self::pci_hotplug::NetResourceCarrier; +#[cfg(feature = "pci-hotplug")] +pub use self::pci_hotplug::ResourceCarrier; pub use self::pci_root::PciConfigIo; pub use self::pci_root::PciConfigMmio; pub use self::pci_root::PciRoot; diff --git a/devices/src/pci/pci_hotplug.rs b/devices/src/pci/pci_hotplug.rs index bfce12cd06..1e4da20bbc 100644 --- a/devices/src/pci/pci_hotplug.rs +++ b/devices/src/pci/pci_hotplug.rs @@ -2,16 +2,73 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -//! Trait definitions for PCI hotplug. +//! Trait definitions and implementations for PCI hotplug. #![deny(missing_docs)] +use base::AsRawDescriptor; +use base::AsRawDescriptors; +use base::RawDescriptor; +use base::Tube; +use resources::Alloc; +use serde::Deserialize; +use serde::Serialize; +use vm_control::api::VmMemoryClient; + +use crate::virtio::NetParameters; +use crate::IrqLevelEvent; use crate::PciAddress; use crate::PciDevice; use crate::PciDeviceError; +use crate::PciInterruptPin; pub type Result = std::result::Result; +/// A ResourceCarrier moves resources for PCI device across process boundary. +/// +/// ResourceCarrier can be sent across processes using De/Serialize. All the variants shall be able +/// to convert into a HotPlugPluggable device. +#[derive(Serialize, Deserialize)] +pub enum ResourceCarrier { + /// virtio-net device. + VirtioNet(NetResourceCarrier), +} + +impl ResourceCarrier { + /// Returns debug label for the target device. + pub fn debug_label(&self) -> String { + match self { + ResourceCarrier::VirtioNet(c) => c.debug_label(), + } + } + + /// A vector of device-specific file descriptors that must be kept open + /// after jailing. Must be called before the process is jailed. + pub fn keep_rds(&self) -> Vec { + match self { + ResourceCarrier::VirtioNet(c) => c.keep_rds(), + } + } + /// Allocate the preferred address to the device. + pub fn allocate_address( + &mut self, + preferred_address: PciAddress, + resources: &mut resources::SystemAllocator, + ) -> Result<()> { + match self { + ResourceCarrier::VirtioNet(c) => c.allocate_address(preferred_address, resources), + } + } + /// Assign a legacy PCI IRQ to this device. + /// The device may write to `irq_evt` to trigger an interrupt. + /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary. + pub fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) { + match self { + ResourceCarrier::VirtioNet(c) => c.assign_irq(irq_evt, pin, irq_num), + } + } +} + /// Additional requirements for a PciDevice to support hotplug. /// A hotplug device can be configured without access to the SystemAllocator. pub trait HotPluggable: PciDevice { @@ -38,3 +95,101 @@ impl HotPluggable for Box { (**self).configure_device_bars() } } + +/// A NetResourceCarrier is a ResourceCarrier specialization for virtio-net devices. +/// +/// TODO(b/289155315): make members private. +#[derive(Serialize, Deserialize)] +pub struct NetResourceCarrier { + /// NetParameters for constructing tap device + pub net_param: NetParameters, + /// msi_device_tube for VirtioPciDevice constructor + pub msi_device_tube: Tube, + /// ioevent_vm_memory_client for VirtioPciDevice constructor + pub ioevent_vm_memory_client: VmMemoryClient, + /// pci_address for the hotplugged device + pub pci_address: Option, + /// intx_parameter for assign_irq + pub intx_parameter: Option, +} + +impl NetResourceCarrier { + ///Constructs NetResourceCarrier. + pub fn new( + net_param: NetParameters, + msi_device_tube: Tube, + ioevent_vm_memory_client: VmMemoryClient, + ) -> Self { + Self { + net_param, + msi_device_tube, + ioevent_vm_memory_client, + pci_address: None, + intx_parameter: None, + } + } + + fn debug_label(&self) -> String { + "virtio-net".to_owned() + } + + fn keep_rds(&self) -> Vec { + let mut keep_rds = vec![ + self.msi_device_tube.as_raw_descriptor(), + self.ioevent_vm_memory_client.as_raw_descriptor(), + ]; + if let Some(intx_parameter) = &self.intx_parameter { + keep_rds.extend(intx_parameter.irq_evt.as_raw_descriptors()); + } + keep_rds + } + + fn allocate_address( + &mut self, + preferred_address: PciAddress, + resources: &mut resources::SystemAllocator, + ) -> Result<()> { + match self.pci_address { + None => { + if resources.reserve_pci( + Alloc::PciBar { + bus: preferred_address.bus, + dev: preferred_address.dev, + func: preferred_address.func, + bar: 0, + }, + self.debug_label(), + ) { + self.pci_address = Some(preferred_address); + } else { + return Err(PciDeviceError::PciAllocationFailed); + } + } + Some(pci_address) => { + if pci_address != preferred_address { + return Err(PciDeviceError::PciAllocationFailed); + } + } + } + Ok(()) + } + + fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) { + self.intx_parameter = Some(IntxParameter { + irq_evt, + pin, + irq_num, + }); + } +} + +/// Parameters for legacy INTx interrrupt. +#[derive(Serialize, Deserialize)] +pub struct IntxParameter { + /// interrupt level event + pub irq_evt: IrqLevelEvent, + /// INTx interrupt pin + pub pin: PciInterruptPin, + /// irq num + pub irq_num: u32, +} diff --git a/vm_control/src/api.rs b/vm_control/src/api.rs index 61cce33b5c..e0ed8a239b 100644 --- a/vm_control/src/api.rs +++ b/vm_control/src/api.rs @@ -13,6 +13,8 @@ use base::TubeError; use hypervisor::Datamatch; use remain::sorted; use resources::Alloc; +use serde::Deserialize; +use serde::Serialize; use thiserror::Error; use vm_memory::GuestAddress; @@ -38,6 +40,7 @@ pub enum ApiClientError { pub type Result = std::result::Result; +#[derive(Serialize, Deserialize)] pub struct VmMemoryClient { tube: Tube, }