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 resources::SystemAllocator;
17use serde::Deserialize;
18use serde::Deserializer;
19use serde::Serialize;
20use serde::Serializer;
21use snapshot::AnySnapshot;
22
23use crate::pci::pci_configuration::PciBarConfiguration;
24use crate::pci::pci_configuration::PciClassCode;
25use crate::pci::pci_configuration::PciConfiguration;
26use crate::pci::pci_configuration::PciHeaderType;
27use crate::pci::pci_configuration::PciProgrammingInterface;
28use crate::pci::pci_configuration::PciSubclass;
29use crate::pci::pci_device::PciDevice;
30use crate::pci::pci_device::Result;
31use crate::pci::PciAddress;
32use crate::pci::PciBarIndex;
33use crate::pci::PciDeviceError;
34use crate::Suspendable;
35
36#[derive(Debug)]
37pub struct PciClassParameters {
38    pub class: PciClassCode,
39    pub subclass: u8,
40    pub programming_interface: u8,
41}
42
43impl 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`.
55impl<'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
78impl 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")]
93pub 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
109pub struct StubPciDevice {
110    requested_address: PciAddress,
111    assigned_address: Option<PciAddress>,
112    config_regs: PciConfiguration,
113}
114
115struct NumericPciSubClass(u8);
116
117impl PciSubclass for NumericPciSubClass {
118    fn get_register_value(&self) -> u8 {
119        self.0
120    }
121}
122
123struct NumericPciProgrammingInterface(u8);
124
125impl PciProgrammingInterface for NumericPciProgrammingInterface {
126    fn get_register_value(&self) -> u8 {
127        self.0
128    }
129}
130
131impl 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
155impl 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
195impl 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)]
216mod 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}