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