1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! Trait definitions and implementations for PCI hotplug. 6 7 #![deny(missing_docs)] 8 9 use base::AsRawDescriptor; 10 use base::AsRawDescriptors; 11 use base::RawDescriptor; 12 use base::Tube; 13 use serde::Deserialize; 14 use serde::Serialize; 15 use vm_control::api::VmMemoryClient; 16 17 use crate::virtio::NetParameters; 18 use crate::IrqLevelEvent; 19 use crate::PciAddress; 20 use crate::PciDevice; 21 use crate::PciDeviceError; 22 use crate::PciInterruptPin; 23 24 pub type Result<T> = std::result::Result<T, PciDeviceError>; 25 26 /// A ResourceCarrier moves resources for PCI device across process boundary. 27 /// 28 /// ResourceCarrier can be sent across processes using De/Serialize. All the variants shall be able 29 /// to convert into a HotPlugPluggable device. 30 #[derive(Serialize, Deserialize)] 31 pub enum ResourceCarrier { 32 /// virtio-net device. 33 VirtioNet(NetResourceCarrier), 34 } 35 36 impl ResourceCarrier { 37 /// Returns debug label for the target device. debug_label(&self) -> String38 pub fn debug_label(&self) -> String { 39 match self { 40 ResourceCarrier::VirtioNet(c) => c.debug_label(), 41 } 42 } 43 44 /// A vector of device-specific file descriptors that must be kept open 45 /// after jailing. Must be called before the process is jailed. keep_rds(&self) -> Vec<RawDescriptor>46 pub fn keep_rds(&self) -> Vec<RawDescriptor> { 47 match self { 48 ResourceCarrier::VirtioNet(c) => c.keep_rds(), 49 } 50 } 51 /// Allocate the preferred address to the device. allocate_address( &mut self, preferred_address: PciAddress, resources: &mut resources::SystemAllocator, ) -> Result<()>52 pub fn allocate_address( 53 &mut self, 54 preferred_address: PciAddress, 55 resources: &mut resources::SystemAllocator, 56 ) -> Result<()> { 57 match self { 58 ResourceCarrier::VirtioNet(c) => c.allocate_address(preferred_address, resources), 59 } 60 } 61 /// Assign a legacy PCI IRQ to this device. 62 /// The device may write to `irq_evt` to trigger an interrupt. 63 /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary. assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32)64 pub fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) { 65 match self { 66 ResourceCarrier::VirtioNet(c) => c.assign_irq(irq_evt, pin, irq_num), 67 } 68 } 69 } 70 71 /// Additional requirements for a PciDevice to support hotplug. 72 /// A hotplug device can be configured without access to the SystemAllocator. 73 pub trait HotPluggable: PciDevice { 74 /// Sets PciAddress to pci_addr. Replaces allocate_address. set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()>75 fn set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()>; 76 77 /// Configures IO BAR layout without memory alloc. Replaces allocate_io_bars. configure_io_bars(&mut self) -> Result<()>78 fn configure_io_bars(&mut self) -> Result<()>; 79 80 /// Configure device BAR layout without memory alloc. Replaces allocate_device_bars. configure_device_bars(&mut self) -> Result<()>81 fn configure_device_bars(&mut self) -> Result<()>; 82 } 83 84 impl<T: HotPluggable + ?Sized> HotPluggable for Box<T> { set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()>85 fn set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()> { 86 (**self).set_pci_address(pci_addr) 87 } 88 configure_io_bars(&mut self) -> Result<()>89 fn configure_io_bars(&mut self) -> Result<()> { 90 (**self).configure_io_bars() 91 } 92 configure_device_bars(&mut self) -> Result<()>93 fn configure_device_bars(&mut self) -> Result<()> { 94 (**self).configure_device_bars() 95 } 96 } 97 98 /// A NetResourceCarrier is a ResourceCarrier specialization for virtio-net devices. 99 /// 100 /// TODO(b/289155315): make members private. 101 #[derive(Serialize, Deserialize)] 102 pub struct NetResourceCarrier { 103 /// NetParameters for constructing tap device 104 pub net_param: NetParameters, 105 /// msi_device_tube for VirtioPciDevice constructor 106 pub msi_device_tube: Tube, 107 /// ioevent_vm_memory_client for VirtioPciDevice constructor 108 pub ioevent_vm_memory_client: VmMemoryClient, 109 /// pci_address for the hotplugged device 110 pub pci_address: Option<PciAddress>, 111 /// intx_parameter for assign_irq 112 pub intx_parameter: Option<IntxParameter>, 113 /// vm_control_tube for VirtioPciDevice constructor 114 pub vm_control_tube: Tube, 115 } 116 117 impl NetResourceCarrier { 118 ///Constructs NetResourceCarrier. new( net_param: NetParameters, msi_device_tube: Tube, ioevent_vm_memory_client: VmMemoryClient, vm_control_tube: Tube, ) -> Self119 pub fn new( 120 net_param: NetParameters, 121 msi_device_tube: Tube, 122 ioevent_vm_memory_client: VmMemoryClient, 123 vm_control_tube: Tube, 124 ) -> Self { 125 Self { 126 net_param, 127 msi_device_tube, 128 ioevent_vm_memory_client, 129 pci_address: None, 130 intx_parameter: None, 131 vm_control_tube, 132 } 133 } 134 debug_label(&self) -> String135 fn debug_label(&self) -> String { 136 "virtio-net".to_owned() 137 } 138 keep_rds(&self) -> Vec<RawDescriptor>139 fn keep_rds(&self) -> Vec<RawDescriptor> { 140 let mut keep_rds = vec![ 141 self.msi_device_tube.as_raw_descriptor(), 142 self.ioevent_vm_memory_client.as_raw_descriptor(), 143 ]; 144 if let Some(intx_parameter) = &self.intx_parameter { 145 keep_rds.extend(intx_parameter.irq_evt.as_raw_descriptors()); 146 } 147 keep_rds 148 } 149 allocate_address( &mut self, preferred_address: PciAddress, resources: &mut resources::SystemAllocator, ) -> Result<()>150 fn allocate_address( 151 &mut self, 152 preferred_address: PciAddress, 153 resources: &mut resources::SystemAllocator, 154 ) -> Result<()> { 155 match self.pci_address { 156 None => { 157 if resources.reserve_pci(preferred_address, self.debug_label()) { 158 self.pci_address = Some(preferred_address); 159 } else { 160 return Err(PciDeviceError::PciAllocationFailed); 161 } 162 } 163 Some(pci_address) => { 164 if pci_address != preferred_address { 165 return Err(PciDeviceError::PciAllocationFailed); 166 } 167 } 168 } 169 Ok(()) 170 } 171 assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32)172 fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) { 173 self.intx_parameter = Some(IntxParameter { 174 irq_evt, 175 pin, 176 irq_num, 177 }); 178 } 179 } 180 181 /// Parameters for legacy INTx interrrupt. 182 #[derive(Serialize, Deserialize)] 183 pub struct IntxParameter { 184 /// interrupt level event 185 pub irq_evt: IrqLevelEvent, 186 /// INTx interrupt pin 187 pub pin: PciInterruptPin, 188 /// irq num 189 pub irq_num: u32, 190 } 191