xref: /aosp_15_r20/external/crosvm/devices/src/pci/pci_hotplug.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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