devices/pci/
stub.rs

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