• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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