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