Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pci/stub.rs
5394 views
1
// Copyright 2021 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
//! Implements a stub PCI device. This can be used to put a device on the PCI bus that will
6
//! show up in PCI device enumeration with the configured parameters. The device will otherwise be
7
//! non-functional, in particular it doesn't have any BARs, IRQs etc. and neither will it handle
8
//! config register interactions.
9
//!
10
//! The motivation for stub PCI devices is the case of multifunction PCI devices getting passed
11
//! through via VFIO to the guest. Per PCI device enumeration, functions other than 0 will only be
12
//! scanned if function 0 is present. A stub PCI device is useful in that situation to present
13
//! something to the guest on function 0.
14
15
use base::RawDescriptor;
16
use resources::SystemAllocator;
17
use serde::Deserialize;
18
use serde::Deserializer;
19
use serde::Serialize;
20
use serde::Serializer;
21
use snapshot::AnySnapshot;
22
23
use crate::pci::pci_configuration::PciBarConfiguration;
24
use crate::pci::pci_configuration::PciClassCode;
25
use crate::pci::pci_configuration::PciConfiguration;
26
use crate::pci::pci_configuration::PciHeaderType;
27
use crate::pci::pci_configuration::PciProgrammingInterface;
28
use crate::pci::pci_configuration::PciSubclass;
29
use crate::pci::pci_device::PciDevice;
30
use crate::pci::pci_device::Result;
31
use crate::pci::PciAddress;
32
use crate::pci::PciBarIndex;
33
use crate::pci::PciDeviceError;
34
use crate::Suspendable;
35
36
#[derive(Debug)]
37
pub struct PciClassParameters {
38
pub class: PciClassCode,
39
pub subclass: u8,
40
pub programming_interface: u8,
41
}
42
43
impl Default for PciClassParameters {
44
fn default() -> Self {
45
PciClassParameters {
46
class: PciClassCode::Other,
47
subclass: 0,
48
programming_interface: 0,
49
}
50
}
51
}
52
53
// Deserialize the combined class, subclass, and programming interface as a single numeric value.
54
// This matches the numeric format used in `/sys/bus/pci/devices/*/class`.
55
impl<'de> Deserialize<'de> for PciClassParameters {
56
fn deserialize<D>(deserializer: D) -> std::result::Result<PciClassParameters, D::Error>
57
where
58
D: Deserializer<'de>,
59
{
60
let class_numeric = u32::deserialize(deserializer)?;
61
62
let class_code = (class_numeric >> 16) as u8;
63
let class = PciClassCode::try_from(class_code)
64
.map_err(|_| serde::de::Error::custom(format!("Unknown class code {class_code:#x}")))?;
65
66
let subclass = (class_numeric >> 8) as u8;
67
68
let programming_interface = class_numeric as u8;
69
70
Ok(PciClassParameters {
71
class,
72
subclass,
73
programming_interface,
74
})
75
}
76
}
77
78
impl Serialize for PciClassParameters {
79
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
80
where
81
S: Serializer,
82
{
83
let class_numeric: u32 = ((self.class as u32) << 16)
84
| ((self.subclass as u32) << 8)
85
| self.programming_interface as u32;
86
87
serializer.serialize_u32(class_numeric)
88
}
89
}
90
91
#[derive(Serialize, Deserialize, Debug, serde_keyvalue::FromKeyValues)]
92
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
93
pub struct StubPciParameters {
94
pub address: PciAddress,
95
#[serde(default)]
96
pub vendor: u16,
97
#[serde(default)]
98
pub device: u16,
99
#[serde(default)]
100
pub class: PciClassParameters,
101
#[serde(default, alias = "subsystem_vendor")]
102
pub subsystem_vendor: u16,
103
#[serde(default, alias = "subsystem_device")]
104
pub subsystem_device: u16,
105
#[serde(default)]
106
pub revision: u8,
107
}
108
109
pub struct StubPciDevice {
110
requested_address: PciAddress,
111
assigned_address: Option<PciAddress>,
112
config_regs: PciConfiguration,
113
}
114
115
struct NumericPciSubClass(u8);
116
117
impl PciSubclass for NumericPciSubClass {
118
fn get_register_value(&self) -> u8 {
119
self.0
120
}
121
}
122
123
struct NumericPciProgrammingInterface(u8);
124
125
impl PciProgrammingInterface for NumericPciProgrammingInterface {
126
fn get_register_value(&self) -> u8 {
127
self.0
128
}
129
}
130
131
impl StubPciDevice {
132
pub fn new(config: &StubPciParameters) -> StubPciDevice {
133
let config_regs = PciConfiguration::new(
134
config.vendor,
135
config.device,
136
config.class.class,
137
&NumericPciSubClass(config.class.subclass),
138
Some(&NumericPciProgrammingInterface(
139
config.class.programming_interface,
140
)),
141
PciHeaderType::Device,
142
config.subsystem_vendor,
143
config.subsystem_device,
144
config.revision,
145
);
146
147
Self {
148
requested_address: config.address,
149
assigned_address: None,
150
config_regs,
151
}
152
}
153
}
154
155
impl PciDevice for StubPciDevice {
156
fn debug_label(&self) -> String {
157
"Stub".to_owned()
158
}
159
160
fn preferred_address(&self) -> Option<PciAddress> {
161
Some(self.requested_address)
162
}
163
164
fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
165
if self.assigned_address.is_none() {
166
if resources.reserve_pci(self.requested_address, self.debug_label()) {
167
self.assigned_address = Some(self.requested_address);
168
}
169
}
170
self.assigned_address
171
.ok_or(PciDeviceError::PciAllocationFailed)
172
}
173
174
fn keep_rds(&self) -> Vec<RawDescriptor> {
175
Vec::new()
176
}
177
178
fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
179
self.config_regs.get_bar_configuration(bar_num)
180
}
181
182
fn read_config_register(&self, reg_idx: usize) -> u32 {
183
self.config_regs.read_reg(reg_idx)
184
}
185
186
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
187
self.config_regs.write_reg(reg_idx, offset, data);
188
}
189
190
fn read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8]) {}
191
192
fn write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8]) {}
193
}
194
195
impl Suspendable for StubPciDevice {
196
fn sleep(&mut self) -> anyhow::Result<()> {
197
// There are no workers to sleep/wake.
198
Ok(())
199
}
200
201
fn wake(&mut self) -> anyhow::Result<()> {
202
// There are no workers to sleep/wake.
203
Ok(())
204
}
205
206
fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
207
self.config_regs.snapshot()
208
}
209
210
fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
211
self.config_regs.restore(data)
212
}
213
}
214
215
#[cfg(test)]
216
mod test {
217
use resources::AddressRange;
218
use resources::SystemAllocator;
219
use resources::SystemAllocatorConfig;
220
use serde_keyvalue::from_key_values;
221
use serde_keyvalue::ErrorKind;
222
use serde_keyvalue::ParseError;
223
224
use super::*;
225
226
const CONFIG: StubPciParameters = StubPciParameters {
227
address: PciAddress {
228
bus: 0x0a,
229
dev: 0x0b,
230
func: 0x1,
231
},
232
vendor: 2,
233
device: 3,
234
class: PciClassParameters {
235
class: PciClassCode::MultimediaController,
236
subclass: 5,
237
programming_interface: 6,
238
},
239
subsystem_vendor: 7,
240
subsystem_device: 8,
241
revision: 9,
242
};
243
244
fn from_stub_arg(options: &str) -> std::result::Result<StubPciParameters, ParseError> {
245
from_key_values(options)
246
}
247
248
#[test]
249
fn configuration() {
250
let device = StubPciDevice::new(&CONFIG);
251
252
assert_eq!(device.read_config_register(0), 0x0003_0002);
253
assert_eq!(device.read_config_register(2), 0x04_05_06_09);
254
assert_eq!(device.read_config_register(11), 0x0008_0007);
255
}
256
257
#[test]
258
fn address_allocation() {
259
let mut allocator = SystemAllocator::new(
260
SystemAllocatorConfig {
261
io: Some(AddressRange {
262
start: 0x1000,
263
end: 0x2fff,
264
}),
265
low_mmio: AddressRange {
266
start: 0x2000_0000,
267
end: 0x2fff_ffff,
268
},
269
high_mmio: AddressRange {
270
start: 0x1_0000_0000,
271
end: 0x1_0fff_ffff,
272
},
273
platform_mmio: None,
274
first_irq: 5,
275
},
276
None,
277
&[],
278
)
279
.unwrap();
280
let mut device = StubPciDevice::new(&CONFIG);
281
282
assert!(device.allocate_address(&mut allocator).is_ok());
283
assert!(allocator.release_pci(PciAddress::new(0, 0xa, 0xb, 1).unwrap()));
284
}
285
286
#[test]
287
fn params_missing_address() {
288
// PCI address argument is mandatory.
289
let err = from_stub_arg("").unwrap_err();
290
assert_eq!(
291
err,
292
ParseError {
293
kind: ErrorKind::SerdeError("missing field `address`".into()),
294
pos: 0,
295
}
296
);
297
}
298
299
#[test]
300
fn params_address_implicit() {
301
// Address is the default argument.
302
let params = from_stub_arg("0000:00:01.2").unwrap();
303
assert_eq!(
304
params.address,
305
PciAddress {
306
bus: 0,
307
dev: 1,
308
func: 2
309
}
310
);
311
}
312
313
#[test]
314
fn params_address_explicit() {
315
// Explicitly-specified address.
316
let params = from_stub_arg("address=0000:00:01.2").unwrap();
317
assert_eq!(
318
params.address,
319
PciAddress {
320
bus: 0,
321
dev: 1,
322
func: 2
323
}
324
);
325
}
326
327
#[test]
328
fn params_class() {
329
// Class, subclass, and programming interface are encoded as a single number.
330
let params = from_stub_arg("address=0000:00:01.2,class=0x012345").unwrap();
331
assert_eq!(params.class.class, PciClassCode::MassStorage);
332
assert_eq!(params.class.subclass, 0x23);
333
assert_eq!(params.class.programming_interface, 0x45);
334
}
335
336
#[test]
337
fn params_subsystem_underscores() {
338
// Accept aliases with underscores rather than hyphens for compatibility.
339
let params =
340
from_stub_arg("address=0000:00:01.2,subsystem_vendor=0x8675,subsystem_device=0x309")
341
.unwrap();
342
assert_eq!(params.subsystem_vendor, 0x8675);
343
assert_eq!(params.subsystem_device, 0x0309);
344
}
345
346
#[test]
347
fn params_full() {
348
let params = from_stub_arg(
349
"address=0000:00:01.2,vendor=0x1234,device=0x5678,subsystem-vendor=0x8675,subsystem-device=0x309,class=0x012345,revision=52",
350
).unwrap();
351
assert_eq!(
352
params.address,
353
PciAddress {
354
bus: 0,
355
dev: 1,
356
func: 2
357
}
358
);
359
assert_eq!(params.vendor, 0x1234);
360
assert_eq!(params.device, 0x5678);
361
assert_eq!(params.subsystem_vendor, 0x8675);
362
assert_eq!(params.subsystem_device, 0x0309);
363
assert_eq!(params.class.class, PciClassCode::MassStorage);
364
assert_eq!(params.class.subclass, 0x23);
365
assert_eq!(params.class.programming_interface, 0x45);
366
assert_eq!(params.revision, 52);
367
}
368
369
#[test]
370
fn stub_pci_device_snapshot_restore() -> anyhow::Result<()> {
371
let mut device = StubPciDevice::new(&CONFIG);
372
let init_reg_value = device.read_config_register(1);
373
let snapshot_init = device.snapshot().unwrap();
374
375
// Modify config reg 1 and make sure it went through.
376
let new_reg_value: u32 = 0xCAFE;
377
device.write_config_register(1, 0, &new_reg_value.to_le_bytes());
378
assert_eq!(device.read_config_register(1), new_reg_value);
379
380
// Capture a snapshot after the modification.
381
let mut snapshot_modified = device.snapshot().unwrap();
382
assert_ne!(snapshot_init, snapshot_modified);
383
384
// Modify the same register and verify that it's restored correctly.
385
device.write_config_register(1, 0, &[0xBA, 0xBA]);
386
assert_ne!(device.read_config_register(1), new_reg_value);
387
assert_ne!(device.read_config_register(1), init_reg_value);
388
device.restore(snapshot_init.clone())?;
389
assert_eq!(device.read_config_register(1), init_reg_value);
390
391
// Capture a snapshot after restoring the initial snapshot.
392
let mut snapshot_restored = device.snapshot().unwrap();
393
assert_eq!(snapshot_init, snapshot_restored);
394
395
// Restore to the first modification and verify the values.
396
device.restore(snapshot_modified.clone())?;
397
assert_eq!(device.read_config_register(1), new_reg_value);
398
snapshot_restored = device.snapshot().unwrap();
399
assert_eq!(snapshot_modified, snapshot_restored);
400
401
/*
402
Restore the initial snapshot and verify that addresses are not encoded.
403
The addresses are only configurable during VM creation so they never
404
change afterwards and are not part of the snapshot. Force a change
405
to requested_address to confirm that.
406
*/
407
device.restore(snapshot_init.clone())?;
408
device.requested_address = PciAddress {
409
bus: 0x0d,
410
dev: 0x0e,
411
func: 0x4,
412
};
413
snapshot_modified = device.snapshot().unwrap();
414
assert_eq!(snapshot_init, snapshot_modified);
415
416
Ok(())
417
}
418
}
419
420