devices/pci/
pci_hotplug.rs

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
9use base::AsRawDescriptor;
10use base::AsRawDescriptors;
11use base::RawDescriptor;
12use base::Tube;
13use serde::Deserialize;
14use serde::Serialize;
15use vm_control::api::VmMemoryClient;
16
17use crate::virtio::NetParameters;
18use crate::IrqLevelEvent;
19use crate::PciAddress;
20use crate::PciDevice;
21use crate::PciDeviceError;
22use crate::PciInterruptPin;
23
24pub 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)]
31pub enum ResourceCarrier {
32    /// virtio-net device.
33    VirtioNet(NetResourceCarrier),
34}
35
36impl ResourceCarrier {
37    /// Returns debug label for the target device.
38    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.
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.
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.
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.
73pub trait HotPluggable: PciDevice {
74    /// Sets PciAddress to pci_addr. Replaces allocate_address.
75    fn set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()>;
76
77    /// Configures IO BAR layout without memory alloc. Replaces allocate_io_bars.
78    fn configure_io_bars(&mut self) -> Result<()>;
79
80    /// Configure device BAR layout without memory alloc. Replaces allocate_device_bars.
81    fn configure_device_bars(&mut self) -> Result<()>;
82}
83
84impl<T: HotPluggable + ?Sized> HotPluggable for Box<T> {
85    fn set_pci_address(&mut self, pci_addr: PciAddress) -> Result<()> {
86        (**self).set_pci_address(pci_addr)
87    }
88
89    fn configure_io_bars(&mut self) -> Result<()> {
90        (**self).configure_io_bars()
91    }
92
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)]
102pub 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
117impl NetResourceCarrier {
118    ///Constructs NetResourceCarrier.
119    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
135    fn debug_label(&self) -> String {
136        "virtio-net".to_owned()
137    }
138
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
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
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)]
183pub 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}