• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
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::{Alloc, SystemAllocator};
17 
18 use crate::pci::pci_configuration::{
19     PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface,
20     PciSubclass,
21 };
22 use crate::pci::pci_device::{PciDevice, Result};
23 use crate::pci::{PciAddress, PciDeviceError};
24 use serde::{Deserialize, Serialize};
25 
26 #[derive(Serialize, Deserialize)]
27 pub struct StubPciParameters {
28     pub address: PciAddress,
29     pub vendor_id: u16,
30     pub device_id: u16,
31     pub class: PciClassCode,
32     pub subclass: u8,
33     pub programming_interface: u8,
34     pub subsystem_vendor_id: u16,
35     pub subsystem_device_id: u16,
36     pub revision_id: u8,
37 }
38 
39 pub struct StubPciDevice {
40     requested_address: PciAddress,
41     assigned_address: Option<PciAddress>,
42     config_regs: PciConfiguration,
43 }
44 
45 struct NumericPciSubClass(u8);
46 
47 impl PciSubclass for NumericPciSubClass {
get_register_value(&self) -> u848     fn get_register_value(&self) -> u8 {
49         self.0
50     }
51 }
52 
53 struct NumericPciProgrammingInterface(u8);
54 
55 impl PciProgrammingInterface for NumericPciProgrammingInterface {
get_register_value(&self) -> u856     fn get_register_value(&self) -> u8 {
57         self.0
58     }
59 }
60 
61 impl StubPciDevice {
new(config: &StubPciParameters) -> StubPciDevice62     pub fn new(config: &StubPciParameters) -> StubPciDevice {
63         let config_regs = PciConfiguration::new(
64             config.vendor_id,
65             config.device_id,
66             config.class,
67             &NumericPciSubClass(config.subclass),
68             Some(&NumericPciProgrammingInterface(
69                 config.programming_interface,
70             )),
71             PciHeaderType::Device,
72             config.subsystem_vendor_id,
73             config.subsystem_device_id,
74             config.revision_id,
75         );
76 
77         Self {
78             requested_address: config.address,
79             assigned_address: None,
80             config_regs,
81         }
82     }
83 }
84 
85 impl PciDevice for StubPciDevice {
debug_label(&self) -> String86     fn debug_label(&self) -> String {
87         "Stub".to_owned()
88     }
89 
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>90     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
91         if self.assigned_address.is_none() {
92             if resources.reserve_pci(
93                 Alloc::PciBar {
94                     bus: self.requested_address.bus,
95                     dev: self.requested_address.dev,
96                     func: self.requested_address.func,
97                     bar: 0,
98                 },
99                 self.debug_label(),
100             ) {
101                 self.assigned_address = Some(self.requested_address);
102             }
103         }
104         self.assigned_address
105             .ok_or(PciDeviceError::PciAllocationFailed)
106     }
107 
keep_rds(&self) -> Vec<RawDescriptor>108     fn keep_rds(&self) -> Vec<RawDescriptor> {
109         Vec::new()
110     }
111 
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>112     fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
113         self.config_regs.get_bar_configuration(bar_num)
114     }
115 
read_config_register(&self, reg_idx: usize) -> u32116     fn read_config_register(&self, reg_idx: usize) -> u32 {
117         self.config_regs.read_reg(reg_idx)
118     }
119 
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])120     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
121         (&mut self.config_regs).write_reg(reg_idx, offset, data)
122     }
123 
read_bar(&mut self, _addr: u64, _data: &mut [u8])124     fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {}
125 
write_bar(&mut self, _addr: u64, _data: &[u8])126     fn write_bar(&mut self, _addr: u64, _data: &[u8]) {}
127 }
128 
129 #[cfg(test)]
130 mod test {
131     use super::*;
132     use resources::{MemRegion, SystemAllocator, SystemAllocatorConfig};
133 
134     const CONFIG: StubPciParameters = StubPciParameters {
135         address: PciAddress {
136             bus: 0x0a,
137             dev: 0x0b,
138             func: 0x1,
139         },
140         vendor_id: 2,
141         device_id: 3,
142         class: PciClassCode::MultimediaController,
143         subclass: 5,
144         programming_interface: 6,
145         subsystem_vendor_id: 7,
146         subsystem_device_id: 8,
147         revision_id: 9,
148     };
149 
150     #[test]
configuration()151     fn configuration() {
152         let device = StubPciDevice::new(&CONFIG);
153 
154         assert_eq!(device.read_config_register(0), 0x0003_0002);
155         assert_eq!(device.read_config_register(2), 0x04_05_06_09);
156         assert_eq!(device.read_config_register(11), 0x0008_0007);
157     }
158 
159     #[test]
address_allocation()160     fn address_allocation() {
161         let mut allocator = SystemAllocator::new(
162             SystemAllocatorConfig {
163                 io: Some(MemRegion {
164                     base: 0x1000,
165                     size: 0x2000,
166                 }),
167                 low_mmio: MemRegion {
168                     base: 0x2000_0000,
169                     size: 0x1000_0000,
170                 },
171                 high_mmio: MemRegion {
172                     base: 0x1_0000_0000,
173                     size: 0x1000_0000,
174                 },
175                 platform_mmio: None,
176                 first_irq: 5,
177             },
178             None,
179             &[],
180         )
181         .unwrap();
182         let mut device = StubPciDevice::new(&CONFIG);
183 
184         assert!(device.allocate_address(&mut allocator).is_ok());
185         assert!(allocator.release_pci(0xa, 0xb, 1));
186     }
187 }
188