xref: /cloud-hypervisor/pci/src/configuration.rs (revision a007b750ffc61c7c7f867bf3013de0ca900ed81d)
1e8308dd1SSamuel Ortiz // Copyright 2018 The Chromium OS Authors. All rights reserved.
2e8308dd1SSamuel Ortiz // Use of this source code is governed by a BSD-style license that can be
3040ea543SSamuel Ortiz // found in the LICENSE-BSD-3-Clause file.
45e9886bbSRuslan Mstoi //
55e9886bbSRuslan Mstoi // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
6e8308dd1SSamuel Ortiz 
7e1701f11SSebastien Boeuf use std::sync::{Arc, Mutex};
888a9f799SRob Bradford 
988a9f799SRob Bradford use byteorder::{ByteOrder, LittleEndian};
1088a9f799SRob Bradford use serde::{Deserialize, Serialize};
11*a007b750SPhilipp Schuster use thiserror::Error;
1211e9f433SSebastien Boeuf use vm_device::PciBarType;
1310ab87d6SRob Bradford use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
14e8308dd1SSamuel Ortiz 
1588a9f799SRob Bradford use crate::device::BarReprogrammingParams;
1688a9f799SRob Bradford use crate::{MsixConfig, PciInterruptPin};
1788a9f799SRob Bradford 
184cf89d37SQiu Wenbo // The number of 32bit registers in the config space, 4096 bytes.
194cf89d37SQiu Wenbo const NUM_CONFIGURATION_REGISTERS: usize = 1024;
20e8308dd1SSamuel Ortiz 
218da7c13eSBo Chen pub(crate) const COMMAND_REG: usize = 1;
228da7c13eSBo Chen pub(crate) const COMMAND_REG_MEMORY_SPACE_MASK: u32 = 0x0000_0002;
23e8308dd1SSamuel Ortiz const STATUS_REG: usize = 1;
24e8308dd1SSamuel Ortiz const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
25e8308dd1SSamuel Ortiz const BAR0_REG: usize = 4;
26d217089bSSebastien Boeuf const ROM_BAR_REG: usize = 12;
27da95c0d7SSebastien Boeuf const ROM_BAR_IDX: usize = 6;
28e8308dd1SSamuel Ortiz const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
29e8308dd1SSamuel Ortiz const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
30d217089bSSebastien Boeuf const ROM_BAR_ADDR_MASK: u32 = 0xffff_f800;
3123fb4fa2SSebastien Boeuf const MSI_CAPABILITY_REGISTER_MASK: u32 = 0x0071_0000;
32a116add9SRob Bradford const MSIX_CAPABILITY_REGISTER_MASK: u32 = 0xc000_0000;
33e8308dd1SSamuel Ortiz const NUM_BAR_REGS: usize = 6;
34e8308dd1SSamuel Ortiz const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
35e8308dd1SSamuel Ortiz const FIRST_CAPABILITY_OFFSET: usize = 0x40;
36e8308dd1SSamuel Ortiz const CAPABILITY_MAX_OFFSET: usize = 192;
37e8308dd1SSamuel Ortiz 
38e8308dd1SSamuel Ortiz const INTERRUPT_LINE_PIN_REG: usize = 15;
39e8308dd1SSamuel Ortiz 
40eae80438SSebastien Boeuf pub const PCI_CONFIGURATION_ID: &str = "pci_configuration";
41eae80438SSebastien Boeuf 
42e8308dd1SSamuel Ortiz /// Represents the types of PCI headers allowed in the configuration registers.
43e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
44e8308dd1SSamuel Ortiz pub enum PciHeaderType {
45e8308dd1SSamuel Ortiz     Device,
46e8308dd1SSamuel Ortiz     Bridge,
47e8308dd1SSamuel Ortiz }
48e8308dd1SSamuel Ortiz 
49e8308dd1SSamuel Ortiz /// Classes of PCI nodes.
50e8308dd1SSamuel Ortiz #[allow(dead_code)]
51e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
52e8308dd1SSamuel Ortiz pub enum PciClassCode {
53e8308dd1SSamuel Ortiz     TooOld,
54e8308dd1SSamuel Ortiz     MassStorage,
55e8308dd1SSamuel Ortiz     NetworkController,
56e8308dd1SSamuel Ortiz     DisplayController,
57e8308dd1SSamuel Ortiz     MultimediaController,
58e8308dd1SSamuel Ortiz     MemoryController,
59e8308dd1SSamuel Ortiz     BridgeDevice,
60e8308dd1SSamuel Ortiz     SimpleCommunicationController,
61e8308dd1SSamuel Ortiz     BaseSystemPeripheral,
62e8308dd1SSamuel Ortiz     InputDevice,
63e8308dd1SSamuel Ortiz     DockingStation,
64e8308dd1SSamuel Ortiz     Processor,
65e8308dd1SSamuel Ortiz     SerialBusController,
66e8308dd1SSamuel Ortiz     WirelessController,
67e8308dd1SSamuel Ortiz     IntelligentIoController,
68e8308dd1SSamuel Ortiz     EncryptionController,
69e8308dd1SSamuel Ortiz     DataAcquisitionSignalProcessing,
70e8308dd1SSamuel Ortiz     Other = 0xff,
71e8308dd1SSamuel Ortiz }
72e8308dd1SSamuel Ortiz 
73e8308dd1SSamuel Ortiz impl PciClassCode {
get_register_value(self) -> u874e8308dd1SSamuel Ortiz     pub fn get_register_value(self) -> u8 {
75e8308dd1SSamuel Ortiz         self as u8
76e8308dd1SSamuel Ortiz     }
77e8308dd1SSamuel Ortiz }
78e8308dd1SSamuel Ortiz 
795c3f4dbeSJosh Soref /// A PCI subclass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait
80e8308dd1SSamuel Ortiz /// is implemented by each subclass. It allows use of a trait object to generate configurations.
81e8308dd1SSamuel Ortiz pub trait PciSubclass {
82e8308dd1SSamuel Ortiz     /// Convert this subclass to the value used in the PCI specification.
get_register_value(&self) -> u883e8308dd1SSamuel Ortiz     fn get_register_value(&self) -> u8;
84e8308dd1SSamuel Ortiz }
85e8308dd1SSamuel Ortiz 
86e8308dd1SSamuel Ortiz /// Subclasses of the MultimediaController class.
87e8308dd1SSamuel Ortiz #[allow(dead_code)]
88e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
89e8308dd1SSamuel Ortiz pub enum PciMultimediaSubclass {
90e8308dd1SSamuel Ortiz     VideoController = 0x00,
91e8308dd1SSamuel Ortiz     AudioController = 0x01,
92e8308dd1SSamuel Ortiz     TelephonyDevice = 0x02,
93e8308dd1SSamuel Ortiz     AudioDevice = 0x03,
94e8308dd1SSamuel Ortiz     Other = 0x80,
95e8308dd1SSamuel Ortiz }
96e8308dd1SSamuel Ortiz 
97e8308dd1SSamuel Ortiz impl PciSubclass for PciMultimediaSubclass {
get_register_value(&self) -> u898e8308dd1SSamuel Ortiz     fn get_register_value(&self) -> u8 {
99e8308dd1SSamuel Ortiz         *self as u8
100e8308dd1SSamuel Ortiz     }
101e8308dd1SSamuel Ortiz }
102e8308dd1SSamuel Ortiz 
103e8308dd1SSamuel Ortiz /// Subclasses of the BridgeDevice
104e8308dd1SSamuel Ortiz #[allow(dead_code)]
105e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
106e8308dd1SSamuel Ortiz pub enum PciBridgeSubclass {
107e8308dd1SSamuel Ortiz     HostBridge = 0x00,
108e8308dd1SSamuel Ortiz     IsaBridge = 0x01,
109e8308dd1SSamuel Ortiz     EisaBridge = 0x02,
110e8308dd1SSamuel Ortiz     McaBridge = 0x03,
111e8308dd1SSamuel Ortiz     PciToPciBridge = 0x04,
112e8308dd1SSamuel Ortiz     PcmciaBridge = 0x05,
113e8308dd1SSamuel Ortiz     NuBusBridge = 0x06,
114e8308dd1SSamuel Ortiz     CardBusBridge = 0x07,
115827229d8SRob Bradford     RacEwayBridge = 0x08,
116e8308dd1SSamuel Ortiz     PciToPciSemiTransparentBridge = 0x09,
117e8308dd1SSamuel Ortiz     InfiniBrandToPciHostBridge = 0x0a,
118e8308dd1SSamuel Ortiz     OtherBridgeDevice = 0x80,
119e8308dd1SSamuel Ortiz }
120e8308dd1SSamuel Ortiz 
121e8308dd1SSamuel Ortiz impl PciSubclass for PciBridgeSubclass {
get_register_value(&self) -> u8122e8308dd1SSamuel Ortiz     fn get_register_value(&self) -> u8 {
123e8308dd1SSamuel Ortiz         *self as u8
124e8308dd1SSamuel Ortiz     }
125e8308dd1SSamuel Ortiz }
126e8308dd1SSamuel Ortiz 
127e8308dd1SSamuel Ortiz /// Subclass of the SerialBus
128e8308dd1SSamuel Ortiz #[allow(dead_code)]
129e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
130e8308dd1SSamuel Ortiz pub enum PciSerialBusSubClass {
131e8308dd1SSamuel Ortiz     Firewire = 0x00,
132827229d8SRob Bradford     Accessbus = 0x01,
133827229d8SRob Bradford     Ssa = 0x02,
134827229d8SRob Bradford     Usb = 0x03,
135e8308dd1SSamuel Ortiz }
136e8308dd1SSamuel Ortiz 
137e8308dd1SSamuel Ortiz impl PciSubclass for PciSerialBusSubClass {
get_register_value(&self) -> u8138e8308dd1SSamuel Ortiz     fn get_register_value(&self) -> u8 {
139e8308dd1SSamuel Ortiz         *self as u8
140e8308dd1SSamuel Ortiz     }
141e8308dd1SSamuel Ortiz }
142e8308dd1SSamuel Ortiz 
1430b7fb42aSSamuel Ortiz /// Mass Storage Sub Classes
1440b7fb42aSSamuel Ortiz #[allow(dead_code)]
1450b7fb42aSSamuel Ortiz #[derive(Copy, Clone)]
1460b7fb42aSSamuel Ortiz pub enum PciMassStorageSubclass {
147827229d8SRob Bradford     ScsiStorage = 0x00,
148827229d8SRob Bradford     IdeInterface = 0x01,
1490b7fb42aSSamuel Ortiz     FloppyController = 0x02,
150827229d8SRob Bradford     IpiController = 0x03,
151827229d8SRob Bradford     RaidController = 0x04,
152827229d8SRob Bradford     AtaController = 0x05,
153827229d8SRob Bradford     SataController = 0x06,
154827229d8SRob Bradford     SerialScsiController = 0x07,
155827229d8SRob Bradford     NvmController = 0x08,
1560b7fb42aSSamuel Ortiz     MassStorage = 0x80,
1570b7fb42aSSamuel Ortiz }
1580b7fb42aSSamuel Ortiz 
1590b7fb42aSSamuel Ortiz impl PciSubclass for PciMassStorageSubclass {
get_register_value(&self) -> u81600b7fb42aSSamuel Ortiz     fn get_register_value(&self) -> u8 {
1610b7fb42aSSamuel Ortiz         *self as u8
1620b7fb42aSSamuel Ortiz     }
1630b7fb42aSSamuel Ortiz }
1640b7fb42aSSamuel Ortiz 
1650b7fb42aSSamuel Ortiz /// Network Controller Sub Classes
1660b7fb42aSSamuel Ortiz #[allow(dead_code)]
1670b7fb42aSSamuel Ortiz #[derive(Copy, Clone)]
1680b7fb42aSSamuel Ortiz pub enum PciNetworkControllerSubclass {
1690b7fb42aSSamuel Ortiz     EthernetController = 0x00,
1700b7fb42aSSamuel Ortiz     TokenRingController = 0x01,
171827229d8SRob Bradford     FddiController = 0x02,
172827229d8SRob Bradford     AtmController = 0x03,
173827229d8SRob Bradford     IsdnController = 0x04,
1740b7fb42aSSamuel Ortiz     WorldFipController = 0x05,
175827229d8SRob Bradford     PicmgController = 0x06,
1760b7fb42aSSamuel Ortiz     InfinibandController = 0x07,
1770b7fb42aSSamuel Ortiz     FabricController = 0x08,
1780b7fb42aSSamuel Ortiz     NetworkController = 0x80,
1790b7fb42aSSamuel Ortiz }
1800b7fb42aSSamuel Ortiz 
1810b7fb42aSSamuel Ortiz impl PciSubclass for PciNetworkControllerSubclass {
get_register_value(&self) -> u81820b7fb42aSSamuel Ortiz     fn get_register_value(&self) -> u8 {
1830b7fb42aSSamuel Ortiz         *self as u8
1840b7fb42aSSamuel Ortiz     }
1850b7fb42aSSamuel Ortiz }
1860b7fb42aSSamuel Ortiz 
18760c8a72eSBo Chen /// Trait to define a PCI class programming interface
18860c8a72eSBo Chen ///
18960c8a72eSBo Chen /// Each combination of `PciClassCode` and `PciSubclass` can specify a
19060c8a72eSBo Chen /// set of register-level programming interfaces.
191e8308dd1SSamuel Ortiz /// This trait is implemented by each programming interface.
192e8308dd1SSamuel Ortiz /// It allows use of a trait object to generate configurations.
193e8308dd1SSamuel Ortiz pub trait PciProgrammingInterface {
194e8308dd1SSamuel Ortiz     /// Convert this programming interface to the value used in the PCI specification.
get_register_value(&self) -> u8195e8308dd1SSamuel Ortiz     fn get_register_value(&self) -> u8;
196e8308dd1SSamuel Ortiz }
197e8308dd1SSamuel Ortiz 
198e8308dd1SSamuel Ortiz /// Types of PCI capabilities.
1992716bc33SRob Bradford #[derive(PartialEq, Eq, Copy, Clone)]
20029878956SSamuel Ortiz #[allow(dead_code)]
20129878956SSamuel Ortiz #[allow(non_camel_case_types)]
202e45e3df6SSebastien Boeuf #[repr(u8)]
203827229d8SRob Bradford pub enum PciCapabilityId {
204827229d8SRob Bradford     ListId = 0,
205e8308dd1SSamuel Ortiz     PowerManagement = 0x01,
206e8308dd1SSamuel Ortiz     AcceleratedGraphicsPort = 0x02,
207e8308dd1SSamuel Ortiz     VitalProductData = 0x03,
208e8308dd1SSamuel Ortiz     SlotIdentification = 0x04,
209e8308dd1SSamuel Ortiz     MessageSignalledInterrupts = 0x05,
210827229d8SRob Bradford     CompactPciHotSwap = 0x06,
211827229d8SRob Bradford     PciX = 0x07,
212e8308dd1SSamuel Ortiz     HyperTransport = 0x08,
213e8308dd1SSamuel Ortiz     VendorSpecific = 0x09,
214e8308dd1SSamuel Ortiz     Debugport = 0x0A,
215827229d8SRob Bradford     CompactPciCentralResourceControl = 0x0B,
216827229d8SRob Bradford     PciStandardHotPlugController = 0x0C,
217827229d8SRob Bradford     BridgeSubsystemVendorDeviceId = 0x0D,
218827229d8SRob Bradford     AgpTargetPciPcibridge = 0x0E,
219e8308dd1SSamuel Ortiz     SecureDevice = 0x0F,
220827229d8SRob Bradford     PciExpress = 0x10,
221827229d8SRob Bradford     MsiX = 0x11,
222827229d8SRob Bradford     SataDataIndexConf = 0x12,
223827229d8SRob Bradford     PciAdvancedFeatures = 0x13,
224827229d8SRob Bradford     PciEnhancedAllocation = 0x14,
225e8308dd1SSamuel Ortiz }
226e8308dd1SSamuel Ortiz 
227827229d8SRob Bradford impl From<u8> for PciCapabilityId {
from(c: u8) -> Self22829878956SSamuel Ortiz     fn from(c: u8) -> Self {
22929878956SSamuel Ortiz         match c {
230827229d8SRob Bradford             0 => PciCapabilityId::ListId,
231827229d8SRob Bradford             0x01 => PciCapabilityId::PowerManagement,
232827229d8SRob Bradford             0x02 => PciCapabilityId::AcceleratedGraphicsPort,
233827229d8SRob Bradford             0x03 => PciCapabilityId::VitalProductData,
234827229d8SRob Bradford             0x04 => PciCapabilityId::SlotIdentification,
235827229d8SRob Bradford             0x05 => PciCapabilityId::MessageSignalledInterrupts,
236827229d8SRob Bradford             0x06 => PciCapabilityId::CompactPciHotSwap,
237827229d8SRob Bradford             0x07 => PciCapabilityId::PciX,
238827229d8SRob Bradford             0x08 => PciCapabilityId::HyperTransport,
239827229d8SRob Bradford             0x09 => PciCapabilityId::VendorSpecific,
240827229d8SRob Bradford             0x0A => PciCapabilityId::Debugport,
241827229d8SRob Bradford             0x0B => PciCapabilityId::CompactPciCentralResourceControl,
242827229d8SRob Bradford             0x0C => PciCapabilityId::PciStandardHotPlugController,
243827229d8SRob Bradford             0x0D => PciCapabilityId::BridgeSubsystemVendorDeviceId,
244827229d8SRob Bradford             0x0E => PciCapabilityId::AgpTargetPciPcibridge,
245827229d8SRob Bradford             0x0F => PciCapabilityId::SecureDevice,
246827229d8SRob Bradford             0x10 => PciCapabilityId::PciExpress,
247827229d8SRob Bradford             0x11 => PciCapabilityId::MsiX,
248827229d8SRob Bradford             0x12 => PciCapabilityId::SataDataIndexConf,
249827229d8SRob Bradford             0x13 => PciCapabilityId::PciAdvancedFeatures,
250827229d8SRob Bradford             0x14 => PciCapabilityId::PciEnhancedAllocation,
251827229d8SRob Bradford             _ => PciCapabilityId::ListId,
25229878956SSamuel Ortiz         }
25329878956SSamuel Ortiz     }
25429878956SSamuel Ortiz }
25529878956SSamuel Ortiz 
256e45e3df6SSebastien Boeuf /// Types of PCI Express capabilities.
257e45e3df6SSebastien Boeuf #[derive(PartialEq, Eq, Copy, Clone, Debug)]
258e45e3df6SSebastien Boeuf #[allow(dead_code)]
259e45e3df6SSebastien Boeuf #[repr(u16)]
260e45e3df6SSebastien Boeuf pub enum PciExpressCapabilityId {
261e45e3df6SSebastien Boeuf     NullCapability = 0x0000,
262e45e3df6SSebastien Boeuf     AdvancedErrorReporting = 0x0001,
263e45e3df6SSebastien Boeuf     VirtualChannelMultiFunctionVirtualChannelNotPresent = 0x0002,
264e45e3df6SSebastien Boeuf     DeviceSerialNumber = 0x0003,
265e45e3df6SSebastien Boeuf     PowerBudgeting = 0x0004,
266e45e3df6SSebastien Boeuf     RootComplexLinkDeclaration = 0x0005,
267e45e3df6SSebastien Boeuf     RootComplexInternalLinkControl = 0x0006,
268e45e3df6SSebastien Boeuf     RootComplexEventCollectorEndpointAssociation = 0x0007,
269e45e3df6SSebastien Boeuf     MultiFunctionVirtualChannel = 0x0008,
270e45e3df6SSebastien Boeuf     VirtualChannelMultiFunctionVirtualChannelPresent = 0x0009,
271e45e3df6SSebastien Boeuf     RootComplexRegisterBlock = 0x000a,
272e45e3df6SSebastien Boeuf     VendorSpecificExtendedCapability = 0x000b,
273e45e3df6SSebastien Boeuf     ConfigurationAccessCorrelation = 0x000c,
274e45e3df6SSebastien Boeuf     AccessControlServices = 0x000d,
2757bf0cc1eSPhilipp Schuster     AlternativeRoutingIdentificationInterpretation = 0x000e,
276e45e3df6SSebastien Boeuf     AddressTranslationServices = 0x000f,
277e45e3df6SSebastien Boeuf     SingleRootIoVirtualization = 0x0010,
2787bf0cc1eSPhilipp Schuster     DeprecatedMultiRootIoVirtualization = 0x0011,
279e45e3df6SSebastien Boeuf     Multicast = 0x0012,
280e45e3df6SSebastien Boeuf     PageRequestInterface = 0x0013,
281e45e3df6SSebastien Boeuf     ReservedForAmd = 0x0014,
282e45e3df6SSebastien Boeuf     ResizeableBar = 0x0015,
283e45e3df6SSebastien Boeuf     DynamicPowerAllocation = 0x0016,
284e45e3df6SSebastien Boeuf     ThpRequester = 0x0017,
285e45e3df6SSebastien Boeuf     LatencyToleranceReporting = 0x0018,
286e45e3df6SSebastien Boeuf     SecondaryPciExpress = 0x0019,
287e45e3df6SSebastien Boeuf     ProtocolMultiplexing = 0x001a,
288e45e3df6SSebastien Boeuf     ProcessAddressSpaceId = 0x001b,
2897bf0cc1eSPhilipp Schuster     LnRequester = 0x001c,
290e45e3df6SSebastien Boeuf     DownstreamPortContainment = 0x001d,
291e45e3df6SSebastien Boeuf     L1PmSubstates = 0x001e,
292e45e3df6SSebastien Boeuf     PrecisionTimeMeasurement = 0x001f,
293e45e3df6SSebastien Boeuf     PciExpressOverMphy = 0x0020,
294e45e3df6SSebastien Boeuf     FRSQueueing = 0x0021,
295e45e3df6SSebastien Boeuf     ReadinessTimeReporting = 0x0022,
296e45e3df6SSebastien Boeuf     DesignatedVendorSpecificExtendedCapability = 0x0023,
297e45e3df6SSebastien Boeuf     VfResizeableBar = 0x0024,
298e45e3df6SSebastien Boeuf     DataLinkFeature = 0x0025,
299e45e3df6SSebastien Boeuf     PhysicalLayerSixteenGts = 0x0026,
30042e9632cSJosh Soref     LaneMarginingAtTheReceiver = 0x0027,
301e45e3df6SSebastien Boeuf     HierarchyId = 0x0028,
302e45e3df6SSebastien Boeuf     NativePcieEnclosureManagement = 0x0029,
303e45e3df6SSebastien Boeuf     PhysicalLayerThirtyTwoGts = 0x002a,
304e45e3df6SSebastien Boeuf     AlternateProtocol = 0x002b,
305e45e3df6SSebastien Boeuf     SystemFirmwareIntermediary = 0x002c,
306e45e3df6SSebastien Boeuf     ShadowFunctions = 0x002d,
307e45e3df6SSebastien Boeuf     DataObjectExchange = 0x002e,
308e45e3df6SSebastien Boeuf     Reserved = 0x002f,
309e45e3df6SSebastien Boeuf     ExtendedCapabilitiesAbsence = 0xffff,
310e45e3df6SSebastien Boeuf }
311e45e3df6SSebastien Boeuf 
312e45e3df6SSebastien Boeuf impl From<u16> for PciExpressCapabilityId {
from(c: u16) -> Self313e45e3df6SSebastien Boeuf     fn from(c: u16) -> Self {
314e45e3df6SSebastien Boeuf         match c {
315e45e3df6SSebastien Boeuf             0x0000 => PciExpressCapabilityId::NullCapability,
316e45e3df6SSebastien Boeuf             0x0001 => PciExpressCapabilityId::AdvancedErrorReporting,
317e45e3df6SSebastien Boeuf             0x0002 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelNotPresent,
318e45e3df6SSebastien Boeuf             0x0003 => PciExpressCapabilityId::DeviceSerialNumber,
319e45e3df6SSebastien Boeuf             0x0004 => PciExpressCapabilityId::PowerBudgeting,
320e45e3df6SSebastien Boeuf             0x0005 => PciExpressCapabilityId::RootComplexLinkDeclaration,
321e45e3df6SSebastien Boeuf             0x0006 => PciExpressCapabilityId::RootComplexInternalLinkControl,
322e45e3df6SSebastien Boeuf             0x0007 => PciExpressCapabilityId::RootComplexEventCollectorEndpointAssociation,
323e45e3df6SSebastien Boeuf             0x0008 => PciExpressCapabilityId::MultiFunctionVirtualChannel,
324e45e3df6SSebastien Boeuf             0x0009 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelPresent,
325e45e3df6SSebastien Boeuf             0x000a => PciExpressCapabilityId::RootComplexRegisterBlock,
326e45e3df6SSebastien Boeuf             0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
327e45e3df6SSebastien Boeuf             0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
328e45e3df6SSebastien Boeuf             0x000d => PciExpressCapabilityId::AccessControlServices,
3297bf0cc1eSPhilipp Schuster             0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationInterpretation,
330e45e3df6SSebastien Boeuf             0x000f => PciExpressCapabilityId::AddressTranslationServices,
331e45e3df6SSebastien Boeuf             0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
3327bf0cc1eSPhilipp Schuster             0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualization,
333e45e3df6SSebastien Boeuf             0x0012 => PciExpressCapabilityId::Multicast,
334e45e3df6SSebastien Boeuf             0x0013 => PciExpressCapabilityId::PageRequestInterface,
335e45e3df6SSebastien Boeuf             0x0014 => PciExpressCapabilityId::ReservedForAmd,
336e45e3df6SSebastien Boeuf             0x0015 => PciExpressCapabilityId::ResizeableBar,
337e45e3df6SSebastien Boeuf             0x0016 => PciExpressCapabilityId::DynamicPowerAllocation,
338e45e3df6SSebastien Boeuf             0x0017 => PciExpressCapabilityId::ThpRequester,
339e45e3df6SSebastien Boeuf             0x0018 => PciExpressCapabilityId::LatencyToleranceReporting,
340e45e3df6SSebastien Boeuf             0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
341e45e3df6SSebastien Boeuf             0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
342e45e3df6SSebastien Boeuf             0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
3437bf0cc1eSPhilipp Schuster             0x001c => PciExpressCapabilityId::LnRequester,
344e45e3df6SSebastien Boeuf             0x001d => PciExpressCapabilityId::DownstreamPortContainment,
345e45e3df6SSebastien Boeuf             0x001e => PciExpressCapabilityId::L1PmSubstates,
346e45e3df6SSebastien Boeuf             0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
347e45e3df6SSebastien Boeuf             0x0020 => PciExpressCapabilityId::PciExpressOverMphy,
348e45e3df6SSebastien Boeuf             0x0021 => PciExpressCapabilityId::FRSQueueing,
349e45e3df6SSebastien Boeuf             0x0022 => PciExpressCapabilityId::ReadinessTimeReporting,
350e45e3df6SSebastien Boeuf             0x0023 => PciExpressCapabilityId::DesignatedVendorSpecificExtendedCapability,
351e45e3df6SSebastien Boeuf             0x0024 => PciExpressCapabilityId::VfResizeableBar,
352e45e3df6SSebastien Boeuf             0x0025 => PciExpressCapabilityId::DataLinkFeature,
353e45e3df6SSebastien Boeuf             0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
35442e9632cSJosh Soref             0x0027 => PciExpressCapabilityId::LaneMarginingAtTheReceiver,
355e45e3df6SSebastien Boeuf             0x0028 => PciExpressCapabilityId::HierarchyId,
356e45e3df6SSebastien Boeuf             0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
357e45e3df6SSebastien Boeuf             0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
358e45e3df6SSebastien Boeuf             0x002b => PciExpressCapabilityId::AlternateProtocol,
359e45e3df6SSebastien Boeuf             0x002c => PciExpressCapabilityId::SystemFirmwareIntermediary,
360e45e3df6SSebastien Boeuf             0x002d => PciExpressCapabilityId::ShadowFunctions,
361e45e3df6SSebastien Boeuf             0x002e => PciExpressCapabilityId::DataObjectExchange,
362e45e3df6SSebastien Boeuf             0xffff => PciExpressCapabilityId::ExtendedCapabilitiesAbsence,
363e45e3df6SSebastien Boeuf             _ => PciExpressCapabilityId::Reserved,
364e45e3df6SSebastien Boeuf         }
365e45e3df6SSebastien Boeuf     }
366e45e3df6SSebastien Boeuf }
367e45e3df6SSebastien Boeuf 
368e8308dd1SSamuel Ortiz /// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
369e8308dd1SSamuel Ortiz pub trait PciCapability {
bytes(&self) -> &[u8]370e8308dd1SSamuel Ortiz     fn bytes(&self) -> &[u8];
id(&self) -> PciCapabilityId371827229d8SRob Bradford     fn id(&self) -> PciCapabilityId;
372e8308dd1SSamuel Ortiz }
373e8308dd1SSamuel Ortiz 
encode_32_bits_bar_size(bar_size: u32) -> Option<u32>374b8cfdab8SRob Bradford fn encode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
375b8cfdab8SRob Bradford     if bar_size > 0 {
376b8cfdab8SRob Bradford         return Some(!(bar_size - 1));
377b8cfdab8SRob Bradford     }
378b8cfdab8SRob Bradford     None
379b8cfdab8SRob Bradford }
380b8cfdab8SRob Bradford 
decode_32_bits_bar_size(bar_size: u32) -> Option<u32>381b8cfdab8SRob Bradford fn decode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
382b8cfdab8SRob Bradford     if bar_size > 0 {
383b8cfdab8SRob Bradford         return Some(!bar_size + 1);
384b8cfdab8SRob Bradford     }
385b8cfdab8SRob Bradford     None
386b8cfdab8SRob Bradford }
387b8cfdab8SRob Bradford 
encode_64_bits_bar_size(bar_size: u64) -> Option<(u32, u32)>388b8cfdab8SRob Bradford fn encode_64_bits_bar_size(bar_size: u64) -> Option<(u32, u32)> {
389b8cfdab8SRob Bradford     if bar_size > 0 {
390b8cfdab8SRob Bradford         let result = !(bar_size - 1);
391b8cfdab8SRob Bradford         let result_hi = (result >> 32) as u32;
392b8cfdab8SRob Bradford         let result_lo = (result & 0xffff_ffff) as u32;
393b8cfdab8SRob Bradford         return Some((result_hi, result_lo));
394b8cfdab8SRob Bradford     }
395b8cfdab8SRob Bradford     None
396b8cfdab8SRob Bradford }
397b8cfdab8SRob Bradford 
decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64>398b8cfdab8SRob Bradford fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64> {
399b8cfdab8SRob Bradford     let bar_size: u64 = ((bar_size_hi as u64) << 32) | (bar_size_lo as u64);
400b8cfdab8SRob Bradford     if bar_size > 0 {
401b8cfdab8SRob Bradford         return Some(!bar_size + 1);
402b8cfdab8SRob Bradford     }
403b8cfdab8SRob Bradford     None
404b8cfdab8SRob Bradford }
405b8cfdab8SRob Bradford 
40610ab87d6SRob Bradford #[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
407dc55e459SRob Bradford struct PciBar {
408dc55e459SRob Bradford     addr: u32,
409dc55e459SRob Bradford     size: u32,
410dc55e459SRob Bradford     used: bool,
411dc55e459SRob Bradford     r#type: Option<PciBarRegionType>,
412dc55e459SRob Bradford }
413dc55e459SRob Bradford 
41410ab87d6SRob Bradford #[derive(Serialize, Deserialize)]
415eae80438SSebastien Boeuf pub struct PciConfigurationState {
416e1701f11SSebastien Boeuf     registers: Vec<u32>,
417e1701f11SSebastien Boeuf     writable_bits: Vec<u32>,
418dc55e459SRob Bradford     bars: Vec<PciBar>,
419e1701f11SSebastien Boeuf     rom_bar_addr: u32,
420e1701f11SSebastien Boeuf     rom_bar_size: u32,
421e1701f11SSebastien Boeuf     rom_bar_used: bool,
422e1701f11SSebastien Boeuf     last_capability: Option<(usize, usize)>,
423e1701f11SSebastien Boeuf     msix_cap_reg_idx: Option<usize>,
424e1701f11SSebastien Boeuf }
425e1701f11SSebastien Boeuf 
426e8308dd1SSamuel Ortiz /// Contains the configuration space of a PCI node.
42760c8a72eSBo Chen ///
428e8308dd1SSamuel Ortiz /// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space).
429e8308dd1SSamuel Ortiz /// The configuration space is accessed with DWORD reads and writes from the guest.
430e8308dd1SSamuel Ortiz pub struct PciConfiguration {
431e8308dd1SSamuel Ortiz     registers: [u32; NUM_CONFIGURATION_REGISTERS],
432e8308dd1SSamuel Ortiz     writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
433dc55e459SRob Bradford     bars: [PciBar; NUM_BAR_REGS],
434149b61b2SSebastien Boeuf     rom_bar_addr: u32,
435d217089bSSebastien Boeuf     rom_bar_size: u32,
436d217089bSSebastien Boeuf     rom_bar_used: bool,
437e8308dd1SSamuel Ortiz     // Contains the byte offset and size of the last capability.
438e8308dd1SSamuel Ortiz     last_capability: Option<(usize, usize)>,
4394d98dcb0SSebastien Boeuf     msix_cap_reg_idx: Option<usize>,
4404d98dcb0SSebastien Boeuf     msix_config: Option<Arc<Mutex<MsixConfig>>>,
441aaf86ef2SBo Chen     pending_bar_reprogram: Vec<BarReprogrammingParams>,
442e8308dd1SSamuel Ortiz }
443e8308dd1SSamuel Ortiz 
444e8308dd1SSamuel Ortiz /// See pci_regs.h in kernel
44510ab87d6SRob Bradford #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
446e8308dd1SSamuel Ortiz pub enum PciBarRegionType {
447e8308dd1SSamuel Ortiz     Memory32BitRegion = 0,
448827229d8SRob Bradford     IoRegion = 0x01,
449e8308dd1SSamuel Ortiz     Memory64BitRegion = 0x04,
450e8308dd1SSamuel Ortiz }
451e8308dd1SSamuel Ortiz 
45211e9f433SSebastien Boeuf impl From<PciBarType> for PciBarRegionType {
from(type_: PciBarType) -> Self45311e9f433SSebastien Boeuf     fn from(type_: PciBarType) -> Self {
45411e9f433SSebastien Boeuf         match type_ {
45511e9f433SSebastien Boeuf             PciBarType::Io => PciBarRegionType::IoRegion,
45611e9f433SSebastien Boeuf             PciBarType::Mmio32 => PciBarRegionType::Memory32BitRegion,
45711e9f433SSebastien Boeuf             PciBarType::Mmio64 => PciBarRegionType::Memory64BitRegion,
45811e9f433SSebastien Boeuf         }
45911e9f433SSebastien Boeuf     }
46011e9f433SSebastien Boeuf }
46111e9f433SSebastien Boeuf 
4625b51024eSRavi kumar Veeramally impl From<PciBarRegionType> for PciBarType {
from(val: PciBarRegionType) -> Self4635b51024eSRavi kumar Veeramally     fn from(val: PciBarRegionType) -> Self {
4645b51024eSRavi kumar Veeramally         match val {
46511e9f433SSebastien Boeuf             PciBarRegionType::IoRegion => PciBarType::Io,
46611e9f433SSebastien Boeuf             PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
46711e9f433SSebastien Boeuf             PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
46811e9f433SSebastien Boeuf         }
46911e9f433SSebastien Boeuf     }
47011e9f433SSebastien Boeuf }
47111e9f433SSebastien Boeuf 
472e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
473e8308dd1SSamuel Ortiz pub enum PciBarPrefetchable {
474e8308dd1SSamuel Ortiz     NotPrefetchable = 0,
475e8308dd1SSamuel Ortiz     Prefetchable = 0x08,
476e8308dd1SSamuel Ortiz }
477e8308dd1SSamuel Ortiz 
4785b51024eSRavi kumar Veeramally impl From<PciBarPrefetchable> for bool {
from(val: PciBarPrefetchable) -> Self4795b51024eSRavi kumar Veeramally     fn from(val: PciBarPrefetchable) -> Self {
4805b51024eSRavi kumar Veeramally         match val {
48111e9f433SSebastien Boeuf             PciBarPrefetchable::NotPrefetchable => false,
48211e9f433SSebastien Boeuf             PciBarPrefetchable::Prefetchable => true,
48311e9f433SSebastien Boeuf         }
48411e9f433SSebastien Boeuf     }
48511e9f433SSebastien Boeuf }
48611e9f433SSebastien Boeuf 
487e8308dd1SSamuel Ortiz #[derive(Copy, Clone)]
488e8308dd1SSamuel Ortiz pub struct PciBarConfiguration {
489e8308dd1SSamuel Ortiz     addr: u64,
490e8308dd1SSamuel Ortiz     size: u64,
491da95c0d7SSebastien Boeuf     idx: usize,
492e8308dd1SSamuel Ortiz     region_type: PciBarRegionType,
493e8308dd1SSamuel Ortiz     prefetchable: PciBarPrefetchable,
494e8308dd1SSamuel Ortiz }
495e8308dd1SSamuel Ortiz 
496*a007b750SPhilipp Schuster #[derive(Error, Debug)]
497e8308dd1SSamuel Ortiz pub enum Error {
498*a007b750SPhilipp Schuster     #[error("address {0} size {1} too big")]
499e8308dd1SSamuel Ortiz     BarAddressInvalid(u64, u64),
500*a007b750SPhilipp Schuster     #[error("bar {0} already used")]
501e8308dd1SSamuel Ortiz     BarInUse(usize),
502*a007b750SPhilipp Schuster     #[error("64bit bar {0} already used (requires two regs)")]
503e8308dd1SSamuel Ortiz     BarInUse64(usize),
504*a007b750SPhilipp Schuster     #[error("bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
505e8308dd1SSamuel Ortiz     BarInvalid(usize),
506*a007b750SPhilipp Schuster     #[error("64bitbar {0} invalid, requires two regs, max {max}", max = NUM_BAR_REGS - 1)]
507e8308dd1SSamuel Ortiz     BarInvalid64(usize),
508*a007b750SPhilipp Schuster     #[error("bar address {0} not a power of two")]
509e8308dd1SSamuel Ortiz     BarSizeInvalid(u64),
510*a007b750SPhilipp Schuster     #[error("empty capabilities are invalid")]
511e8308dd1SSamuel Ortiz     CapabilityEmpty,
512*a007b750SPhilipp Schuster     #[error("Invalid capability length {0}")]
513e8308dd1SSamuel Ortiz     CapabilityLengthInvalid(usize),
514*a007b750SPhilipp Schuster     #[error("capability of size {0} doesn't fit")]
515e8308dd1SSamuel Ortiz     CapabilitySpaceFull(usize),
516*a007b750SPhilipp Schuster     #[error("failed to decode 32 bits BAR size")]
517b8cfdab8SRob Bradford     Decode32BarSize,
518*a007b750SPhilipp Schuster     #[error("failed to decode 64 bits BAR size")]
519b8cfdab8SRob Bradford     Decode64BarSize,
520*a007b750SPhilipp Schuster     #[error("failed to encode 32 bits BAR size")]
521b8cfdab8SRob Bradford     Encode32BarSize,
522*a007b750SPhilipp Schuster     #[error("failed to encode 64 bits BAR size")]
523b8cfdab8SRob Bradford     Encode64BarSize,
524*a007b750SPhilipp Schuster     #[error("address {0} size {1} too big")]
525d217089bSSebastien Boeuf     RomBarAddressInvalid(u64, u64),
526*a007b750SPhilipp Schuster     #[error("rom bar {0} already used")]
527d217089bSSebastien Boeuf     RomBarInUse(usize),
528*a007b750SPhilipp Schuster     #[error("rom bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
529d217089bSSebastien Boeuf     RomBarInvalid(usize),
530*a007b750SPhilipp Schuster     #[error("rom bar address {0} not a power of two")]
531d217089bSSebastien Boeuf     RomBarSizeInvalid(u64),
532e8308dd1SSamuel Ortiz }
533e8308dd1SSamuel Ortiz pub type Result<T> = std::result::Result<T, Error>;
534e8308dd1SSamuel Ortiz 
535e8308dd1SSamuel Ortiz impl PciConfiguration {
536e8308dd1SSamuel Ortiz     #[allow(clippy::too_many_arguments)]
new( vendor_id: u16, device_id: u16, revision_id: u8, class_code: PciClassCode, subclass: &dyn PciSubclass, programming_interface: Option<&dyn PciProgrammingInterface>, header_type: PciHeaderType, subsystem_vendor_id: u16, subsystem_id: u16, msix_config: Option<Arc<Mutex<MsixConfig>>>, state: Option<PciConfigurationState>, ) -> Self537e8308dd1SSamuel Ortiz     pub fn new(
538e8308dd1SSamuel Ortiz         vendor_id: u16,
539e8308dd1SSamuel Ortiz         device_id: u16,
5409bd5ec89SRob Bradford         revision_id: u8,
541e8308dd1SSamuel Ortiz         class_code: PciClassCode,
542e8308dd1SSamuel Ortiz         subclass: &dyn PciSubclass,
543e8308dd1SSamuel Ortiz         programming_interface: Option<&dyn PciProgrammingInterface>,
544e8308dd1SSamuel Ortiz         header_type: PciHeaderType,
545e8308dd1SSamuel Ortiz         subsystem_vendor_id: u16,
546e8308dd1SSamuel Ortiz         subsystem_id: u16,
5474d98dcb0SSebastien Boeuf         msix_config: Option<Arc<Mutex<MsixConfig>>>,
548eae80438SSebastien Boeuf         state: Option<PciConfigurationState>,
549e8308dd1SSamuel Ortiz     ) -> Self {
550eae80438SSebastien Boeuf         let (
551eae80438SSebastien Boeuf             registers,
552eae80438SSebastien Boeuf             writable_bits,
553eae80438SSebastien Boeuf             bars,
554eae80438SSebastien Boeuf             rom_bar_addr,
555eae80438SSebastien Boeuf             rom_bar_size,
556eae80438SSebastien Boeuf             rom_bar_used,
557eae80438SSebastien Boeuf             last_capability,
558eae80438SSebastien Boeuf             msix_cap_reg_idx,
559eae80438SSebastien Boeuf         ) = if let Some(state) = state {
560eae80438SSebastien Boeuf             (
561eae80438SSebastien Boeuf                 state.registers.try_into().unwrap(),
562eae80438SSebastien Boeuf                 state.writable_bits.try_into().unwrap(),
563eae80438SSebastien Boeuf                 state.bars.try_into().unwrap(),
564eae80438SSebastien Boeuf                 state.rom_bar_addr,
565eae80438SSebastien Boeuf                 state.rom_bar_size,
566eae80438SSebastien Boeuf                 state.rom_bar_used,
567eae80438SSebastien Boeuf                 state.last_capability,
568eae80438SSebastien Boeuf                 state.msix_cap_reg_idx,
569eae80438SSebastien Boeuf             )
570eae80438SSebastien Boeuf         } else {
571e8308dd1SSamuel Ortiz             let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
572e8308dd1SSamuel Ortiz             let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
573b57cc3d7SRob Bradford             registers[0] = (u32::from(device_id) << 16) | u32::from(vendor_id);
574e8308dd1SSamuel Ortiz             // TODO(dverkamp): Status should be write-1-to-clear
575e8308dd1SSamuel Ortiz             writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
576e8308dd1SSamuel Ortiz             let pi = if let Some(pi) = programming_interface {
577e8308dd1SSamuel Ortiz                 pi.get_register_value()
578e8308dd1SSamuel Ortiz             } else {
579e8308dd1SSamuel Ortiz                 0
580e8308dd1SSamuel Ortiz             };
581b57cc3d7SRob Bradford             registers[2] = (u32::from(class_code.get_register_value()) << 24)
582b57cc3d7SRob Bradford                 | (u32::from(subclass.get_register_value()) << 16)
583b57cc3d7SRob Bradford                 | (u32::from(pi) << 8)
5849bd5ec89SRob Bradford                 | u32::from(revision_id);
585e8308dd1SSamuel Ortiz             writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w)
586e8308dd1SSamuel Ortiz             match header_type {
587e8308dd1SSamuel Ortiz                 PciHeaderType::Device => {
588e8308dd1SSamuel Ortiz                     registers[3] = 0x0000_0000; // Header type 0 (device)
589e8308dd1SSamuel Ortiz                     writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w)
590e8308dd1SSamuel Ortiz                 }
591e8308dd1SSamuel Ortiz                 PciHeaderType::Bridge => {
592e8308dd1SSamuel Ortiz                     registers[3] = 0x0001_0000; // Header type 1 (bridge)
593e8308dd1SSamuel Ortiz                     writable_bits[9] = 0xfff0_fff0; // Memory base and limit
594e8308dd1SSamuel Ortiz                     writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w)
595e8308dd1SSamuel Ortiz                 }
596e8308dd1SSamuel Ortiz             };
597b57cc3d7SRob Bradford             registers[11] = (u32::from(subsystem_id) << 16) | u32::from(subsystem_vendor_id);
598e8308dd1SSamuel Ortiz 
599eae80438SSebastien Boeuf             (
600eae80438SSebastien Boeuf                 registers,
601eae80438SSebastien Boeuf                 writable_bits,
602eae80438SSebastien Boeuf                 [PciBar::default(); NUM_BAR_REGS],
603eae80438SSebastien Boeuf                 0,
604eae80438SSebastien Boeuf                 0,
605eae80438SSebastien Boeuf                 false,
606eae80438SSebastien Boeuf                 None,
607eae80438SSebastien Boeuf                 None,
608eae80438SSebastien Boeuf             )
609eae80438SSebastien Boeuf         };
610dc55e459SRob Bradford 
611e8308dd1SSamuel Ortiz         PciConfiguration {
612e8308dd1SSamuel Ortiz             registers,
613e8308dd1SSamuel Ortiz             writable_bits,
614dc55e459SRob Bradford             bars,
615eae80438SSebastien Boeuf             rom_bar_addr,
616eae80438SSebastien Boeuf             rom_bar_size,
617eae80438SSebastien Boeuf             rom_bar_used,
618eae80438SSebastien Boeuf             last_capability,
619eae80438SSebastien Boeuf             msix_cap_reg_idx,
6204d98dcb0SSebastien Boeuf             msix_config,
621aaf86ef2SBo Chen             pending_bar_reprogram: Vec::new(),
622e8308dd1SSamuel Ortiz         }
623e8308dd1SSamuel Ortiz     }
624e8308dd1SSamuel Ortiz 
state(&self) -> PciConfigurationState625e1701f11SSebastien Boeuf     fn state(&self) -> PciConfigurationState {
626e1701f11SSebastien Boeuf         PciConfigurationState {
627e1701f11SSebastien Boeuf             registers: self.registers.to_vec(),
628e1701f11SSebastien Boeuf             writable_bits: self.writable_bits.to_vec(),
629dc55e459SRob Bradford             bars: self.bars.to_vec(),
630e1701f11SSebastien Boeuf             rom_bar_addr: self.rom_bar_addr,
631e1701f11SSebastien Boeuf             rom_bar_size: self.rom_bar_size,
632e1701f11SSebastien Boeuf             rom_bar_used: self.rom_bar_used,
633e1701f11SSebastien Boeuf             last_capability: self.last_capability,
634e1701f11SSebastien Boeuf             msix_cap_reg_idx: self.msix_cap_reg_idx,
635e1701f11SSebastien Boeuf         }
636e1701f11SSebastien Boeuf     }
637e1701f11SSebastien Boeuf 
638e8308dd1SSamuel Ortiz     /// Reads a 32bit register from `reg_idx` in the register map.
read_reg(&self, reg_idx: usize) -> u32639e8308dd1SSamuel Ortiz     pub fn read_reg(&self, reg_idx: usize) -> u32 {
640e8308dd1SSamuel Ortiz         *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff))
641e8308dd1SSamuel Ortiz     }
642e8308dd1SSamuel Ortiz 
643e8308dd1SSamuel Ortiz     /// Writes a 32bit register to `reg_idx` in the register map.
write_reg(&mut self, reg_idx: usize, value: u32)644e8308dd1SSamuel Ortiz     pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
645b1571816SSebastien Boeuf         let mut mask = self.writable_bits[reg_idx];
646149b61b2SSebastien Boeuf 
6476ccd32c9SRob Bradford         if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
648b1571816SSebastien Boeuf             // Handle very specific case where the BAR is being written with
64907bad79fSSebastien Boeuf             // all 1's to retrieve the BAR size during next BAR reading.
65007bad79fSSebastien Boeuf             if value == 0xffff_ffff {
651dc55e459SRob Bradford                 mask &= self.bars[reg_idx - 4].size;
65207bad79fSSebastien Boeuf             }
653149b61b2SSebastien Boeuf         } else if reg_idx == ROM_BAR_REG {
65407bad79fSSebastien Boeuf             // Handle very specific case where the BAR is being written with
65507bad79fSSebastien Boeuf             // all 1's on bits 31-11 to retrieve the BAR size during next BAR
65607bad79fSSebastien Boeuf             // reading.
65707bad79fSSebastien Boeuf             if value & ROM_BAR_ADDR_MASK == ROM_BAR_ADDR_MASK {
658b8cfdab8SRob Bradford                 mask &= self.rom_bar_size;
659b1571816SSebastien Boeuf             }
660149b61b2SSebastien Boeuf         }
661b1571816SSebastien Boeuf 
662e8308dd1SSamuel Ortiz         if let Some(r) = self.registers.get_mut(reg_idx) {
663b1571816SSebastien Boeuf             *r = (*r & !self.writable_bits[reg_idx]) | (value & mask);
664e8308dd1SSamuel Ortiz         } else {
665e8308dd1SSamuel Ortiz             warn!("bad PCI register write {}", reg_idx);
666e8308dd1SSamuel Ortiz         }
667e8308dd1SSamuel Ortiz     }
668e8308dd1SSamuel Ortiz 
669e8308dd1SSamuel Ortiz     /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned.
write_word(&mut self, offset: usize, value: u16)670e8308dd1SSamuel Ortiz     pub fn write_word(&mut self, offset: usize, value: u16) {
671e8308dd1SSamuel Ortiz         let shift = match offset % 4 {
672e8308dd1SSamuel Ortiz             0 => 0,
673e8308dd1SSamuel Ortiz             2 => 16,
674e8308dd1SSamuel Ortiz             _ => {
675e8308dd1SSamuel Ortiz                 warn!("bad PCI config write offset {}", offset);
676e8308dd1SSamuel Ortiz                 return;
677e8308dd1SSamuel Ortiz             }
678e8308dd1SSamuel Ortiz         };
679e8308dd1SSamuel Ortiz         let reg_idx = offset / 4;
680e8308dd1SSamuel Ortiz 
681e8308dd1SSamuel Ortiz         if let Some(r) = self.registers.get_mut(reg_idx) {
682e8308dd1SSamuel Ortiz             let writable_mask = self.writable_bits[reg_idx];
683e8308dd1SSamuel Ortiz             let mask = (0xffffu32 << shift) & writable_mask;
684e8308dd1SSamuel Ortiz             let shifted_value = (u32::from(value) << shift) & writable_mask;
685e8308dd1SSamuel Ortiz             *r = *r & !mask | shifted_value;
686e8308dd1SSamuel Ortiz         } else {
687e8308dd1SSamuel Ortiz             warn!("bad PCI config write offset {}", offset);
688e8308dd1SSamuel Ortiz         }
689e8308dd1SSamuel Ortiz     }
690e8308dd1SSamuel Ortiz 
691e8308dd1SSamuel Ortiz     /// Writes a byte to `offset`.
write_byte(&mut self, offset: usize, value: u8)692e8308dd1SSamuel Ortiz     pub fn write_byte(&mut self, offset: usize, value: u8) {
693e8308dd1SSamuel Ortiz         self.write_byte_internal(offset, value, true);
694e8308dd1SSamuel Ortiz     }
695e8308dd1SSamuel Ortiz 
696e8308dd1SSamuel Ortiz     /// Writes a byte to `offset`, optionally enforcing read-only bits.
write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool)697e8308dd1SSamuel Ortiz     fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) {
698e8308dd1SSamuel Ortiz         let shift = (offset % 4) * 8;
699e8308dd1SSamuel Ortiz         let reg_idx = offset / 4;
700e8308dd1SSamuel Ortiz 
701e8308dd1SSamuel Ortiz         if let Some(r) = self.registers.get_mut(reg_idx) {
702e8308dd1SSamuel Ortiz             let writable_mask = if apply_writable_mask {
703e8308dd1SSamuel Ortiz                 self.writable_bits[reg_idx]
704e8308dd1SSamuel Ortiz             } else {
705e8308dd1SSamuel Ortiz                 0xffff_ffff
706e8308dd1SSamuel Ortiz             };
707e8308dd1SSamuel Ortiz             let mask = (0xffu32 << shift) & writable_mask;
708e8308dd1SSamuel Ortiz             let shifted_value = (u32::from(value) << shift) & writable_mask;
709e8308dd1SSamuel Ortiz             *r = *r & !mask | shifted_value;
710e8308dd1SSamuel Ortiz         } else {
711e8308dd1SSamuel Ortiz             warn!("bad PCI config write offset {}", offset);
712e8308dd1SSamuel Ortiz         }
713e8308dd1SSamuel Ortiz     }
714e8308dd1SSamuel Ortiz 
715e8308dd1SSamuel Ortiz     /// Adds a region specified by `config`.  Configures the specified BAR(s) to
716e8308dd1SSamuel Ortiz     /// report this region and size to the guest kernel.  Enforces a few constraints
717da95c0d7SSebastien Boeuf     /// (i.e, region size must be power of two, register not already used).
add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()>718da95c0d7SSebastien Boeuf     pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<()> {
719da95c0d7SSebastien Boeuf         let bar_idx = config.idx;
720da95c0d7SSebastien Boeuf         let reg_idx = BAR0_REG + bar_idx;
721da95c0d7SSebastien Boeuf 
722da95c0d7SSebastien Boeuf         if self.bars[bar_idx].used {
723da95c0d7SSebastien Boeuf             return Err(Error::BarInUse(bar_idx));
724e8308dd1SSamuel Ortiz         }
725e8308dd1SSamuel Ortiz 
726b41daddcSRuoqing He         if !config.size.is_power_of_two() {
727e8308dd1SSamuel Ortiz             return Err(Error::BarSizeInvalid(config.size));
728e8308dd1SSamuel Ortiz         }
729e8308dd1SSamuel Ortiz 
730da95c0d7SSebastien Boeuf         if bar_idx >= NUM_BAR_REGS {
731da95c0d7SSebastien Boeuf             return Err(Error::BarInvalid(bar_idx));
732e8308dd1SSamuel Ortiz         }
733e8308dd1SSamuel Ortiz 
734e8308dd1SSamuel Ortiz         let end_addr = config
735e8308dd1SSamuel Ortiz             .addr
736927861ceSSebastien Boeuf             .checked_add(config.size - 1)
73772bb255fSSamuel Ortiz             .ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
738e8308dd1SSamuel Ortiz         match config.region_type {
739827229d8SRob Bradford             PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
740f6cd3bd8SWei Liu                 if end_addr > u64::from(u32::MAX) {
741e8308dd1SSamuel Ortiz                     return Err(Error::BarAddressInvalid(config.addr, config.size));
742e8308dd1SSamuel Ortiz                 }
743b8cfdab8SRob Bradford 
744b8cfdab8SRob Bradford                 // Encode the BAR size as expected by the software running in
745b8cfdab8SRob Bradford                 // the guest.
746da95c0d7SSebastien Boeuf                 self.bars[bar_idx].size =
747b8cfdab8SRob Bradford                     encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
748e8308dd1SSamuel Ortiz             }
749e8308dd1SSamuel Ortiz             PciBarRegionType::Memory64BitRegion => {
750da95c0d7SSebastien Boeuf                 if bar_idx + 1 >= NUM_BAR_REGS {
751da95c0d7SSebastien Boeuf                     return Err(Error::BarInvalid64(bar_idx));
752e8308dd1SSamuel Ortiz                 }
753e8308dd1SSamuel Ortiz 
754da95c0d7SSebastien Boeuf                 if self.bars[bar_idx + 1].used {
755da95c0d7SSebastien Boeuf                     return Err(Error::BarInUse64(bar_idx));
756e8308dd1SSamuel Ortiz                 }
757e8308dd1SSamuel Ortiz 
758b8cfdab8SRob Bradford                 // Encode the BAR size as expected by the software running in
759b8cfdab8SRob Bradford                 // the guest.
760b8cfdab8SRob Bradford                 let (bar_size_hi, bar_size_lo) =
761b8cfdab8SRob Bradford                     encode_64_bits_bar_size(config.size).ok_or(Error::Encode64BarSize)?;
762b8cfdab8SRob Bradford 
763da95c0d7SSebastien Boeuf                 self.registers[reg_idx + 1] = (config.addr >> 32) as u32;
764da95c0d7SSebastien Boeuf                 self.writable_bits[reg_idx + 1] = 0xffff_ffff;
765da95c0d7SSebastien Boeuf                 self.bars[bar_idx + 1].addr = self.registers[reg_idx + 1];
766da95c0d7SSebastien Boeuf                 self.bars[bar_idx].size = bar_size_lo;
767da95c0d7SSebastien Boeuf                 self.bars[bar_idx + 1].size = bar_size_hi;
768da95c0d7SSebastien Boeuf                 self.bars[bar_idx + 1].used = true;
769e8308dd1SSamuel Ortiz             }
770e8308dd1SSamuel Ortiz         }
771e8308dd1SSamuel Ortiz 
772e8308dd1SSamuel Ortiz         let (mask, lower_bits) = match config.region_type {
773e8308dd1SSamuel Ortiz             PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => (
774e8308dd1SSamuel Ortiz                 BAR_MEM_ADDR_MASK,
775e8308dd1SSamuel Ortiz                 config.prefetchable as u32 | config.region_type as u32,
776e8308dd1SSamuel Ortiz             ),
777827229d8SRob Bradford             PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
778e8308dd1SSamuel Ortiz         };
779e8308dd1SSamuel Ortiz 
780da95c0d7SSebastien Boeuf         self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits;
781da95c0d7SSebastien Boeuf         self.writable_bits[reg_idx] = mask;
782da95c0d7SSebastien Boeuf         self.bars[bar_idx].addr = self.registers[reg_idx];
783da95c0d7SSebastien Boeuf         self.bars[bar_idx].used = true;
784da95c0d7SSebastien Boeuf         self.bars[bar_idx].r#type = Some(config.region_type);
785da95c0d7SSebastien Boeuf 
786da95c0d7SSebastien Boeuf         Ok(())
787e8308dd1SSamuel Ortiz     }
788e8308dd1SSamuel Ortiz 
789d217089bSSebastien Boeuf     /// Adds rom expansion BAR.
add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()>790da95c0d7SSebastien Boeuf     pub fn add_pci_rom_bar(&mut self, config: &PciBarConfiguration, active: u32) -> Result<()> {
791da95c0d7SSebastien Boeuf         let bar_idx = config.idx;
792da95c0d7SSebastien Boeuf         let reg_idx = ROM_BAR_REG;
793da95c0d7SSebastien Boeuf 
794d217089bSSebastien Boeuf         if self.rom_bar_used {
795da95c0d7SSebastien Boeuf             return Err(Error::RomBarInUse(bar_idx));
796d217089bSSebastien Boeuf         }
797d217089bSSebastien Boeuf 
798b41daddcSRuoqing He         if !config.size.is_power_of_two() {
799d217089bSSebastien Boeuf             return Err(Error::RomBarSizeInvalid(config.size));
800d217089bSSebastien Boeuf         }
801d217089bSSebastien Boeuf 
802da95c0d7SSebastien Boeuf         if bar_idx != ROM_BAR_IDX {
803da95c0d7SSebastien Boeuf             return Err(Error::RomBarInvalid(bar_idx));
804d217089bSSebastien Boeuf         }
805d217089bSSebastien Boeuf 
806d217089bSSebastien Boeuf         let end_addr = config
807d217089bSSebastien Boeuf             .addr
808d217089bSSebastien Boeuf             .checked_add(config.size - 1)
80972bb255fSSamuel Ortiz             .ok_or(Error::RomBarAddressInvalid(config.addr, config.size))?;
810d217089bSSebastien Boeuf 
811f6cd3bd8SWei Liu         if end_addr > u64::from(u32::MAX) {
812d217089bSSebastien Boeuf             return Err(Error::RomBarAddressInvalid(config.addr, config.size));
813d217089bSSebastien Boeuf         }
814d217089bSSebastien Boeuf 
815da95c0d7SSebastien Boeuf         self.registers[reg_idx] = (config.addr as u32) | active;
816da95c0d7SSebastien Boeuf         self.writable_bits[reg_idx] = ROM_BAR_ADDR_MASK;
817da95c0d7SSebastien Boeuf         self.rom_bar_addr = self.registers[reg_idx];
818b8cfdab8SRob Bradford         self.rom_bar_size =
819b8cfdab8SRob Bradford             encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
820d217089bSSebastien Boeuf         self.rom_bar_used = true;
821da95c0d7SSebastien Boeuf 
822da95c0d7SSebastien Boeuf         Ok(())
823d217089bSSebastien Boeuf     }
824d217089bSSebastien Boeuf 
8254f8054faSSebastien Boeuf     /// Returns the address of the given BAR region.
get_bar_addr(&self, bar_num: usize) -> u648264f8054faSSebastien Boeuf     pub fn get_bar_addr(&self, bar_num: usize) -> u64 {
827e8308dd1SSamuel Ortiz         let bar_idx = BAR0_REG + bar_num;
828e8308dd1SSamuel Ortiz 
829dc55e459SRob Bradford         let mut addr = u64::from(self.bars[bar_num].addr & self.writable_bits[bar_idx]);
8304f8054faSSebastien Boeuf 
831dc55e459SRob Bradford         if let Some(bar_type) = self.bars[bar_num].r#type {
8324f8054faSSebastien Boeuf             if bar_type == PciBarRegionType::Memory64BitRegion {
833dc55e459SRob Bradford                 addr |= u64::from(self.bars[bar_num + 1].addr) << 32;
8344f8054faSSebastien Boeuf             }
835e8308dd1SSamuel Ortiz         }
836e8308dd1SSamuel Ortiz 
8374f8054faSSebastien Boeuf         addr
838b67e0b3dSSebastien Boeuf     }
839b67e0b3dSSebastien Boeuf 
840e8308dd1SSamuel Ortiz     /// Configures the IRQ line and pin used by this device.
set_irq(&mut self, line: u8, pin: PciInterruptPin)841e8308dd1SSamuel Ortiz     pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) {
842e8308dd1SSamuel Ortiz         // `pin` is 1-based in the pci config space.
843e8308dd1SSamuel Ortiz         let pin_idx = (pin as u32) + 1;
844e8308dd1SSamuel Ortiz         self.registers[INTERRUPT_LINE_PIN_REG] = (self.registers[INTERRUPT_LINE_PIN_REG]
845e8308dd1SSamuel Ortiz             & 0xffff_0000)
846e8308dd1SSamuel Ortiz             | (pin_idx << 8)
847e8308dd1SSamuel Ortiz             | u32::from(line);
848e8308dd1SSamuel Ortiz     }
849e8308dd1SSamuel Ortiz 
850e8308dd1SSamuel Ortiz     /// Adds the capability `cap_data` to the list of capabilities.
851e8308dd1SSamuel Ortiz     /// `cap_data` should include the two-byte PCI capability header (type, next),
852e8308dd1SSamuel Ortiz     /// but not populate it. Correct values will be generated automatically based
853e8308dd1SSamuel Ortiz     /// on `cap_data.id()`.
add_capability(&mut self, cap_data: &dyn PciCapability) -> Result<usize>854e8308dd1SSamuel Ortiz     pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result<usize> {
855e8308dd1SSamuel Ortiz         let total_len = cap_data.bytes().len();
856e8308dd1SSamuel Ortiz         // Check that the length is valid.
857e8308dd1SSamuel Ortiz         if cap_data.bytes().is_empty() {
858e8308dd1SSamuel Ortiz             return Err(Error::CapabilityEmpty);
859e8308dd1SSamuel Ortiz         }
860e8308dd1SSamuel Ortiz         let (cap_offset, tail_offset) = match self.last_capability {
861e8308dd1SSamuel Ortiz             Some((offset, len)) => (Self::next_dword(offset, len), offset + 1),
862e8308dd1SSamuel Ortiz             None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET),
863e8308dd1SSamuel Ortiz         };
864e8308dd1SSamuel Ortiz         let end_offset = cap_offset
865e8308dd1SSamuel Ortiz             .checked_add(total_len)
86672bb255fSSamuel Ortiz             .ok_or(Error::CapabilitySpaceFull(total_len))?;
867e8308dd1SSamuel Ortiz         if end_offset > CAPABILITY_MAX_OFFSET {
868e8308dd1SSamuel Ortiz             return Err(Error::CapabilitySpaceFull(total_len));
869e8308dd1SSamuel Ortiz         }
870e8308dd1SSamuel Ortiz         self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK;
871e8308dd1SSamuel Ortiz         self.write_byte_internal(tail_offset, cap_offset as u8, false);
872e8308dd1SSamuel Ortiz         self.write_byte_internal(cap_offset, cap_data.id() as u8, false);
873e8308dd1SSamuel Ortiz         self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer.
874e8308dd1SSamuel Ortiz         for (i, byte) in cap_data.bytes().iter().enumerate() {
875e8308dd1SSamuel Ortiz             self.write_byte_internal(cap_offset + i + 2, *byte, false);
876e8308dd1SSamuel Ortiz         }
877e8308dd1SSamuel Ortiz         self.last_capability = Some((cap_offset, total_len));
8784d98dcb0SSebastien Boeuf 
87923fb4fa2SSebastien Boeuf         match cap_data.id() {
88023fb4fa2SSebastien Boeuf             PciCapabilityId::MessageSignalledInterrupts => {
88123fb4fa2SSebastien Boeuf                 self.writable_bits[cap_offset / 4] = MSI_CAPABILITY_REGISTER_MASK;
88223fb4fa2SSebastien Boeuf             }
88323fb4fa2SSebastien Boeuf             PciCapabilityId::MsiX => {
8844d98dcb0SSebastien Boeuf                 self.msix_cap_reg_idx = Some(cap_offset / 4);
885a116add9SRob Bradford                 self.writable_bits[self.msix_cap_reg_idx.unwrap()] = MSIX_CAPABILITY_REGISTER_MASK;
8864d98dcb0SSebastien Boeuf             }
88723fb4fa2SSebastien Boeuf             _ => {}
88823fb4fa2SSebastien Boeuf         }
8894d98dcb0SSebastien Boeuf 
890e8308dd1SSamuel Ortiz         Ok(cap_offset)
891e8308dd1SSamuel Ortiz     }
892e8308dd1SSamuel Ortiz 
893e8308dd1SSamuel Ortiz     // Find the next aligned offset after the one given.
next_dword(offset: usize, len: usize) -> usize894e8308dd1SSamuel Ortiz     fn next_dword(offset: usize, len: usize) -> usize {
895e8308dd1SSamuel Ortiz         let next = offset + len;
896e8308dd1SSamuel Ortiz         (next + 3) & !3
897e8308dd1SSamuel Ortiz     }
898e8308dd1SSamuel Ortiz 
write_config_register( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> Vec<BarReprogrammingParams>899cb52cf91SBo Chen     pub fn write_config_register(
900cb52cf91SBo Chen         &mut self,
901cb52cf91SBo Chen         reg_idx: usize,
902cb52cf91SBo Chen         offset: u64,
903cb52cf91SBo Chen         data: &[u8],
904aaf86ef2SBo Chen     ) -> Vec<BarReprogrammingParams> {
905e8308dd1SSamuel Ortiz         if offset as usize + data.len() > 4 {
906aaf86ef2SBo Chen             return Vec::new();
907e8308dd1SSamuel Ortiz         }
908e8308dd1SSamuel Ortiz 
9094d98dcb0SSebastien Boeuf         // Handle potential write to MSI-X message control register
9104d98dcb0SSebastien Boeuf         if let Some(msix_cap_reg_idx) = self.msix_cap_reg_idx {
9114d98dcb0SSebastien Boeuf             if let Some(msix_config) = &self.msix_config {
9124d98dcb0SSebastien Boeuf                 if msix_cap_reg_idx == reg_idx && offset == 2 && data.len() == 2 {
9134d98dcb0SSebastien Boeuf                     msix_config
9144d98dcb0SSebastien Boeuf                         .lock()
9154d98dcb0SSebastien Boeuf                         .unwrap()
9164d98dcb0SSebastien Boeuf                         .set_msg_ctl(LittleEndian::read_u16(data));
9179c6e7c4aSRob Bradford                 } else if msix_cap_reg_idx == reg_idx && offset == 0 && data.len() == 4 {
9189c6e7c4aSRob Bradford                     msix_config
9199c6e7c4aSRob Bradford                         .lock()
9209c6e7c4aSRob Bradford                         .unwrap()
9219c6e7c4aSRob Bradford                         .set_msg_ctl((LittleEndian::read_u32(data) >> 16) as u16);
9224d98dcb0SSebastien Boeuf                 }
9234d98dcb0SSebastien Boeuf             }
9244d98dcb0SSebastien Boeuf         }
9254d98dcb0SSebastien Boeuf 
926e8308dd1SSamuel Ortiz         match data.len() {
927e8308dd1SSamuel Ortiz             1 => self.write_byte(reg_idx * 4 + offset as usize, data[0]),
928e8308dd1SSamuel Ortiz             2 => self.write_word(
929e8308dd1SSamuel Ortiz                 reg_idx * 4 + offset as usize,
930b57cc3d7SRob Bradford                 u16::from(data[0]) | (u16::from(data[1]) << 8),
931e8308dd1SSamuel Ortiz             ),
932e8308dd1SSamuel Ortiz             4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
933e8308dd1SSamuel Ortiz             _ => (),
934e8308dd1SSamuel Ortiz         }
935cb52cf91SBo Chen 
936aaf86ef2SBo Chen         if let Some(param) = self.detect_bar_reprogramming(reg_idx, data) {
937aaf86ef2SBo Chen             self.pending_bar_reprogram.push(param);
938aaf86ef2SBo Chen         }
939aaf86ef2SBo Chen 
940aaf86ef2SBo Chen         if !self.pending_bar_reprogram.is_empty() {
941aaf86ef2SBo Chen             // Return bar reprogramming only if the MSE bit is enabled;
942aaf86ef2SBo Chen             if self.read_config_register(COMMAND_REG) & COMMAND_REG_MEMORY_SPACE_MASK
943aaf86ef2SBo Chen                 == COMMAND_REG_MEMORY_SPACE_MASK
944aaf86ef2SBo Chen             {
945aaf86ef2SBo Chen                 info!(
946aaf86ef2SBo Chen                     "BAR reprogramming parameter is returned: {:x?}",
947aaf86ef2SBo Chen                     self.pending_bar_reprogram
948aaf86ef2SBo Chen                 );
949aaf86ef2SBo Chen                 return self.pending_bar_reprogram.drain(..).collect();
950aaf86ef2SBo Chen             } else {
951aaf86ef2SBo Chen                 info!(
952aaf86ef2SBo Chen                     "MSE bit is disabled. No BAR reprogramming parameter is returned: {:x?}",
953aaf86ef2SBo Chen                     self.pending_bar_reprogram
954aaf86ef2SBo Chen                 );
955aaf86ef2SBo Chen             }
956aaf86ef2SBo Chen         }
957aaf86ef2SBo Chen 
958aaf86ef2SBo Chen         Vec::new()
959e8308dd1SSamuel Ortiz     }
960e8308dd1SSamuel Ortiz 
read_config_register(&self, reg_idx: usize) -> u32961e8308dd1SSamuel Ortiz     pub fn read_config_register(&self, reg_idx: usize) -> u32 {
962e8308dd1SSamuel Ortiz         self.read_reg(reg_idx)
963e8308dd1SSamuel Ortiz     }
964149b61b2SSebastien Boeuf 
detect_bar_reprogramming( &mut self, reg_idx: usize, data: &[u8], ) -> Option<BarReprogrammingParams>965cb52cf91SBo Chen     fn detect_bar_reprogramming(
966149b61b2SSebastien Boeuf         &mut self,
967149b61b2SSebastien Boeuf         reg_idx: usize,
968149b61b2SSebastien Boeuf         data: &[u8],
969149b61b2SSebastien Boeuf     ) -> Option<BarReprogrammingParams> {
970149b61b2SSebastien Boeuf         if data.len() != 4 {
971149b61b2SSebastien Boeuf             return None;
972149b61b2SSebastien Boeuf         }
973149b61b2SSebastien Boeuf 
974149b61b2SSebastien Boeuf         let value = LittleEndian::read_u32(data);
975149b61b2SSebastien Boeuf 
976149b61b2SSebastien Boeuf         let mask = self.writable_bits[reg_idx];
9776ccd32c9SRob Bradford         if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
9787c457378SSebastien Boeuf             // Ignore the case where the BAR size is being asked for.
9797c457378SSebastien Boeuf             if value == 0xffff_ffff {
9807c457378SSebastien Boeuf                 return None;
9817c457378SSebastien Boeuf             }
9827c457378SSebastien Boeuf 
983149b61b2SSebastien Boeuf             let bar_idx = reg_idx - 4;
984149b61b2SSebastien Boeuf             // Handle special case where the address being written is
985149b61b2SSebastien Boeuf             // different from the address initially provided. This is a
986149b61b2SSebastien Boeuf             // BAR reprogramming case which needs to be properly caught.
987dc55e459SRob Bradford             if let Some(bar_type) = self.bars[bar_idx].r#type {
9887c457378SSebastien Boeuf                 // In case of 64 bits memory BAR, we don't do anything until
9897c457378SSebastien Boeuf                 // the upper BAR is modified, otherwise we would be moving the
9907c457378SSebastien Boeuf                 // BAR to a wrong location in memory.
9917c457378SSebastien Boeuf                 if bar_type == PciBarRegionType::Memory64BitRegion {
9927c457378SSebastien Boeuf                     return None;
9937c457378SSebastien Boeuf                 }
9947c457378SSebastien Boeuf 
9957c457378SSebastien Boeuf                 // Ignore the case where the value is unchanged.
9967c457378SSebastien Boeuf                 if (value & mask) == (self.bars[bar_idx].addr & mask) {
997c2ae3805SSebastien Boeuf                     return None;
998c2ae3805SSebastien Boeuf                 }
999c2ae3805SSebastien Boeuf 
100064e217cfSRob Bradford                 info!(
100164e217cfSRob Bradford                     "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
100259f98a2eSBo Chen                     bar_idx, self.bars[bar_idx].addr, value
1003149b61b2SSebastien Boeuf                 );
1004dc55e459SRob Bradford                 let old_base = u64::from(self.bars[bar_idx].addr & mask);
1005149b61b2SSebastien Boeuf                 let new_base = u64::from(value & mask);
1006b8cfdab8SRob Bradford                 let len = u64::from(
1007dc55e459SRob Bradford                     decode_32_bits_bar_size(self.bars[bar_idx].size)
1008b8cfdab8SRob Bradford                         .ok_or(Error::Decode32BarSize)
1009b8cfdab8SRob Bradford                         .unwrap(),
1010b8cfdab8SRob Bradford                 );
1011149b61b2SSebastien Boeuf                 let region_type = bar_type;
1012149b61b2SSebastien Boeuf 
1013dc55e459SRob Bradford                 self.bars[bar_idx].addr = value;
1014149b61b2SSebastien Boeuf 
1015149b61b2SSebastien Boeuf                 return Some(BarReprogrammingParams {
1016149b61b2SSebastien Boeuf                     old_base,
1017149b61b2SSebastien Boeuf                     new_base,
1018149b61b2SSebastien Boeuf                     len,
1019149b61b2SSebastien Boeuf                     region_type,
1020149b61b2SSebastien Boeuf                 });
1021149b61b2SSebastien Boeuf             } else if (reg_idx > BAR0_REG)
10227c457378SSebastien Boeuf                 && ((self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
1023dc55e459SRob Bradford                     != (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
10247c457378SSebastien Boeuf                     || (value & mask) != (self.bars[bar_idx].addr & mask))
1025149b61b2SSebastien Boeuf             {
102664e217cfSRob Bradford                 info!(
102764e217cfSRob Bradford                     "Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
102859f98a2eSBo Chen                     bar_idx, self.bars[bar_idx].addr, value
1029149b61b2SSebastien Boeuf                 );
1030b57cc3d7SRob Bradford                 let old_base = (u64::from(self.bars[bar_idx].addr & mask) << 32)
1031dc55e459SRob Bradford                     | u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
1032b57cc3d7SRob Bradford                 let new_base = (u64::from(value & mask) << 32)
1033149b61b2SSebastien Boeuf                     | u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
10347c457378SSebastien Boeuf                 let len =
10357c457378SSebastien Boeuf                     decode_64_bits_bar_size(self.bars[bar_idx].size, self.bars[bar_idx - 1].size)
1036b8cfdab8SRob Bradford                         .ok_or(Error::Decode64BarSize)
1037b8cfdab8SRob Bradford                         .unwrap();
1038149b61b2SSebastien Boeuf                 let region_type = PciBarRegionType::Memory64BitRegion;
1039149b61b2SSebastien Boeuf 
1040dc55e459SRob Bradford                 self.bars[bar_idx].addr = value;
1041dc55e459SRob Bradford                 self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
1042149b61b2SSebastien Boeuf 
1043149b61b2SSebastien Boeuf                 return Some(BarReprogrammingParams {
1044149b61b2SSebastien Boeuf                     old_base,
1045149b61b2SSebastien Boeuf                     new_base,
1046149b61b2SSebastien Boeuf                     len,
1047149b61b2SSebastien Boeuf                     region_type,
1048149b61b2SSebastien Boeuf                 });
1049149b61b2SSebastien Boeuf             }
1050149b61b2SSebastien Boeuf         } else if reg_idx == ROM_BAR_REG && (value & mask) != (self.rom_bar_addr & mask) {
105107bad79fSSebastien Boeuf             // Ignore the case where the BAR size is being asked for.
105207bad79fSSebastien Boeuf             if value & ROM_BAR_ADDR_MASK == ROM_BAR_ADDR_MASK {
105307bad79fSSebastien Boeuf                 return None;
105407bad79fSSebastien Boeuf             }
105507bad79fSSebastien Boeuf 
105664e217cfSRob Bradford             info!(
105759f98a2eSBo Chen                 "Detected ROM BAR reprogramming: (Expansion ROM BAR) 0x{:x}->0x{:x}",
105859f98a2eSBo Chen                 self.rom_bar_addr, value
1059149b61b2SSebastien Boeuf             );
1060149b61b2SSebastien Boeuf             let old_base = u64::from(self.rom_bar_addr & mask);
1061149b61b2SSebastien Boeuf             let new_base = u64::from(value & mask);
1062b8cfdab8SRob Bradford             let len = u64::from(
1063b8cfdab8SRob Bradford                 decode_32_bits_bar_size(self.rom_bar_size)
1064b8cfdab8SRob Bradford                     .ok_or(Error::Decode32BarSize)
1065b8cfdab8SRob Bradford                     .unwrap(),
1066b8cfdab8SRob Bradford             );
1067149b61b2SSebastien Boeuf             let region_type = PciBarRegionType::Memory32BitRegion;
1068149b61b2SSebastien Boeuf 
1069149b61b2SSebastien Boeuf             self.rom_bar_addr = value;
1070149b61b2SSebastien Boeuf 
1071149b61b2SSebastien Boeuf             return Some(BarReprogrammingParams {
1072149b61b2SSebastien Boeuf                 old_base,
1073149b61b2SSebastien Boeuf                 new_base,
1074149b61b2SSebastien Boeuf                 len,
1075149b61b2SSebastien Boeuf                 region_type,
1076149b61b2SSebastien Boeuf             });
1077149b61b2SSebastien Boeuf         }
1078149b61b2SSebastien Boeuf 
1079149b61b2SSebastien Boeuf         None
1080149b61b2SSebastien Boeuf     }
10818da7c13eSBo Chen 
pending_bar_reprogram(&self) -> Vec<BarReprogrammingParams>10828da7c13eSBo Chen     pub(crate) fn pending_bar_reprogram(&self) -> Vec<BarReprogrammingParams> {
10838da7c13eSBo Chen         self.pending_bar_reprogram.clone()
10848da7c13eSBo Chen     }
10858da7c13eSBo Chen 
clear_pending_bar_reprogram(&mut self)10868da7c13eSBo Chen     pub(crate) fn clear_pending_bar_reprogram(&mut self) {
10878da7c13eSBo Chen         self.pending_bar_reprogram = Vec::new();
10888da7c13eSBo Chen     }
1089e8308dd1SSamuel Ortiz }
1090e8308dd1SSamuel Ortiz 
1091e1701f11SSebastien Boeuf impl Pausable for PciConfiguration {}
1092e1701f11SSebastien Boeuf 
1093e1701f11SSebastien Boeuf impl Snapshottable for PciConfiguration {
id(&self) -> String1094e1701f11SSebastien Boeuf     fn id(&self) -> String {
1095eae80438SSebastien Boeuf         String::from(PCI_CONFIGURATION_ID)
1096e1701f11SSebastien Boeuf     }
1097e1701f11SSebastien Boeuf 
snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>1098871138d5SSebastien Boeuf     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
109910ab87d6SRob Bradford         Snapshot::new_from_state(&self.state())
1100e1701f11SSebastien Boeuf     }
1101e1701f11SSebastien Boeuf }
1102e1701f11SSebastien Boeuf 
1103e8308dd1SSamuel Ortiz impl Default for PciBarConfiguration {
default() -> Self1104e8308dd1SSamuel Ortiz     fn default() -> Self {
1105e8308dd1SSamuel Ortiz         PciBarConfiguration {
1106da95c0d7SSebastien Boeuf             idx: 0,
1107e8308dd1SSamuel Ortiz             addr: 0,
1108e8308dd1SSamuel Ortiz             size: 0,
1109e8308dd1SSamuel Ortiz             region_type: PciBarRegionType::Memory64BitRegion,
1110e8308dd1SSamuel Ortiz             prefetchable: PciBarPrefetchable::NotPrefetchable,
1111e8308dd1SSamuel Ortiz         }
1112e8308dd1SSamuel Ortiz     }
1113e8308dd1SSamuel Ortiz }
1114e8308dd1SSamuel Ortiz 
1115e8308dd1SSamuel Ortiz impl PciBarConfiguration {
new( idx: usize, size: u64, region_type: PciBarRegionType, prefetchable: PciBarPrefetchable, ) -> Self1116e8308dd1SSamuel Ortiz     pub fn new(
1117da95c0d7SSebastien Boeuf         idx: usize,
1118e8308dd1SSamuel Ortiz         size: u64,
1119e8308dd1SSamuel Ortiz         region_type: PciBarRegionType,
1120e8308dd1SSamuel Ortiz         prefetchable: PciBarPrefetchable,
1121e8308dd1SSamuel Ortiz     ) -> Self {
1122e8308dd1SSamuel Ortiz         PciBarConfiguration {
1123da95c0d7SSebastien Boeuf             idx,
1124e8308dd1SSamuel Ortiz             addr: 0,
1125e8308dd1SSamuel Ortiz             size,
1126e8308dd1SSamuel Ortiz             region_type,
1127e8308dd1SSamuel Ortiz             prefetchable,
1128e8308dd1SSamuel Ortiz         }
1129e8308dd1SSamuel Ortiz     }
1130e8308dd1SSamuel Ortiz 
1131221c1f1bSRob Bradford     #[must_use]
set_index(mut self, idx: usize) -> Self1132da95c0d7SSebastien Boeuf     pub fn set_index(mut self, idx: usize) -> Self {
1133da95c0d7SSebastien Boeuf         self.idx = idx;
1134e8308dd1SSamuel Ortiz         self
1135e8308dd1SSamuel Ortiz     }
1136e8308dd1SSamuel Ortiz 
1137221c1f1bSRob Bradford     #[must_use]
set_address(mut self, addr: u64) -> Self1138e8308dd1SSamuel Ortiz     pub fn set_address(mut self, addr: u64) -> Self {
1139e8308dd1SSamuel Ortiz         self.addr = addr;
1140e8308dd1SSamuel Ortiz         self
1141e8308dd1SSamuel Ortiz     }
1142e8308dd1SSamuel Ortiz 
1143221c1f1bSRob Bradford     #[must_use]
set_size(mut self, size: u64) -> Self1144e8308dd1SSamuel Ortiz     pub fn set_size(mut self, size: u64) -> Self {
1145e8308dd1SSamuel Ortiz         self.size = size;
1146e8308dd1SSamuel Ortiz         self
1147e8308dd1SSamuel Ortiz     }
1148e8308dd1SSamuel Ortiz 
1149221c1f1bSRob Bradford     #[must_use]
set_region_type(mut self, region_type: PciBarRegionType) -> Self1150185b1082SSebastien Boeuf     pub fn set_region_type(mut self, region_type: PciBarRegionType) -> Self {
1151185b1082SSebastien Boeuf         self.region_type = region_type;
1152185b1082SSebastien Boeuf         self
1153185b1082SSebastien Boeuf     }
115489218b6dSSebastien Boeuf 
1155868d1f69SSteven Dake     #[must_use]
set_prefetchable(mut self, prefetchable: PciBarPrefetchable) -> Self1156868d1f69SSteven Dake     pub fn set_prefetchable(mut self, prefetchable: PciBarPrefetchable) -> Self {
1157868d1f69SSteven Dake         self.prefetchable = prefetchable;
1158868d1f69SSteven Dake         self
1159868d1f69SSteven Dake     }
1160868d1f69SSteven Dake 
idx(&self) -> usize116189218b6dSSebastien Boeuf     pub fn idx(&self) -> usize {
116289218b6dSSebastien Boeuf         self.idx
116389218b6dSSebastien Boeuf     }
116489218b6dSSebastien Boeuf 
addr(&self) -> u64116589218b6dSSebastien Boeuf     pub fn addr(&self) -> u64 {
116689218b6dSSebastien Boeuf         self.addr
116789218b6dSSebastien Boeuf     }
116889218b6dSSebastien Boeuf 
size(&self) -> u64116989218b6dSSebastien Boeuf     pub fn size(&self) -> u64 {
117089218b6dSSebastien Boeuf         self.size
117189218b6dSSebastien Boeuf     }
117289218b6dSSebastien Boeuf 
region_type(&self) -> PciBarRegionType117389218b6dSSebastien Boeuf     pub fn region_type(&self) -> PciBarRegionType {
117489218b6dSSebastien Boeuf         self.region_type
117589218b6dSSebastien Boeuf     }
117611e9f433SSebastien Boeuf 
prefetchable(&self) -> PciBarPrefetchable117711e9f433SSebastien Boeuf     pub fn prefetchable(&self) -> PciBarPrefetchable {
117811e9f433SSebastien Boeuf         self.prefetchable
117911e9f433SSebastien Boeuf     }
1180e8308dd1SSamuel Ortiz }
1181e8308dd1SSamuel Ortiz 
1182e8308dd1SSamuel Ortiz #[cfg(test)]
1183e8308dd1SSamuel Ortiz mod tests {
1184e8308dd1SSamuel Ortiz     use vm_memory::ByteValued;
1185e8308dd1SSamuel Ortiz 
1186e8308dd1SSamuel Ortiz     use super::*;
1187e8308dd1SSamuel Ortiz 
11882e22b8bcSWei Liu     #[repr(C, packed)]
1189e8308dd1SSamuel Ortiz     #[derive(Clone, Copy, Default)]
1190e8308dd1SSamuel Ortiz     #[allow(dead_code)]
1191e8308dd1SSamuel Ortiz     struct TestCap {
1192e8308dd1SSamuel Ortiz         len: u8,
1193e8308dd1SSamuel Ortiz         foo: u8,
1194e8308dd1SSamuel Ortiz     }
1195e8308dd1SSamuel Ortiz 
1196d2d6eb05SWei Liu     // SAFETY: All members are simple numbers and any value is valid.
1197e8308dd1SSamuel Ortiz     unsafe impl ByteValued for TestCap {}
1198e8308dd1SSamuel Ortiz 
1199e8308dd1SSamuel Ortiz     impl PciCapability for TestCap {
bytes(&self) -> &[u8]1200e8308dd1SSamuel Ortiz         fn bytes(&self) -> &[u8] {
1201e8308dd1SSamuel Ortiz             self.as_slice()
1202e8308dd1SSamuel Ortiz         }
1203e8308dd1SSamuel Ortiz 
id(&self) -> PciCapabilityId1204827229d8SRob Bradford         fn id(&self) -> PciCapabilityId {
1205827229d8SRob Bradford             PciCapabilityId::VendorSpecific
1206e8308dd1SSamuel Ortiz         }
1207e8308dd1SSamuel Ortiz     }
1208e8308dd1SSamuel Ortiz 
1209e8308dd1SSamuel Ortiz     #[test]
add_capability()1210e8308dd1SSamuel Ortiz     fn add_capability() {
1211e8308dd1SSamuel Ortiz         let mut cfg = PciConfiguration::new(
1212e8308dd1SSamuel Ortiz             0x1234,
1213e8308dd1SSamuel Ortiz             0x5678,
12149bd5ec89SRob Bradford             0x1,
1215e8308dd1SSamuel Ortiz             PciClassCode::MultimediaController,
1216e8308dd1SSamuel Ortiz             &PciMultimediaSubclass::AudioController,
1217e8308dd1SSamuel Ortiz             None,
1218e8308dd1SSamuel Ortiz             PciHeaderType::Device,
1219e8308dd1SSamuel Ortiz             0xABCD,
1220e8308dd1SSamuel Ortiz             0x2468,
12219a178716SRob Bradford             None,
1222eae80438SSebastien Boeuf             None,
1223e8308dd1SSamuel Ortiz         );
1224e8308dd1SSamuel Ortiz 
1225e8308dd1SSamuel Ortiz         // Add two capabilities with different contents.
1226846505d3SSebastien Boeuf         let cap1 = TestCap { len: 4, foo: 0xAA };
1227e8308dd1SSamuel Ortiz         let cap1_offset = cfg.add_capability(&cap1).unwrap();
1228e8308dd1SSamuel Ortiz         assert_eq!(cap1_offset % 4, 0);
1229e8308dd1SSamuel Ortiz 
1230e8308dd1SSamuel Ortiz         let cap2 = TestCap {
1231e8308dd1SSamuel Ortiz             len: 0x04,
1232e8308dd1SSamuel Ortiz             foo: 0x55,
1233e8308dd1SSamuel Ortiz         };
1234e8308dd1SSamuel Ortiz         let cap2_offset = cfg.add_capability(&cap2).unwrap();
1235e8308dd1SSamuel Ortiz         assert_eq!(cap2_offset % 4, 0);
1236e8308dd1SSamuel Ortiz 
1237e8308dd1SSamuel Ortiz         // The capability list head should be pointing to cap1.
1238e8308dd1SSamuel Ortiz         let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF;
1239e8308dd1SSamuel Ortiz         assert_eq!(cap1_offset, cap_ptr as usize);
1240e8308dd1SSamuel Ortiz 
1241e8308dd1SSamuel Ortiz         // Verify the contents of the capabilities.
1242e8308dd1SSamuel Ortiz         let cap1_data = cfg.read_reg(cap1_offset / 4);
1243e8308dd1SSamuel Ortiz         assert_eq!(cap1_data & 0xFF, 0x09); // capability ID
1244e8308dd1SSamuel Ortiz         assert_eq!((cap1_data >> 8) & 0xFF, cap2_offset as u32); // next capability pointer
1245e8308dd1SSamuel Ortiz         assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len
1246e8308dd1SSamuel Ortiz         assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo
1247e8308dd1SSamuel Ortiz 
1248e8308dd1SSamuel Ortiz         let cap2_data = cfg.read_reg(cap2_offset / 4);
1249e8308dd1SSamuel Ortiz         assert_eq!(cap2_data & 0xFF, 0x09); // capability ID
1250e8308dd1SSamuel Ortiz         assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer
1251e8308dd1SSamuel Ortiz         assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len
1252e8308dd1SSamuel Ortiz         assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo
1253e8308dd1SSamuel Ortiz     }
1254e8308dd1SSamuel Ortiz 
1255e8308dd1SSamuel Ortiz     #[derive(Copy, Clone)]
1256827229d8SRob Bradford     enum TestPi {
1257e8308dd1SSamuel Ortiz         Test = 0x5a,
1258e8308dd1SSamuel Ortiz     }
1259e8308dd1SSamuel Ortiz 
1260827229d8SRob Bradford     impl PciProgrammingInterface for TestPi {
get_register_value(&self) -> u81261e8308dd1SSamuel Ortiz         fn get_register_value(&self) -> u8 {
1262e8308dd1SSamuel Ortiz             *self as u8
1263e8308dd1SSamuel Ortiz         }
1264e8308dd1SSamuel Ortiz     }
1265e8308dd1SSamuel Ortiz 
1266e8308dd1SSamuel Ortiz     #[test]
class_code()1267e8308dd1SSamuel Ortiz     fn class_code() {
1268e8308dd1SSamuel Ortiz         let cfg = PciConfiguration::new(
1269e8308dd1SSamuel Ortiz             0x1234,
1270e8308dd1SSamuel Ortiz             0x5678,
12719bd5ec89SRob Bradford             0x1,
1272e8308dd1SSamuel Ortiz             PciClassCode::MultimediaController,
1273e8308dd1SSamuel Ortiz             &PciMultimediaSubclass::AudioController,
1274827229d8SRob Bradford             Some(&TestPi::Test),
1275e8308dd1SSamuel Ortiz             PciHeaderType::Device,
1276e8308dd1SSamuel Ortiz             0xABCD,
1277e8308dd1SSamuel Ortiz             0x2468,
12789a178716SRob Bradford             None,
1279eae80438SSebastien Boeuf             None,
1280e8308dd1SSamuel Ortiz         );
1281e8308dd1SSamuel Ortiz 
1282e8308dd1SSamuel Ortiz         let class_reg = cfg.read_reg(2);
1283e8308dd1SSamuel Ortiz         let class_code = (class_reg >> 24) & 0xFF;
1284e8308dd1SSamuel Ortiz         let subclass = (class_reg >> 16) & 0xFF;
1285e8308dd1SSamuel Ortiz         let prog_if = (class_reg >> 8) & 0xFF;
1286e8308dd1SSamuel Ortiz         assert_eq!(class_code, 0x04);
1287e8308dd1SSamuel Ortiz         assert_eq!(subclass, 0x01);
1288e8308dd1SSamuel Ortiz         assert_eq!(prog_if, 0x5a);
1289e8308dd1SSamuel Ortiz     }
1290e8308dd1SSamuel Ortiz }
1291