Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/pci_hotplug.rs
5394 views
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.
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.
73
pub 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
84
impl<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)]
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.
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)]
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
192